Zend Translation and View Scripts - zend-framework

Form labels and error messages are translated automatically. But strings in the view scripts are not. I have to use $this->translate("text to transfer"); in each and every phtml files.I don't want to use this $this->translate("text to transfer"); method.How can I automatically translate view scripts as in the case of forms.My code is
protected function _initTranslation() {
$langNamespace = new Zend_Session_Namespace('language_sess');
$lang = $langNamespace->lang;
$registry = Zend_Registry::getInstance();
$tr = new Zend_Translate(
array(
'adapter' => 'array',
'content' => APPLICATION_PATH . "/languages/$lang/$lang.php",
'locale' => "ar",
'scan' => Zend_Translate::LOCALE_DIRECTORY
)
);
$registry->set('Zend_Translate', $tr);
return $registry;
}

OK, to make it clear, we need to understand few things:
Zend_Translate is supposed to translate only part of your site, not related to content. That is menu items, section names, titles, etc. You might have exceptions from that rule, especially if you use Zend_Translate_Adapter_Gettext.
For content, you should use something else to provide your translation. Usually, for example if it is a static article, you get that data from DB, not the language file.
In your case, if you are ok with a little bit "machine" translation, you should use google translate engine for sites
https://translate.google.com/manager/website/
As for dropping $this->translate("text to transfer"); part, you can try to translate all data in controller, but that is rather bad advice, I would advice you to do something else. When you pass your data to view, you can loop through it and make translation for each and everystring inside:
foreach($data as $key => $string) {
$data[$key] = $tr->translate($string);
}
or something like that. This is bad approach because of few reasons. But most important is that you are not dividing your presentation from your logic, and that is why the best place for this is View.
So my summary:
Try to keep using $this->translate()
If that really does not work for you, try to use google translate engine
If that is also not good for you, try Zend_Translate_Adapter_Gettext.
Please, do not use language files for your content.

Related

Zend_Framework how to get the two URL Parameters

