JSF 2.2: Configure available Locales outside faces-config.xml - jsf

Currently i have a section in my faces-config.xml such as follows:
<locale-config>
<default-locale>en</default-locale>
<supported-locale>en_US</supported-locale>
<supported-locale>en_GB</supported-locale>
<supported-locale>de</supported-locale>
<supported-locale>de_DE</supported-locale>
</locale-config>
This is working as intended.
The problem is, i have to be able to define these Locales whithout touching the faces-config.xml in my war. The idea is having e.g. a config File on the resource Path defining all the available Locales.
I already tried an programatic approach as well as using a second faces-config.xml in the META-INF of another module i have access to and defining an specific file on the ressource path which should be merged according to this article. The second file in each case contained the above section in a valid faces-config.xml format. Both without any effect (neither errors nor any change in the behaviour)
Is there any nice way to do this without touching the original faces-config?
I am able to change the ear module and to access ressources programatically in any location. Unfortunately adjusting the faces-config.xml in the .war is no option.

There is a misconception here as far as I can see. There is no need to configure anything in the locale-config if you use custom resolvers/resource bundles/whatever and have otherwise full control over things.
If you look at the answer in Add Resource Bundles Programmatically, you see the OmniFaces Faces.getLocale() is used to pass a locale to the resourceBundle
Locale userLocale = Faces.getLocale();
ResourceBundle b = ResourceBundle.getBundle("msgs", userLocale);
So what is effectively done is pass a programmatically chosen locale to it, not automagically using something that is configured in the locale-config.
The fact the IN Faces.getLocale() some code is present to use the locale-config is to make sure it can be used when people have something configured in locale-config, so it behaves as expected in plain normal JSF. In line 16 it retreives the locale send in the request by the user. In line 18 it checks this against the supported-locale (can be left out in a custom implementation!!!) and if no match, use the default-locale (can be left out to, as can line 7-12)
1 /**
2 * {#inheritDoc}
3 * #see Faces#getLocale()
4 */
5 public static Locale getLocale(FacesContext context) {
6 Locale locale = null;
7 UIViewRoot viewRoot = context.getViewRoot();
8
9 // Prefer the locale set in the view.
10 if (viewRoot != null) {
11 locale = viewRoot.getLocale();
12 }
13
14 // Then the client preferred locale.
15 if (locale == null) {
16 Locale clientLocale = context.getExternalContext().getRequestLocale();
17
18 if (getSupportedLocales(context).contains(clientLocale)) {
19 locale = clientLocale;
20 }
21 }
22
23 // Then the JSF default locale.
24 if (locale == null) {
25 locale = context.getApplication().getDefaultLocale();
26 }
27
28 // Finally the system default locale.
29 if (locale == null) {
30 locale = Locale.getDefault();
31 }
32
33 return locale;
34 }
So this code can be totally tweaked to your liking, including first checking if the user has configured a locale of preference in your application and otherwise using the locale send by the browser IF supported.

Related

Custom renderer for PrimeFaces component not registering

I've written a custom Renderer Class to fix Primefaces issue#5869 and am now trying to use it on the website I'm working on. I've done so by including
<renderer>
<component-family>org.primefaces.component</component-family>
<renderer-type>org.primefaces.component.inputtext.InputTextRenderer</renderer-type>
<renderer-class>at.ac.uibk.library.utils.fixedInputTextRenderer</renderer-class>
</renderer>
in my faces-config.xml.
But it's still possible to insert more than the specified character limit with js.
I added these lines in the fixedInputTextRenderer which should do the necessary check
if (submittedValue != null) {
int maxlength = inputText.getMaxlength();
if (maxlength > 0 && submittedValue.length() > maxlength) {
submittedValue = submittedValue.substring(0, maxlength);
}
inputText.setSubmittedValue(submittedValue);
}
The specified <renderer-type> is wrong:
<renderer-type>org.primefaces.component.inputtext.InputTextRenderer</renderer-type>
According to the VDL documentation of <p:inputText> it's by default registered on org.primefaces.component.InputTextRenderer.
Component information
Info
Value
Component Type
org.primefaces.component.InputText
Handler Class
None
Renderer Type
org.primefaces.component.InputTextRenderer
Description
None
So adjust it accordingly:
<renderer-type>org.primefaces.component.InputTextRenderer</renderer-type>
Do note that the component type and renderer type don't actually represent FQNs, but that they are merely keys/identifiers. That they look like FQNs is indeed an unfortunate side effect of enforcing uniqueness which may indeed be confusing for starters.
See also:
What is the relationship between component family, component type and renderer type?
How to findout component-family and renderer-type of a JSF component

