How to set resource bundle "On-The-Fly"? - xpages

I have a combobox where users can select an available language. The application contains properties files for each language.
In the resources section of the page the resource bundle is calculates according to a language tag ( DE, EN ... ) in a user config document.
Is there any easy way to change the language in the onChange event according to the value of the combobox? I thought of context.setProperty(???).
Any suggestion?

To implement this application wide you could use a phase listener. In this example, the locale to use is stored in a sessionScope variable named "Language".
Just add a combobox to your XPage(s) containing all allowed locales.
<xp:comboBox id="comboBox1" value="#{sessionScope.Language}">
<xp:selectItem itemLabel="Chinese" itemValue="zh"></xp:selectItem>
<xp:selectItem itemLabel="German" itemValue="de"></xp:selectItem>
<xp:selectItem itemLabel="Turkish" itemValue="tr"></xp:selectItem>
<xp:eventHandler event="onchange" submit="true" refreshMode="complete" />
</xp:comboBox>
Then you have to use a phase listener like this one:
package ch.hasselba.xpages.jsf.core;
import javax.faces.context.FacesContext;
import javax.faces.application.Application;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
import javax.faces.component.UIViewRoot;
import java.util.Locale;
import java.util.Map;
public class LocalizationSetter implements PhaseListener {
private static final long serialVersionUID = -1L;
private static final String scopeVarName = "Language";
private static final String scopeName = "sessionScope";
public void afterPhase(PhaseEvent event) {}
public void beforePhase(PhaseEvent event) {
FacesContext facesContext = event.getFacesContext();
UIViewRoot view = facesContext.getViewRoot();
view.setLocale( getLanguage(facesContext) ) ;
}
public PhaseId getPhaseId() {
return PhaseId.RENDER_RESPONSE;
}
private Locale getLanguage( FacesContext facesContext ){
try{
Application app = facesContext.getApplication();
Object obj = app.getVariableResolver().resolveVariable(facesContext, scopeName );
Object lang = ((Map) obj).get( scopeVarName );
if( lang != null ){
return new Locale((String) lang);
}
}catch(Exception e){}
return Locale.getDefault();
}
}
You can add lookups / etc. to access the user profiles in the "getLanguag()" method.
Hope this helps
Sven

I have hade some struggle with this in the past and have still not solved it correctly.
you can set the language using
context.setLocaleString("en");
context.reloadPage();
but if you want the language to be changed depending on a user document I think you need to add the code to each beforePageLoad event you have. because the browser will always use the setting you have in the html lang=".." The resource bundle will also use the language in this setting
I have some problems with getting it to work without manual reload or reload twice.
You can also try to change the lang attribute using the phaselistener

Related

Simple customization bean for dates in dynamic view panel