I have a build server (http://url.server.zip/release) which contains a list of zip files with some numeric numbers. E.g. http://url.server.zip/release/first_release_1,http://url.server.zip/release/first_release_2 and so on...
View: In my view page I have developed one search box from the search box, I need to search the string from that server (http://url.server.zip/release) then I have to display the release zip file in front view page.
public function viewPostAction() {
$this->params()->fromRoute('page', 1);
return new ViewModel();
}
I'm not sure I really understand your question, but I'll give a try.
Regardless of your situation, in general, you'll have to set the two parameters' name in the route (module.config.php). Like this for example where I want to get in the url the id and name of a theme :
'theme' => array(
'type' => 'Zend\Mvc\Router\Http\Segment',
'options' => array(
'route' => '/theme/[:themeId]/[:themeName]',
...
The [] means that it is not mandatory to use a parameter in the link. Up to you to know if you want to make it mandatory or not.
The URL would be something like "http://test.com/12/testing". Then, you'll be able to get both of the parameters this way:
$themeId = $this->params()->fromRoute('themeId');
$themeName = $this->params()->fromRoute('themeName');
$themeId = 12 and $themeName = testing, if we follow the example.
Hope it is what you were looking for. If not, could you maybe give us some more info ? Like your routing file, etc.

perl Template::Alloy with Catalyst not displaying properly

From my understanding, Template::Alloy::TT should be interchangable with Template Toolkit, however I am having some issues trying to swap one out with the other. Here is the config for my view file:
package maypp::View::HTML;
use strict;
use base 'Catalyst::View::TT';
__PACKAGE__->config({
INCLUDE_PATH => [
myapp->path_to( 'root', 'src' ),
myapp->path_to( 'root', 'lib' ),
],
PRE_PROCESS => 'config/main',
WRAPPER => 'site/wrapper',
ERROR => 'error.html',
TIMER => 0,
render_die => 1,
COMPILE_DIR => '/tmp/compiled_templates', #caches compiled templates
STAT_TTL => 1, #how long to cache templates before seeing if there are any changes
TEMPLATE_EXTENSION => '.html',
});
I thought that changing Catalyst::View::TT to Catalyst::View::TT::Alloy would be all I had to do to begin using Template::Alloy (this has been the procedure for me before). However, whenever I change this I do not get the correct output. Below is my wrapper file:
[% IF template.name.match('\.(css|js|txt)');
debug("Passing page through as text: $template.name");
content;
ELSE;
debug("Applying HTML page layout wrappers to $template.name\n");
content WRAPPER "$host/site/html" + "$host/site/layout";
END;
-%]
site/html will be processed, however site/layout does not go into site/html like it does when I just use regular Template Toolkit (typically site/layout goes into [% content %] in site/html). Is there something I'm doing wrong here? I'd like to use Template::Alloy for the speed increase, but that's only if I can get it working :) Thanks for the help!
Just taking a stab in the dark, I'd say TT::Alloy might not support the multiple WRAPPER + directive. In quite a few years of TT development, I've never used it, but that's not to say it isn't popular somewhere. My experience is an app might switch between wrapper A and wrapper B based on context, but wrapping B within A? Not so much.
If you're always going to need to do that though, why not put the second WRAPPER directive inside $host/site/html.tt ?

How can I access custom validators globally?

I created a my own validation class under /library/My/Validate/
In my form I have $this->addElementPrefixPath('My_Validate', 'My/Validate', 'validate');
I am using my validator like so:
$this->addElement('text', 'aField', array(
'validators' => array(
array('TestValidator', false, array('messages' => 'test failed')
),
));
This all works. However, I am interested in improving this in two ways.
I would like to make it so that all forms have access to my validator. Calling addElementPrefixPath() in every form doesn't seem to be a clean way of doing this.
I would like to pass in My_Validate_TestValidator instead of TestValidator so other developers know what they are working with right away.
To answer your first question, the only real easy way to do this would be to create your own instance of the form - My_Form_Abstract - which has an init() method that sets the prefix path - and then of course calls the parent init().
I'm not aware of a way to make your second method work flawlessly. You need to store a prefix in order to build the validator loader correctly. However, as an alternative, you might try creating new instances of the class using the full name, and then adding it to the element:
$element = $this->getElement('aField');
$myValidateTestValidator = new My_Validate_TestValidator();
$element->addValidator($myValidateTestValidator);

Prevent form manipulation in Lithium/mongoDB

I'm writing my first community page with Lithium and mongoDB. I really like the schema-less way of mongo, but there is one problem making it impossible working without a schema:
For instance we have a simple form like this:
<?=$this->form->create();?>
<?=$this->form->field('name',array('label' => 'Topic title'));?>
<?=$this->form->field('text',array('label' => 'Content'));?>
<?=$this->form->submit('create');?>
which will be even simpler saved by this:
if($this->request->is('post')) {
$board_post = BoardPosts::create($this->request->data);
$board_post->save();
}
Now it's possible for everyone to add some form inputs by DOM manipulation with Firebug, Developer Tools etc. Of course that it might be some sensless fields in the database, but maybe someone adds a field, that is really used.
The only way to prevent this, is creating a schema in model. But for me this makes the whole idea of a schema-less database useless, doesn't it? And how to make schemas for different situations/actions, when some fields must not occur?
The Model::save() method accepts a 'whitelist' param in its options. See http://li3.me/docs/lithium/data/Model::save()
$whitelist = array(
'title',
'text'
);
$post = BoardPosts::create();
$post->save($this->request->data, compact('whitelist'));
You can also define protected $_schema in your Model and set protected $_meta = array('locked' => true); which will automatically set the whitelist to the fields defined in your schema. However, it is a good idea to define the whitelist in your controller to avoid attacks like you describe.
This problem is called a mass-assignment vulnerability and exists in many frameworks if developers are not careful.

Correct routing for a Rest API with Zend

I'm trying to implement a REST API to my website.
My problem is that the default Zend routing gets in the way. I've first tried using Zend_Rest_Route but I haven't been able to understand how I was supposed to use it correctly for "deep" routes, aka website/api/resource1/filter/resource2/id.
Using the default Zend routing, I'd need to create a gigantic Resource1Controller to take care of all the possible actions, and I don't think it's the "good" way to do this.
I've tried using Resauce ( http://github.com/mikekelly/Resauce/), creating an api module and adding routes, but I'm not able to get it working correctly :
The patterns I added were :
$this->addResauceRoutes(array(
'api/resource' => 'resource',
'api/resource/:id' => 'custom',
'api/resource/filter' => 'resource-filter',
'api/resource/filter/:id' => 'custom',
));
Which then leads to this :
public function addResauceRoutes($routes) {
$router = Zend_Controller_Front::getInstance()->getRouter();
foreach ($routes as $pattern => $controller) {
$router->addRoute($controller,
new Zend_Controller_Router_Route($pattern, array(
'module' => 'api',
'controller' => $controller
)
)
);
}
Zend_Controller_Front::getInstance()->setRouter($router);
website/api/resource gets me the
Resource1Controller, ok
website/api/resource/filter gets me to the
resource1filterController, ok
website/api/resource/filter/:id gets me to
a custom controller, ok
I'd like for website/api/resource/:id to get me to the same custom controller... But it redirects me to the Resource1Controller.
What solution is there for me to correctly create my API ? Is there a good way to do this with Zend_Rest_Route ?
Edit : Mike,
I felt that it was not appropriate for me to use different controllers since I need the pathes "website/api/resource/:id" and "website/api/resource/filter/:id" to give me almost the exact same result (the only difference is that because the filter is there, I may get a message telling "content filtered" here).
I thought it was a waste creating another almost identical controller when I could've used the same controller and just checked if a parameter "filter" was present.
However, I don't want to use the basic Zend routing since for the path "website/api/resource/filter/resource2" I'd like to have a totally different comportment, so I'd like to use another controller, especially since I'm trying to use Zend_Rest_Action and need my controllers to use the basic actions getAction(), putAction(), postAction() and deleteAction().
Please could you explain why it is you need two URI patterns pointing to the same controller. A better solution might be to use a separate controller for each of the two patterns and move any shared logic into your model.
Forcing a unique controller for each routing pattern was an intentional design decision, so I'd be interested to hear more detail about your use case where you feel this isn't appropriate.
I thought it was a waste creating
another almost identical controller
when I could've used the same
controller and just checked if a
parameter "filter" was present.
Personally, I think it is cleaner to move the shared logic into the model and to keep your controllers skinny. To me it's not wasteful, it's just more organised - it will make your code easier to manage over time.
If you really need to use the same controller you could always use a query parameter instead, that would work fine:
api/resource/foo?filter=true
That URI would be taken care of by the first route ('api/resource/:id' => 'custom') for free.
But please consider using two controllers, I think that is a better approach.
Okay, the reason I didn't get the good controllers was because Resauce uses the controller name as the name of the route, which has to be unique - so second url pointing to "custom" controller couldn't work. Now I'm able to get the files I want :)
So instead of what was previously noted, I use directly the $router->addRoute(); and define new names each times, even if pointing to the same controller.
Example :
$router->addRoute('resource', new Zend_Controller_Router_Route('/api/resources/:id', array('module' => 'api', 'controller' => 'resource')));
$router->addRoute('resourceFiltered', new Zend_Controller_Router_Route('/api/resources/filter1/:id', array('module' => 'api', 'controller' => 'resource', 'filter' => 'filter1'));