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
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>
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.
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
}
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
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.