CMS.Ecommerce.IAddressExtensions obsolete

I'm porting some code over from a Kentico 8.2 build to a Kentico 11 website. One of the snippets that I am migrating makes use of a CMS.Ecommerce.IAddressExtensions extension method public static string GetStateCode(this IAddress address).
When I moved this over to my Kentico 11 build, Visual Studio finds that this extension method no longer exists: IAddress does not contain a definition for GetStateCode and no extension method could be found.. I poked around the Object Viewer for the CMS.Ecommerce DLL, and sure enough, no IAddressExtensions class is present.
Is there a workaround to look up the state code for a given address in Kentico 11?
As you found out, Kentico has removed the IAddressExtensions class when upgrading from version 10 to 11. Here is the page for the API change.
Based on my understanding, you will instead need to use the StateInfoProvider as below.
IAddress someAddress = /* snip */;
var stateInfo = StateInfoProvider.GetStateInfo(someAddress.AddressStateID);
var stateCode = stateInfo.StateCode;
You can then take this same logic and move it into your own extension class.
public static class IAddressExtensions
{
public static string GetStateCode(this IAddress address)
{
var stateInfo = StateInfoProvider.GetStateInfo(address.AddressStateID);
return stateInfo.StateCode;
}
}

change label value using value stored at session

i have two jsf pages (home.jsf and employees.jsf) ,
home page has a button that navigates to employees page,
while navigating i store value in session scope
at (Managed bean)
public void putSessionAL(ActionEvent actionEvent) {
FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put("key","value");
}
public String navigate() {
return "employees";
}
i want to change Label at employees viewObject from UIHints tab depending on value stored at session using the following groovy expression
adf.context.sessionScope.key
and changed trustMode to trusted but it fires the following exception
oracle.jbo.script.ExprScriptException: JBO-29114 ADFContext is not setup to process messages for this exception. Use the exception stack trace and error code to investigate the root cause of this exception. Root cause error code is JBO-25188. Error message parameters are {0=Employees.FirstName, 1=, 2=oracle.jbo.script.ExprSecurityException}
at oracle.jbo.script.ExprScriptException.throwException(ExprScriptException.java:316)
at oracle.jbo.script.ExprScriptException.throwExceptionWithExprDef(ExprScriptException.java:387)
at oracle.jbo.ExprEval.processScriptException(ExprEval.java:599)
at oracle.jbo.ExprEval.doEvaluate(ExprEval.java:697)
at oracle.jbo.ExprEval.evaluate(ExprEval.java:508)
at oracle.jbo.ExprEval.evaluate(ExprEval.java:487)
at oracle.jbo.common.NamedObjectImpl.resolvePropertyRaw(NamedObjectImpl.java:680)
at oracle.jbo.server.DefObject.resolvePropertyRaw(DefObject.java:366)
One way to do it at the VO UIHint attribute label level will be programmaticaly by doing as follow :
In your VO go to the java tab and add the RowImpl java class
In the VORowImpl Add the following function
public String getMySessionLabel() {
return (String)FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get("key");
}
In the Label add the following EL expression :
adf.object.getMySessionLabel()
This technique allow you more control than pure EL, if you want to do more than getting from session for example. In your case pure EL, as you did, should work as well. (Would need to check what is wrong with yours, maybe just missing the
#{adf.context.sessionScope.key}
If you attempt to get your label from a method in viewRowImpl. So this will be executed at least once for each row. I think this solution isn't fit for your case.
anyway ADF as a framework added strong policy and validations in EL in general and especially in version 12.2.x.
The solution for you case as following:
Create new class in model layer which extends oracle.jbo.script.ExprSecurityPolicy class
Override checkProperty method.
#Override
public boolean checkProperty(Object object, String string, Boolean b) {
if (object.getClass().getName().equals("oracle.adf.share.http.ServletADFContext") && string.equals("sessionScope")) {
return true;
}
return super.checkProperty(object, string, b);
}
Open adf-config.xml source and in startup tag set your class ExprSecurityPolicy property.
like:
<startup ExprSecurityPolicy="model.CustomExprSecurityPolicy">

Multilanguage field in Alfresco: cannot get local value

I am creating an alfresco custom type with a multilingual property:
<property name="xxx:myfield">
<type>d:mltext</type>
</property>
When I save a new node, I receive a language containing the locale to apply and I do the following:
Locale locale;
if (language == null || language.isEmpty()) {
locale = new Locale("en");
} else {
locale = new Locale(language);
}
MLText myfieldML = new MLText();
myfieldML .addValue(locale, "someValue");
Map<QName, Serializable> props = new HashMap<QName, Serializable>();
props.put(QName.createQName(getNamespaceModel(), "myfield"),
myfieldML);
And then I invoke the createNode method of NodeService to save the new node. That seems to work fine, as I get two locales store for the same node and I can see them in Share (if I change the browser default language, I get two different values for the two languages I created).
Now, I am not sure how to get the value for myfield for a given node and for a given locale. My current code is:
nodeService.getProperty(currentNode, myFieldQName)
I would expect to get a MLText and, from it, get the value for my locale via getValue(locale). Conversely, getProperty returns a String, always with the same locale (English).
I tried to add the following code before getProperty:
MLPropertyInterceptor.setMLAware(false);
Locale.setDefault(locale);
But I still the same string regardless of the locale I set.
How can I address my issue?
This should work:
public String getLocalizedValue(NodeRef nodeRef, Locale locale)
{
MLPropertyInterceptor.setMLAware(true);
MLText mlProp = (MLText) nodeService.getProperty(nodeRef, QNAME_PROP_TYPE_MLTEXT);
MLPropertyInterceptor.setMLAware(false);
return mlProp.getValue(locale);
}

JSF expression evaluation of attribute tags

I have a problem with evaluating EL expressions containing vars created by other tags.
I have a project where I am using a custom validator.
public class MyValidator implements Validator, StateHolder
I have a tag class associated with it:
public class MyValidatorTag extends ValidatorTag
this class allows for attribute fieldName, with the appropriate tld file for the tag:
<tag>
<name>my-validator</name>
<tag-class>my.packaga.MyValidatorTag</tag-class>
<body-content>JSP</body-content>
<description>This is my validator</description>
<attribute>
<name>fieldName</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
<type>java.lang.String</type>
<description>This is some field I need</description>
</attribute>
</tag>
So far so good. But this setup doesn't allow one to use JSF EL expressions for attribute value.
So the validator class (not tag class) I use this code to evaluate attribute value:
public static String evaluateEl(String expression) {
String value = null;
if (expression == null) {
return "";
}
if ((expression.indexOf("#{") != -1)
&& (expression.indexOf("#{") < expression.indexOf('}'))) {
Object evaledValue =
FacesContext.getCurrentInstance().getApplication().createValueBinding(
expression).getValue(FacesContext.getCurrentInstance());
if (evaledValue != null) {
value = evaledValue.toString();
} else {
value = null;
}
} else {
value = expression;
}
return value;
}
It is limited to strings, and it works for most EL expressions. Expression Hello #{1 eq 2} will cause the attribute to have value Hello false.
But there is a case for which this will not work. Any expression which contains a var created by another tag doesn't work. Datatables, dataiterators and most notably <f:loadBundle>, e.g.
<f:loadBundle basename="mypackage.message.ui-strings" var="msgs" />
Followed by an input component containing validator:
<cust:my-validator fieldName="#{msgs['myfield1.name']}"></cust:my-validator>
Fieldname evaluates to empty string using the expression evaluation code above. Changing it to #{requestScope.msgs.... doesn't fix the problem. Looking at loadBundle implementation, it adds the var msgs to request scope, so I don't see why vars don't work for me. Please, somebody help me understand.
Any expression which contains a var created by another tag doesn't work. Datatables, dataiterators and most notably , e.g.
Tag handlers like <f:xxx>, <c:xxx> and some <ui:xxx> (those not having rendered attribute) are executed during JSF view build time, when a JSF UI component tree is to be produced. They are not part of the UI component tree. They have already done their job of producing the necessary JSF UI components.
UI components like <h:xxx> and some <ui:xxx> (those having rendered attribute) are executed during JSF view render time, when a large HTML string is to be produced which is to be sent to the HTTP response of the current HTTP request.
So they don't run in sync.
There are for the enduser of the validator several ways to go around this, all are listed in this answer: How to set converter properties for each row of a datatable?
For the developer, there's another solution, let the validator extend UIComponentBase instead and perform the job in processValidators(). The parent component is just available by UIComponent#getParent() and its submitted value is in turn available by UIInput#getSubmittedValue().

Resources