I'm using lexpyrocms parser as a package installed with composer along with a codeigniter framework with HMVC, wich allows me to use {{pseudo-variable}} in my templates/views.
I have a very weird behavior whith the parser syntax in my view :
I have this simple $modules array as data that I can print_r() in the view/template
$modules =
Array (
[users] => stdClass Object ( [id_mdl] => 8 [name_mdl] => users ),
[actions] => stdClass Object ( [id_mdl] => 9 [name_mdl] => actions )
);
If I use basic Lex syntax, I can display the name_mdl without problem with
{{modules}} {{name_mdl}} {{/modules}} => output 'users' and 'actions'
but when I use the conditional 'if' inside the loop, I get a wrong matching when I test if a variable exists :
{{modules}}
{{name_mdl}}
{{/modules}}
this outputs correctly users actions
{{modules}}
{{if exists name_mdl}}
name_mdl OK {{name_mdl}}
{{endif}}
{{/modules}}
but this does not output anything : /
{{modules}}
{{if not exists name_mdl}}
name_mdl NOT OK {{name_mdl}}
{{endif}}
{{/modules}}
This outputs 'name_mdl NOT OK users
name_mdl NOT OK actions'
the parser displays correctly a variable it just recused as existing in the condition..
I've searched a bit everywhere but it looks like an orphan problem, can't find a clue anywhere ..
Well.. I managed to get rid of this strange behavior, but I'm not sure which part of the changement I've made solved the problem ...So for what it's worth :
The error I faced was that in a loop {{list_of_things}}{{/list_of_things}} the checking of an existing variable with conditional if exists wasn't possible, though the display of this variable worked fine :
{{list_of_things}}
{{ list_name }} <-- display the list_name of each entry
{{ if exists list_name }}
The condition was never met, tough the data 'list_name' can be displayed above
{{ endif }}
{{/list_of_things}}
I did two things after that, and got rid-off the problem...
1- I moved the declaration of the Lex Parser $this->parser = new Lex\Parser();
from my front controller application/modules/my_module/module_controller.php
into the constructor of my extending core controller class application/core/MY_Controller.php
2- I cleaned up my views folder cause there where some double file from earlier development :
to be noted : the view called was and is application/modules/my_module/views/theme/my_template.php
before :
modules/my_module/views/
my_template.php
modules/my_module/views/theme/
my_template.php
after :
modules/my_module/views/
modules/my_module/views/theme/
my_template.php
So my best guess is that HMVC messed a bit the Lex Parser with the 'loaded_paths', and that two views with the same name in the same module (even though not in the same directory) can lead to unexpected behavior .. but I don't see why the hell that would change the conditionals of the parser ...
Related
I'm trying to make my test app in multilanguage way.
This question has two correlated questions:
First question:
I followed the second answer in How to create multilingual translated routes in Laravel and this help me having a multilanguage site and the route cached, but I've a question and some misunderstanding.
It's a good practice overwrite an app config as they do int the AppServiceProver.php, making:
Config::set('app.locale_prefix', Request::segment(1));
Isn't better to work with the Session::locale in any case?
Second question:
In my case I've two languages, and in the navbar I want to print just ENG when locale is original language, and ITA when session locale is English.
If I'm in the Italian page, the ENG link in the navbar should point to the same English translated page.
Working with the method used in the other question, I hade many problems caused by the:
Config::set('app.locale_prefix', Request::segment(1));
We overwrite the variable in the config file local_prefix, and every time I switch to English language the locale_prefix will change to 'eng' and this sounds me strange, another thing I did is this:
if ( $lang && in_array($lang, config('app.alt_langs')) ){
return app('url')->route($lang . '_' . $name, $parameters, $absolute);
}
We use the alt_langs where are defined only the alternative languages, and this is a problem cause if I pass the local lang, in my case 'it', like lang parameter, this will not be found cause, from the description, the alt_lang should not contain the locale language and you will be able to get only the translated string.
If I change the:
if ( $lang && in_array($lang, config('app.alt_langs')) ){
return app('url')->route($lang . '_' . $name, $parameters, $absolute);
}
in:
if ( $lang && in_array($lang, config('app.all_langs')) ){
return app('url')->route($lang . '_' . $name, $parameters, $absolute);
}
Now using app.all_langs I'm able to choose which URL you want and in which language I want.
How do I get the translated URL?
In the blade file I need to get the translated URL of the page, and if read the other question, we used the $prefix for caching the routes and giving to the route a new name ->name($prefix.'_home'); in this way I can cache all the route and I can call the routes using blade without prefix {{ route('name') }} but, needing the translated url of the actual page a made this on the top of the view:
#php
$ThisRoute = Route::currentRouteName();
$result = substr($ThisRoute, 0, 2);
if ($result =='it' ){
$routeName = str_replace('it_', '', $ThisRoute);
$url = route($routeName,[],true,'en');
} else {
$routeName = str_replace('en_', '', $ThisRoute);
$url = route($routeName,[],true,'it');
}
#endphp
Doing this I get the actual route name that should be it_home I check if start with it_ or en_, I remove the it_ or en_ prefix and I get the translated URL, now you can use the $url as <a href="{{ $url" }}>text</a> cause if I call the {{ route('page') }} I get the link, with the locale language.
This code is not very good, I know, but I written in 5 minutes, need more implementation, and check, but for the moment is just to play with Laravel.
It's a good way?? How can I do it better (except the blade link retrieving)?? Many solution I found used middleware, but I would like to avoid a link in the navbar like mysite.com/changelang?lang=en
Is a good approach overriding the app.locale_prefix?
First
according to your question, it's a bad practice to save the preferences into .env or session because as soon as the session is finished the saved language will be removed also it's common when you need to store any preferences related to your website such as (Color, Font, Language, ...etc) you must store any of them into the cache.
Second
honestly, your code is a very strange and NOT common way and there are two ways to handle what do you need
First
There is a very helpful and awesome package called mcamara it'll help you too much (I recommend this solution).
Second
you can do it from scratch using the lang folder located in the resource folder and you must create files with the same count of the needed languages then use the keys that you'll define into these files into views and you can prefix your routes with the selected language you can use group method like so
Route::group(['prefix' => 'selected_lang'], function() {
Route::get('first_route', [Controller::class, 'your_method']);
});
or you can add the selected language as a query string like so localhost:8000/your_route?lang=en you can follow this tutorial for more info.
I'm trying to customize a label in an edit view using Smarty.
A field named decription should have label LBL_LABEL_ONE with a currency symbol if some_fieds has value 1, LBL_LABEL_TWO otherwise.
With the following code in detailviewdefs.php, I have no problems to accomplish this in the view detail:
array (
'name' => 'description',
'label' => '
{if $bean->some_field==1}
{$MOD.LBL_LABEL_ONE} {$CURRENCY}
{else}
{$MOD.LBL_LABEL_TWO}
{/if}',
),
Note that some_field is a field of the current module and $CURRENCY is assigned in view.detail.php with the following code:
function preDisplay() {
parent::preDisplay();
$currency = new Currency();
$this->ss->assign('CURRENCY', $currency->getDefaultCurrencySymbol());
}
Using same code in editviewdefs.php I have several problems:
First: the following fatal error getting the field of the bean:
Fatal error: Smarty error: [in
cache/modules/omn_fornitura_gas/EditView.tpl line 506]: syntax error:
invalid attribute name: '$bean->some_field'
(Smarty_Compiler.class.php, line 1536) in
C:\xampp\htdocs\sugar\include\Smarty\Smarty.class.php on line 1095
Second: the variable $CURRENCY seems not to be correct:
{€' module='omn_fornitura_gas'}
instead of
€
Quite a puzzle this one.
The reason you're seeing such a thing is that Sugar seems to render Smarty Detail and Edit view definitions quite differently.
The Problem
In detail, it very neatly compiles the field as such:
cache/modules/omn_fornitura_gas/EditView.tpl
{capture name="label" assign="label"}
{if $bean->some_field==1}
{$MOD.LBL_LABEL_ONE} {$CURRENCY}
{else}
{$MOD.LBL_LABEL_TWO}
{/if}{/capture}
However, in edit, it tries to insert it as an attribute of a translated label:
cache/modules/omn_fornitura_gas/EditView.tpl
{capture name="label" assign="label"}{sugar_translate label='
{if $bean->some_field==1}
{$MOD.LBL_LABEL_ONE} {$CURRENCY}
{else}
{$MOD.LBL_LABEL_TWO}
{/if}' module='omn_fornitura_gas'}{/capture}
This explains both the issues you're seeing, as it complains about the attribute, and displays the module name in the bottom line.
So what can we do about it?
Unfortunately, as the edit/detail templates are compiled and cached by Sugar, we can't feasibly get to them directly without digging quite deeply into the Sugar code.
However, what we can do is work around it by specifying a different label, and then setting that label dynamically, just like you were doing in the metadata, but this time via PHP in view.edit.php.
Try this:
editviewdefs.php
array (
'name' => 'description',
'label' => 'LBL_CUSTOM_LABEL',
),
view.edit.php
function preDisplay() {
parent::preDisplay();
$currency = new Currency();
global $mod_strings;
if ($this->bean->some_field == "1"){
$mod_strings['LBL_CUSTOM_LABEL'] = $mod_strings['LBL_LABEL_ONE'] . " " . $currency->getDefaultCurrencySymbol();
}
else {
$mod_strings['LBL_CUSTOM_LABEL'] = $mod_strings['LBL_LABEL_TWO'];
}
}
What happened there?
$mod_strings is a global variable in sugar that holds the current module's labels. What we've done here is use that to our advantage, and conditionally set what the label should be by setting the property of this custom label when the view is generated. Appreciated that this isn't technically a solution using Smarty, but I hope my above details can help shine a light on the inability to solve this problem exactly the same as the detail view definitions within Sugar.
Let me know if that helps, or if I can provide any further information!
My question pertains to connecting to the Close.io API and specifically updating the custom fields associated with leads - http://developer.close.io/#Leads
It states in your documentation that...
custom: To update a single custom field without removing the others,
use custom.field_name: updated_value instead ofcustom: { all:
'fields', listed: 'here' }. You can also unset a single field by
using custom.field_name: null.
But this is causing some quirks in our project. Every update to a single field continues to remove the others.
So here's our code -
# We're using the close.io gem ( a ruby wrapper ) - https://github.com/taylorbrooks/closeio
# First we get the signed in user's email address
# and query closeio to pull the appropriate lead associated with it
closeio_lead_id = (Closeio::Lead.where query: "email:['#{current_user.email}']")[0]['id']
#Next we're attempting to update the lead custom field of "kk_referral" with a float
(Closeio::Lead.update closeio_lead_id,
custom: { kk_referral: Referral.where( :user_id => current_user.id).count.to_f }
)
# And it works! Yay But then we run the next line to update another custom field...
# the last action is erased. what the hell?
(Closeio::Lead.update closeio_lead_id,
custom.kk_blog_posts_submitted: Comment.where( :user_id => current_user.id).count.to_f
)
# So we attempted to store the in a variable so we can replicate some of the direction provided in the documentation.
closeio_comment = Comment.where( :user_id => current_user.id).count.to_f
(Closeio::Lead.update closeio_lead_id,
custom.kk_blog_posts_submitted: closeio_comment
)
# And nothing is coming through. AHhhh. Only the original syntax works.
When attempting to use the syntax provided in the documentation....
custom.field_name: some_variable_storing_a_float
I get a syntax error relating to the colon after field_name.
And when attempting to use more friendly Ruby syntax....
custom.field_name => some_variable_storing_a_float
I get an error on using the custom local variable. (Exactly reads "NameError: undefined local variable or method `custom' for main:Object )
Any advice? Thanks!
Finally figured it out folks.
Here's the syntax -
Closeio::Lead.update 'lead_id', 'custom.Field Name' => 'Value'
Here is a simple jsfiddle. I'm trying to add the class-names a and b, but what I get is an empty class. For example, if I change it to
{{ bindAttr class="str:a:b}}
I get a
Furthermore, is it possible to add some other (default) classnames too ?
{{ bindAttr class"str className1 className2"}}
Of course! Your syntax is just a bit wrong. The syntax for adding a default class is only :className. Here is a simple jsfiddle to show you.
What your first line of code actually states is that if str is true, then assign class 'a' to the element, otherwise class 'b' (which is why you only get 'a' as the assigned class). An example of conditional classes is in an updated version of your jsfiddle here.
How can I show a validation error for a form field outside of a field constructor in Play framework 2? Here is what I tried:
#eventForm.("name").error.message
And I get this error:
value message is not a member of Option[play.api.data.FormError]
I'm confused because in the api docs it says message is a member of FormError. Also this works fine for global errors:
#eventForm.globalError.message
You can get a better grasp of it checking Form's sourcecode here
Form defines an apply method:
def apply(key: String): Field = Field(
this,
key,
constraints.get(key).getOrElse(Nil),
formats.get(key),
errors.collect { case e if e.key == key => e },
data.get(key))
That, as said in the doc, returns any field, even if it doesn't exist. And a Field has an errors member which returns a Seq[FormError]:
So, you could do something like that (for the Seq[FormError]):
eventForm("name").errors.foreach { error =>
<div>#error.message</div>
}
Or (for the Option[FormError])
eventForm("name").error.map { error =>
<div>#error.message</div>
}
Or, you could use Form errors:
def errors(key: String): Seq[FormError] = errors.filter(_.key == key)
And get all errors of a given key. Like this (for the Seq[FormError]):
eventForm.errors("name").foreach { error =>
<div>#error.message</div>
}
Or (for the Option[FormError])
eventForm.error("name").map { error =>
<div>#error.message</div>
}
If you want more details, check the source code. It's well written and well commented.
Cheers!
EDIT:
As biesior commented: to show human readable pretty messages with different languages you have to check how play works I18N out here
To be thorough you're probably going to have to deal with I18N. It's not hard at all to get it all working.
After reading the documentation you may still find yourself a bit consufed. I'll give you a little push. Add a messages file to your conf folder and you can copy its content from here. That way you'll have more control over the default messages. Now, in your view, you should be able to do something like that:
eventForm.errors("name").foreach { error =>
<div>#Messages(error.message, error.args: _*)</div>
}
For instance, if error.message were error.invalid it would show the message previously defined in the conf/messages file Invalid value. args define some arguments that your error message may handle. For instance, if you were handling an error.min, an arg could be the minimum value required. In your message you just have to follow the {n} pattern, where n is the order of your argument.
Of course, you're able to define your own messages like that:
error.futureBirthday=Are you sure you're born in the future? Oowww hay, we got ourselves a time traveler!
And in your controller you could check your form like that (just one line of code to show you the feeling of it)
"year" -> number.verifying("error.furtureBirthday", number <= 2012) // 2012 being the current year
If you want to play around with languages, just follow the documentation.
Cheers, again!
As you said yourself, message is a member of FormError, but you have an Option[FormError]. You could use
eventForm("name").error.map(_.message).getOrElse("")
That gives you the message, if there is an error, and "" if there isn't.