… or how you can use your own FacesContext implementation.
What we need first is our own FacesContext implementation with new methods to set the StlyeKitId (which is the name of the Theme) for initializing the StyleKit instance:
package ch.hasselba.xpages;
import javax.faces.context.FacesContext;
import com.ibm.xsp.application.ApplicationExImpl;
import com.ibm.xsp.context.FacesContextExImpl;
import com.ibm.xsp.stylekit.StyleKit;
/**
* ThemeSwitcherFacesContext
* allows to switch the theme during runtime
*
* @author Sven Hasselbach
* @version 0.1
*/
public class ThemeSwitcherFacesContext extends FacesContextExImpl {
private StyleKit styleKit;
private String styleKitId;
private FacesContext delegated;
/**
* constructor
*
* @param fc
* delegated javax.faces.context.FacesContext
*/
public ThemeSwitcherFacesContext(FacesContext fc) {
super(fc);
this.delegated = fc;
}
/**
* returns current StyleKit
*
* @return com.ibm.xsp.stylekit.StyleKit
* the StyleKit used for rendering the view
*/
public StyleKit getStyleKit() {
if (this.styleKit == null) {
this.styleKit = super.getStyleKit();
}
return this.styleKit;
}
/**
* sets the StyleKit
*
* @param stlyeKit
* the com.ibm.xsp.stylekit.StyleKit to use
*/
public void setStyleKit(final StyleKit styleKit) {
this.styleKit = styleKit;
}
/**
* returns the current StyleKitId (aka Theme name)
*
* @return String
* the id of the StyleKit
*/
@Override
public String getStyleKitId() {
if (this.styleKitId == null) {
this.styleKitId = super.getStyleKitId();
}
return styleKitId;
}
/**
* sets the StyleKitId
*
* @param styleKitId
* the id of the StyleKit
*/
public void setStyleKitId(final String styleKitId) {
this.styleKitId = styleKitId;
}
/**
* initializes the StyleKit for the current view
*/
public void loadStyleKit() {
this.styleKit = ((ApplicationExImpl) getApplication())
.getStyleKit(this.styleKitId);
}
}
But you cannot register the FacesContext implementation directly. That’s why we have to build our own FacesContextFactory to inject our FacesContext instance this:
package ch.hasselba.xpages;
import javax.faces.FacesException;
import javax.faces.context.FacesContext;
import javax.faces.context.FacesContextFactory;
import javax.faces.lifecycle.Lifecycle;
import com.ibm.xsp.FacesExceptionEx;
import com.ibm.xsp.context.FacesContextFactoryImpl;
/**
* ThemeSwitcherFacesContextFactory
*
* @author Sven Hasselbach
* @version 0.1
*/
public class ThemeSwitcherFacesContextFactory extends FacesContextFactory {
private FacesContextFactory delegate;
@SuppressWarnings("unchecked")
public ThemeSwitcherFacesContextFactory(){
try{
Class clazz = Class.forName( "com.sun.faces.context.FacesContextFactoryImpl" );
this.delegate = (FacesContextFactory) clazz.newInstance();
}
catch (Exception e){
throw new FacesExceptionEx(e);
}
}
public ThemeSwitcherFacesContextFactory(FacesContextFactory fcFactory){
this.delegate = fcFactory;
if ((this.delegate instanceof FacesContextFactoryImpl)) {
this.delegate = ((FacesContextFactoryImpl)this.delegate).getDelegate();
}
}
public FacesContext getFacesContext(Object param1, Object param2,
Object param3, Lifecycle paramLC) throws FacesException {
FacesContext fc = this.delegate.getFacesContext(param1, param2, param3, paramLC);
return new ThemeSwitcherFacesContext(fc);
}
}
Now, you have to add the FacesContextFactory to the faces-config.xml to activate it:
<?xml version="1.0" encoding="UTF-8"?>
<faces-config>
<factory>
<faces-context-factory>
ch.hasselba.xpages.ThemeSwitcherFacesContextFactory
</faces-context-factory>
</factory>
</faces-config>
If you open the XPage, an error will occur:
This happens because there are some java security restrictions with the class loader of the delegated classes. You have to modify the java security in the java.policy file (you can limit the the grant to specific database if you want).
grant {
permission java.security.AllPermission;
};
Then you can add a Theme to your XPage, f.e. this one:
<theme
extends="webstandard"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="platform:/plugin/com.ibm.designer.domino.stylekits/schema/stylekit.xsd" >
<control>
<name>ViewRoot</name>
<property>
<name>style</name>
<value>background-color:rgb(255,255,0)</value>
</property>
</control>
</theme>
To use the ThemeSwitcher, you have to add some code in the beforePageLoad event:
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
<xp:this.beforePageLoad>
<![CDATA[#{javascript:
importPackage( ch.hasselba.xpages );
var fc:ch.hasselba.xpages.ThemeSwitcherFacesContext = facesContext;
fc.setStyleKitId( "ThemeA");
fc.loadStyleKit();}
]]>
</xp:this.beforePageLoad>
Switched Theme
</xp:view>
When the XPage is opened in the browser, the Theme is changed:
All other pages in the NSF are stil using the default Theme:
Awesome stuff… for bonus points, implement a ViewHandler that sets the style kit on the custom FacesContext as soon as a new view instance is created to keep the logic for choosing which theme to load outside of the source for the XPage itself. 🙂
works great!
this is the only exact solution in the internet. thank you for sharing, saved me from seperating nsfs and hard work.