I need to write a simple customization bean for a dynamic view panel so dates will always be displayed as yyyy-MM-dd but I have no clue which method to overwrite and how to modify my value so it shows what I want.
Any starter code would be apprciated (and yes, I looked at Jesse's code and it is way too complex for what I want to achieve).
Thanks
Edit: This now the code I have in my customization bean, but it does absolutely nothing...
public class DynamicViewCustomizerBean_Ben extends DominoViewCustomizer {
public static class ExtendedViewColumnConverter extends ViewColumnConverter {
#Override
public String getValueAsString(final FacesContext context, final UIComponent component, final Object value) {
if(value instanceof DateTime) {
SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
return fmt.format(value);
}
if(value instanceof Date) {
SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd");
return fmt.format(value);
}
// for other cases, just return super
return super.getValueAsString(context, component, value);
}
}
}
And yes, the name of my customization bean is set properly on my Dynamic view panel:
<xe:dynamicViewPanel id="dynamicViewPanel1"
showColumnHeader="true"
customizerBean="com.videotron.xpages.DynamicViewCustomizerBean_Ben"
var="rowData">
...
Am I missing something? Is it the good event that is being overridden? I'm asking because if I set a value of "test" instead of the fmt.format(), it doesn't even show up. Nothing in the logs, no visible errors... I can't seem to find a working example of this on the web...
In the ExtendedViewColumnConverter.getValueAsString(FacesContext, UIComponent, Object) method of your customizer bean you need to return the desired value if the value object is a Date instance.
Here's a simple example:
if (value instanceof Date) {
DateFormat fmt = new SimpleDateFormat("yyyy-MM-dd");
return fmt.format(value);
}
I tend to use a request scoped bean that holds a few useful methods I found myself often to need.
The java class:
public class DateBean implements Serializable {
private static final long serialVersionUID = 1L;
private Locale locale;
private Date now;
private String shortDatePattern;
public void setLocale(Locale locale) {
this.locale = locale;
}
public Date getNow() {
if (now == null) {
now = new Date();
}
return now;
}
public String getShortDatePattern() {
if (shortDatePattern == null) {
SimpleDateFormat sdf = (SimpleDateFormat) SimpleDateFormat.getDateInstance(
SimpleDateFormat.SHORT, locale);
shortDatePattern = sdf.toLocalizedPattern()
.replaceAll("y+", "yyyy")
.replaceAll("M+","MM")
.replaceAll("d+", "dd");
}
return shortDatePattern;
}
...
}
Of course, this is just an example, you can tweak to your like
In the faces-config.xml
<managed-bean>
<managed-bean-name>date</managed-bean-name>
<managed-bean-class>demo.DateBean
</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
<managed-property>
<property-name>locale</property-name>
<value>#{facesContext.viewRoot.locale}</value>
</managed-property>
</managed-bean>
Then, on the XPage:
<xp:text value="#{record.myDate}">
<xp:this.converter>
<xp:convertDateTime type="date"
pattern="${date.shortDatePattern}" />
</xp:this.converter>
</xp:text>

Using File Download Control with Java Bean in XPages

I would like to use the standard XPages xp:fileDownload control and bind it to a Java Bean rather than a document source.
I have an RTF field in my form - 'resourceAttachments' - along with several other fields in which in which I will be storing several attachments and nothing else.
Can anyone provide me with an example or point me to some documentation. I have a similar requirement for the xp:uploadControl, I can find samples which create a new document, but I am struggling to implement adding and saving to existing documents, I guess I should post another question for that though, but as the two go together I thought I would at least mention it here.
Many thanks.
Mark
public class TrainingModule implements Serializable {
private static final long serialVersionUID = -6998234266563204541L;
private String description;
private ???? resourceAttachments; --something here ??
private String unid;
public TrainingModule() {
String documentId = ExtLibUtil.readParameter(FacesContext.getCurrentInstance(), "key");
if (StringUtil.isNotEmpty(documentId)) {
load(documentId);
}
}
public String getUnid() {return unid;}
public void setUnid(final String unid) {this.unid = unid;}
public String getDescription() {return description;}
public void setDescription(final String description) {this.description = description;}
? Some attachment Getter & Setter here??
public void load(final String unid) {setUnid(unid);{
Document doc = null;
try {
doc = ExtLibUtil.getCurrentDatabase().getDocumentByUNID(getUnid());
setDescription(doc.getItemValueString("Description"));
??Some load here here??
} catch (Throwable t) { t.printStackTrace();
} finally {
try {
doc.recycle();
?some other recycle here?
} catch (Exception ) {
// Fail Silent
}
}
In my Custom Control amongst other things I have...
<xp:this.beforePageLoad><![CDATA[#{javascript:if(param.containsKey("key"))
{viewScope.put("docAttach",(param.get("key")));}}]]></xp:this.beforePageLoad>
.......
<xp:fileDownload rows="30" id="fileDownload2" displayLastModified="false"
value="#{TrainingModule.ResourceAttachments}" hideWhen="true" allowDelete="true">
</xp:fileDownload>
I have dealt a similar problem last year. I haven't looked for an appropriate binding for fileUpload. Instead, I used a different approach.
When you submit an XPage with a file upload, it will upload the file to the disk (regardless of any binding) and create an com.ibm.xsp.http.UploadedFile object in the requestMap. So you can grab it from a bean method and do the magic.
This is the IBM Connect 2014 demo and I have used this technique to upload file into the BaseCamp.
XSP code is simple (here is the Github repo)
<xp:fileUpload id="fileUpload1"></xp:fileUpload>
<xp:button id="button1"
value="Upload Local File">
<xp:eventHandler event="onclick"
submit="true" refreshMode="complete">
<xp:this.action>
<xp:executeScript
script="#{bcs.uploadLocalFile}">
</xp:executeScript>
</xp:this.action>
</xp:eventHandler>
</xp:button>
bcs is the managed bean and here is the code snippet for upload (from Github repo)
public void uploadLocalFile() {
FacesContext facesContext = FacesContext.getCurrentInstance();
ExternalContext externalContext = facesContext.getExternalContext();
HttpServletRequest request = (HttpServletRequest) externalContext.getRequest();
String fileUploadID = ExtLibUtil.getClientId(facesContext, facesContext.getViewRoot(), "fileUpload1", false);
UploadedFile uploadedFile = ((UploadedFile) request.getParameterMap().get(fileUploadID));
if (uploadedFile == null) {
facesContext.addMessage("messages1", new FacesMessage(FacesMessage.SEVERITY_ERROR, "No file uploaded. Use the file upload button to upload a file.", ""));
return;
}
java.io.File file = uploadedFile.getServerFile();
String contentType=uploadedFile.getContentType();
String fileName = uploadedFile.getClientFileName();
// removed unrelated code...
}
So, basically, you get the requestMap. The UploadedFile object refers to a file object that has been uploaded to a temporary area on the server and it's mapped with the clientId of the fileUpload component.
After this point, you might create a stream and attach the file into a field. When you bind it to a field, document wrapper does the same thing, I guess.

PrimeFaces Wizard is not reset

We are using the PrimeFaces 4.0 + spring 4 on Tomcat 7.
I go to PrimeFaces show case, open the wizard, type first name and last name hit the next button. Then I select other menus from left panel ( like AutoComplete ) I go back to wizard the first name and last name fields are clear. That is what I expected.
I developed a wizard same as above but every time I come back to wizard page the wizard still holds the previously entered value and is not reset.
My managed bean is as below ( I have used ViewScoped no SessionScope which mentioned in documents):
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
#Named
#ViewScoped
public class AccountController {
#Valid
private Account account = new Account()
//Setters and getters
}
Edited:
I found that the problem is for integration of JSF and Spring. When I remove the #Named and use #ManagedBean it works fine. Any comments?!
Spring does not have a built in support for JSF ViewScope, but you can add this scope to JSF as:
public class ViewScope implements Scope {
public Object get(String name, ObjectFactory<?> objectFactory) {
Map<String, Object> viewMap = FacesContext.getCurrentInstance()
.getViewRoot().getViewMap();
if (viewMap.containsKey(name)) {
return viewMap.get(name);
} else {
Object object = objectFactory.getObject();
viewMap.put(name, object);
return object;
}
}
public Object remove(String name) {
return FacesContext.getCurrentInstance().getViewRoot().getViewMap()
.remove(name);
}
public String getConversationId() {
return null;
}
public void registerDestructionCallback(String name, Runnable callback) {
// Not supported
}
public Object resolveContextualObject(String key) {
return null;
}
}
Please refer to http://blog.primefaces.org/?p=702
And in your applicationConetext.xml
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="view">
<bean class="utils.ViewScope" />
</entry>
</map>
</property>
</bean>
And finally:
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
#Named
#ViewScoped
#Scope("view")
public class AccountController {
#Valid
private Account account = new Account()
//Setters and getters
}

Accessing Java method in xpage repeat control

I've created a Java Script Library which has a java class that contains some jdbc code.
It has a method to get values from a database (mysql).
Now i need to access it in the repeat control like <xp:repeat value = ?? >
But i don't find a way to access it there.
If it is a javascript library, the method is accessed as <xp:repeat value="#{javascript:getSQLData()}"
How to achieve it? And is it the right approach to use java in script libraries when we also have a separate application named Java inside the Code Section (below script library in the application view).
My java code is:
package com.db;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
public class Db {
Connection con;
Statement st;
ResultSet rs;
public Db(){
this.connect("localhost", "3306", "vijay", "root", "");
try {
this.query("select * from prodet;");
} catch (SQLException e) {
// TODO Auto-generated catch block
System.out.println("///////////////query///////////////////////////////////////////");
e.printStackTrace();
}
}
public void connect(String server, String port, String db, String user, String pwd){
try {
Class.forName("com.mysql.jdbc.Driver");
con=DriverManager
.getConnection("jdbc:mysql://localhost:3306/vijay","root", "");
//con=DriverManager.getConnection("\"jdbc:mysql://"+server+":"+port+"/"+db+"\""+","+"\""+user+"\""+","+"\""+pwd+"\"");
st=con.createStatement();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
List arr = new ArrayList();
#SuppressWarnings("unchecked")
public void query(String q) throws SQLException{
rs=st.executeQuery(q);
while(rs.next()){
arr.add(rs.getString(2));
arr.add(rs.getInt(3));
}
}
public String getData(){
String arra = arr.toString();
return arra;
}
public String doAllDb(){
return this.getData();
}
public static void main(String a[]){
Db d = new Db();
System.out.println(d.getData());
}
}
And the ssjs to access the method is:
importPackage(com.db);
var v = new com.db.Db();
v.doAllDb();
This ssjs is written under Bind data using ssjs.
<xp:repeat id="repeat1" rows="30">
<xp:this.value><![CDATA[#{javascript:importPackage(com.db);
var v = new com.db.Db();
v.doAllDb();}]]>
.
When the xpage is previewed, it is blank. Doesn't show any value. But i tested the java code. It is working fine.
I use managed beans all the time to do exactly that :-)
<xp:repeat rows="10" var="row" value="#{User.rowClubs}">
"User" is my managed bean. It has to be a Java bean that implements the serializable interface, has a constructor without arguments and has public getters/setters for access to properties. In my example the User bean implements the method getRowClubs() that returns a list of objects representing rows of clubs (from the application I worked on this morning).
Please let me know if you need any more help with managed beans? From your question I guessed you needed help on how to reference methods in your bean.
Edit
I just saw the rest of your bean (in the scrollable view). Using your bean you would use something like:
<xp:repeat id="repeat1" rows="30" value="Db.data" var="row">
assuming that you have defined your bean in faces-config.xml, e.g.:
<managed-bean>
<managed-bean-name>Db</managed-bean-name>
<managed-bean-class>com.db.Db</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
I would also recommend that you remove your "main" method from the bean. If you need to test it from an ordinary Java program then create a test class with a "main" that instantiates and runs your bean.
/John

Proper place to put initialization code in JSF?

Where is the proper 'place' in JSF to put initialisation snippet that follows, in order to get it executed just one time when the server starts?
1. ExternalContext extContext = FacesContext.getCurrentInstance().getExternalContext();
2. HttpSession sesion = (HttpSession)extContext.getSession(true);
3. String parA = extContext.getInitParameter("parA");
4. String parB = someCalculations(parA);
5. sesion.setAttribute("parB", parB);
Basically I want to read a parameter parA from web.xml context-param section, do some transformations, and include it in session (as new parB parameter).
PostConstructApplicationEvent and eager=true techniques doesn't works because session is null at this point (line 4).
ServletContextListener technique doesn't works because FacesContext isn't available.
Thanks!
There are no sessions at application start time; this requirement is impossible to meet.
I interpret your requirements as:
perform an expensive application-scope calculation
inject this application-scope result into other scopes
The JSF way to do this is via managed beans. Here is an application-scope bean to perform the one-time transformation of the context parameter:
package foo;
import javax.faces.bean.ApplicationScoped;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
#ManagedBean
#ApplicationScoped
public class SomeCalculationsBean {
#ManagedProperty("#{initParam.paraA}")
private String paraA;
private String someCalculation;
public String getParaA() {
return paraA;
}
public void setParaA(String paraA) {
this.paraA = paraA;
this.someCalculation = //do some transformation
}
public String getSomeCalculation() {
return someCalculation;
}
}
This value can then be injected into other scopes as you need it:
package foo;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.SessionScoped;
#ManagedBean
#SessionScoped
public class SomeSessionBean {
#ManagedProperty("#{someCalculationsBean.someCalculation}")
private String paraB;
public String getParaB() {
return paraB;
}
public void setParaB(String paraB) {
this.paraB = paraB;
}
}
Code untested. This implementation assumes JSF annotation support but you can do the same thing with faces-config.xml bean configuration.

Resources