JBoss.orgCommunity Documentation

Chapter 10. Development

10.1. Portal Lifecycle
10.1.1. Overview
10.1.2. Application Server start and stop
10.1.3. The Listener
10.2. RTL (Right To Left) Framework
10.2.1. Overview
10.2.2. Direction
10.2.3. Groovy templates
10.2.4. Stylesheet
10.2.5. Images
10.3. Internationalization Configuration
10.3.1. Overview
10.3.2. Introduction
10.3.3. Example
10.3.4. Access
10.3.5. Debugging resource bundle usage
10.4. XML Resources Bundles
10.4.1. Motivation
10.4.2. Portal support
10.4.3. XML format
10.5. Dynamic Layouts
10.5.1. Overview
10.5.2. Advanced Drag and Drop mechanism
10.5.3. Summary
10.6. JavaScript Inter Application Communication
10.6.1. Overview
10.6.2. Common topics
10.6.3. /GateIn
10.6.4. /GateIn/portal/notification
10.6.5. /GateIn/portal/changeTitle (not implemented yet)
10.6.6. /GateIn/portal/pageLoaded (not implemented yet)
10.6.7. /GateIn/portal/pageUnloaded (not implemented yet)
10.6.8. /GateIn/application/applicationLoaded (not implemented yet)
10.6.9. /GateIn/application/applicationUnloaded (not implemented yet)
10.6.10. Library
10.6.11. Syntax
10.6.12. Example
10.7. Upload Component
10.7.1. Overview
10.7.2. Upload Service
10.7.3. How to use the upload component in your application
10.8. Deactivation of the Ajax Loading Mask Layer
10.8.1. Overview
10.8.2. Purpose of requirement
10.8.3. How to deactivate ajax-loading mask in your code
10.8.4. Synchronous issue
10.9. Accessing User Profile

This chapter describes the portal lifecycle from the application server start to its stop as well as how requests are handled.

An GateIn Portal instance is simply a web application deployed as a WAR in an application server. Each portlet is also part of an enhanced WAR that we call a portlet application. Hence, the portal web.xml file is the main entry point to grab information about how does the portal start.

The web.xml file contains several information such as a listener, a servlet as well as some security information.

In the web.xml we can find servlet listener:

  <!- ================================================================== ->
  <!-           LISTENER                                                 ->
  <!- ================================================================== ->
  <listener>
    <listener-class>org.exoplatform.portal.application.PortalSessionListener</listener-class>
  </listener>

That listener implements the HttpSessionListener which means it is called each time a session is created or destroyed; in other words, a session is created each time a user send a first request to the portal. That session is destroyed when he has not sent request to the portal for a long time or when he closes his browser.

public class PortalSessionListener implements HttpSessionListener

Only the destroy method of the Listener object is implemented and it is used to flush resources when a user portal session expires. Here is the code:

  /**
   * This method is called when a HTTP session of a Portal instance is destroyed. 
   * By default the session time is 30 minutes.
   * 
   * In this method, we:
   * 1) first get the portal instance name from where the session is removed.
   * 2) Get the correct instance object from the Root container
   * 3) Put the portal instance in the Portal ThreadLocal
   * 4) Get the main entry point (WebAppController) from the current portal container 
   * 5) Extract from the WebAppController the PortalApplication object which is the entry point to
   *    the StateManager object
   * 6) Expire the portal session stored in the StateManager
   * 7) Finally, removes the WindowInfos object from the WindowInfosContainer container
   * 8) Flush the threadlocal for the PortalContainer
   * 
   */
  public void sessionDestroyed(HttpSessionEvent event) {
    try {
      String portalContainerName = event.getSession().getServletContext().getServletContextName() ;
      log.warn("Destroy session from " + portalContainerName + " portal");
      RootContainer rootContainer = RootContainer.getInstance() ;
      PortalContainer portalContainer = rootContainer.getPortalContainer(portalContainerName) ;
      PortalContainer.setInstance(portalContainer); 
      WebAppController controller = 
        (WebAppController)portalContainer.getComponentInstanceOfType(WebAppController.class) ;
      PortalApplication portalApp =  controller.getApplication(PortalApplication.PORTAL_APPLICATION_ID) ;
      portalApp.getStateManager().expire(event.getSession().getId(), portalApp) ;
      
      WindowInfosContainer.removeInstance(portalContainer, event.getSession().getId());
    } catch(Exception ex) {
      log.error("Error while destroying a portal session",ex);
    } finally {
      PortalContainer.setInstance(null) ;
    }
  }

1.1 The Servlet

The servlet is the main entry point for incoming requests, it also includes some interesting init code when the portal is launched.

Here is its definition in the web.xml file:

  <!-- ================================================================== -->
  <!--           SERVLET                                                  -->
  <!-- ================================================================== -->
  <servlet>
    <servlet-name>portal</servlet-name>
    <servlet-class>org.exoplatform.portal.application.PortalController</servlet-class>
    <init-param>
      <param-name>webui.configuration</param-name>
      <param-value>app:/WEB-INF/webui-configuration.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>

The load-on-startup tag tells that the init method of the servlet is called when the application server starts. We also define some configuration for the portal at the path WEB-INF/webui-configuration.xml inside the portal WAR.

/**
 * The PortalContainer servlet is the main entry point for the GateIn Portal product.
 * 
 * Both the init() and service() methods are implemented. The first one is used to configure all the
 * portal resources to prepare the platform to receive requests. The second one is used to handle them.
 * 
 * Basically, this class is just dispatcher as the real business logic is implemented inside 
 * the WebAppController class.
 */
@SuppressWarnings("serial")
public class PortalController  extends HttpServlet {
  
  protected static Log log = ExoLogger.getLogger("portal:PortalController");  
  
  /**
   * The init() method is used to prepare the portal to receive requests. 
   * 
   *  1) Create the PortalContainer and store it inside the ThreadLocal object. The PortalContainer is
   *     a child of the RootContainer
   *  2) Get the WebAppController component from the container
   *  3) Create a new PortalApplication, init it with the ServletConfig object (which contains init params)
   *  4) Register that PortalApplication inside WebAppController
   *  5) Create a new PortalRequestHandler object and register it in the WebAppController
   *  6) Release the PortalContainer ThreadLocal 
   */
  @SuppressWarnings("unchecked")
  public void init(ServletConfig config) throws ServletException {
    super.init(config) ;
    try {
      RootContainer rootContainer = RootContainer.getInstance() ;
      PortalContainer portalContainer = 
        rootContainer.getPortalContainer(config.getServletContext().getServletContextName()) ;
      portalContainer = rootContainer.createPortalContainer(config.getServletContext()) ;
      PortalContainer.setInstance(portalContainer) ;
      WebAppController controller = 
        (WebAppController)portalContainer.getComponentInstanceOfType(WebAppController.class) ;
      PortalApplication application = new PortalApplication(config);
      application.onInit() ;
      controller.addApplication(application) ;
      controller.register(new PortalRequestHandler()) ;
    } catch (Throwable t){
      throw new ServletException(t) ;
    } finally {
      try {
        PortalContainer.setInstance(null) ;
      } catch (Exception e) {
        log.warn("An error occured while cleaning the ThreadLocal", e);
      }      
    }
    log.info("Init of PortalController Servlet successful");
  }
...

We see that a PortalApplication class is instantiated, initialized and then referenced inside the WebAppController. Note that the WebAppController is a component located inside GateIn IoC service container (and hence registered in one of our service configuration XML file).

The <code><strong>PortalApplication</strong></code> extends the <code><strong>WebuiApplication</strong></code> which itself extends the <code><strong>Application</strong></code> abstract class.

public class PortalApplication extends WebuiApplication {
  
  protected static Log log = ExoLogger.getLogger("portal:PortalApplication");  
  
  final static public String PORTAL_APPLICATION_ID = "PortalApplication" ;
  
  private ServletConfig sconfig_ ;
  private String[] applicationResourceBundleNames_ ;
  
  /**
   * The constructor references resource resolvers that allows the ApplicationResourceResolver to
   * extract files from different locations such as the current war or external one such as the resource 
   * one where several static files are shared among all portal instances.
   * 
   * 
   * @param config, the servlet config that contains init params such as the path location of
   * the XML configuration file for the WebUI framework
   */
  public PortalApplication(ServletConfig config) throws Exception {
    sconfig_ = config ;
    ApplicationResourceResolver resolver = new ApplicationResourceResolver() ;
    resolver.addResourceResolver(new ServletResourceResolver(config.getServletContext(), "war:")) ;
    resolver.addResourceResolver(new ServletResourceResolver(config.getServletContext(), "app:")) ;
    resolver.addResourceResolver(new ServletResourceResolver(config.getServletContext(), "system:")) ;
    resolver.addResourceResolver(new ServletResourceResolver(config.getServletContext().getContext("/GateInResources"), "resources:")) ;
    setResourceResolver(resolver) ;
  }
...

The main goal of this constructor is to fill an <code><strong>ApplicationResourceResolver</strong></code> with several <code><strong>ResourceResolver</strong></code> object that will allow the application to check for files such as groovy templates into different locations. Here the goal of the <code><strong>ResourceResolver</strong></code> is to abstract the different mechanisms to extract files from different location. In the previous code sample the <code><strong>ServletResourceResolver</strong></code> is used and hence methods based on the servlet context object are defined. Note that the <code><strong>ApplicationResourceResolver</strong></code> is also a class of type <code><strong>ResourceResolver</strong></code> but a special one as it can also contains several <code><strong>ResourceResolver</strong></code> itself.

Then the <code><strong>onInit()</strong></code> method of the <code><strong>PortalApplication</strong></code> is called.

/**
   * This method first calls the super.onInit() of the WebuiApplication. That super method parse the XML
   * file and stores its content in the ConfigurationManager object. It also set up he StateManager and 
   * init the application lifecycle phases.
   * 
   * Then we get all the properties file that will be used to create ResourceBundles
   */
  public void onInit() throws Exception {
    super.onInit() ;
    applicationResourceBundleNames_ =
      getConfigurationManager().getApplication().getInitParams().
      getParam("application.resource.bundle").getValue().split(",");
    for(int i = 0; i < applicationResourceBundleNames_.length; i++)  {
      applicationResourceBundleNames_[i] = applicationResourceBundleNames_[i].trim() ;
    }
  }

The <code><strong>ConfigurationManager</strong></code> object parses the XML configuration file. The idea of the framework, once again, is to abstract the type of <code><strong>webapplication</strong></code> in used. Hence the <code><strong>webui-configuration.xml</strong></code> file is used by both the portal and portlet applications.

Here is the <code><strong>webui-configuration</strong></code>:

<webui-configuration>  
  <application>     
    <init-params>
      <param>
        <name>application.resource.bundle</name>
        <value>locale.portal.expression, locale.portal.services, locale.portal.webui</value>
      </param>
    </init-params>
    <ui-component-root>org.exoplatform.portal.webui.workspace.UIPortalApplication</ui-component-root>    
    <state-manager>org.exoplatform.portal.application.PortalStateManager</state-manager>
    
    <application-lifecycle-listeners>       
      <listener>org.exoplatform.portal.application.PortalStatisticLifecycle</listener>       
      <listener>org.exoplatform.portal.application.PortalApplicationLifecycle</listener>
      <listener>org.exoplatform.webui.application.MonitorApplicationLifecycle</listener>       
    </application-lifecycle-listeners>     
    <events>
      <event>
        <event-name>portal.application.lifecycle.event</event-name>
        <listener>org.exoplatform.webui.event.ConsoleEventMonitorListener</listener>
      </event>
      <event>
        <event-name>portal.execution.lifecycle.event</event-name>
        <listener>org.exoplatform.webui.event.ConsoleEventMonitorListener</listener>
      </event>
    </events>
  </application>
</webui-configuration>

In the previous XML file we see that we define several tags such as:

1.1.1 The ui-component-root

Here it is the class <code><strong>org.exoplatform.portal.webui.workspace.UIPortalApplication</strong></code> which is a class that extends the <code><strong>UIApplication</strong></code> and hence is a sibling of <code><strong>UIPortletApplication</strong></code> (used by any GateIn Portlets as the Parent class to build the portlet component tree).

The <code><strong>UIPortalApplication</strong></code> is responsible for building its subtrees - at request time according to some configuration parameters. If all components are displayed it is composed of 3 UI components:

The <code><strong>UIPortalApplication</strong><code> constructor is shown next and is the starting point to build the UI Component tree to which Pages and Portlets will also be mounted. We will not describe that behavior here.

/**
   * The constructor of this class is used to build the tree of UI components that will be aggregated
   * in the portal page. 
   * 
   * 1) The component is stored in the current PortalRequestContext ThreadLocal 
   * 2) The configuration for the portal associated with the current user request is extracted from the 
   *    PortalRequestContext
   * 3) Then according to the context path, either a public or private portal is initiated. Usually a public
   *    portal does not contain the left column and only the private one has it.
   * 4) The skin to use is setup
   * 5) Finally, the current component is associated with the current portal owner      
   * 
   * @throws Exception
   */
  public UIPortalApplication() throws Exception {
    log = ExoLogger.getLogger("portal:UIPortalApplication"); 
    PortalRequestContext  context = PortalRequestContext.getCurrentInstance() ;
    userPortalConfig_ = (UserPortalConfig)context.getAttribute(UserPortalConfig.class);
    if(userPortalConfig_ == null) throw new Exception("Can't load user portal config");
    
    //  dang.tung - set portal language by user preference -> browser -> default
    //----
    String portalLanguage = null ;
    LocaleConfigService localeConfigService  = getApplicationComponent(LocaleConfigService.class) ;
    OrganizationService orgService = getApplicationComponent(OrganizationService.class) ;
    LocaleConfig localeConfig = localeConfigService.getLocaleConfig(userPortalConfig_.getPortalConfig().getLocale());
    String user = context.getRemoteUser();
    if(user!images/= null) {
      UserProfile userProfile = orgService.getUserProfileHandler().findUserProfileByName(user) ;
      if(userProfile!images/= null) {
        portalLanguage = userProfile.getUserInfoMap().get("user.language") ;
       } else {
         if (log.isWarnEnabled()) log.warn("Could not load user profile for " + user + ". Using default portal locale.");
       }
    }
    localeConfig = localeConfigService.getLocaleConfig(portalLanguage) ;
    if(portalLanguage == null ||!images/portalLanguage.equals(localeConfig.getLanguage())) {
      // if user language no support by portal -> get browser language if no -> get portal
      portalLanguage = context.getRequest().getLocale().getLanguage() ;
      localeConfig = localeConfigService.getLocaleConfig(portalLanguage) ;
      if(!portalLanguage.equals(localeConfig.getLanguage())) {
        localeConfig = localeConfigService.getLocaleConfig(userPortalConfig_.getPortalConfig().getLocale()) ;
      }
    }
    setLocale(localeConfig.getLocale()) ;
    setOrientation(localeConfig.getOrientation());
    //----
    context.setUIApplication(this);
    UserACL acl = getApplicationComponent(UserACL.class);
    if(acl.hasAccessControlWorkspacePermission(context.getRemoteUser()))
      addChild(UIControlWorkspace.class, UIPortalApplication.UI_CONTROL_WS_ID, null) ;
    addWorkingWorkspace() ;
    String currentSkin = userPortalConfig_.getPortalConfig().getSkin();
    if(currentSkin!images/= null && currentSkin.trim().length() > 0) skin_ = currentSkin;
    setOwner(context.getPortalOwner());
  }
...

1.1.1 The StateManager

The <code><strong>StateManager</strong></code> here is in the <code><strong>org.exoplatform.portal.application</strong></code> package which is an abstract class.

abstract public class StateManager {
  abstract public UIApplication restoreUIRootComponent(WebuiRequestContext context) throws Exception ;
  abstract public void storeUIRootComponent(WebuiRequestContext context) throws Exception ;
  abstract public void expire(String sessionId, WebuiApplication app) throws Exception ;
}

The goal of the <code><strong>StateManager</strong></code> is to abstract the way <code><strong>UIApplication</strong></code> are stored and restored for all the user session lifetime. The expire method is called from the listener we have introduced before in this chapter.

1.1.1 The application-lifecycle-listeners

There are 2 lifecycle listeners in the Portal, one for the real business logic (<code><strong>PortalApplicationLifecycle</strong></code>), the other one for some monitoring issues. They both implement the interface <code><strong>ApplicationLifecycle<E extends RequestContext></strong></code>.

public interface ApplicationLifecycle<E extends RequestContext> {
  
  public void onInit(Application app) throws Exception  ;
  public void onStartRequest(Application app, E context) throws Exception  ;
  public void onEndRequest(Application app, E context) throws Exception  ;
  public void onDestroy(Application app) throws Exception  ;
  
}

Each registered lifecycle listener will then be able to get events when several states of the portal lifecycle are reached.

1.1 The Request Handler

Once started and fully configured, the portal application WAR can handle HTTP requests.

The entry point is for sure the PortalController servlet we have already seen in the current chapter and defined in the <code><strong>web.xml</strong></code> of the portal context.

...
 /**
   * This method simply delegates the incoming call to the WebAppController stored in the Portal Container object
   */
  public void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
    try {
      ServletConfig config =  getServletConfig() ;
      RootContainer rootContainer = RootContainer.getInstance() ;
      PortalContainer portalContainer = 
        rootContainer.getPortalContainer(config.getServletContext().getServletContextName()) ;
      PortalContainer.setInstance(portalContainer) ;
      WebAppController controller = 
        (WebAppController)portalContainer.getComponentInstanceOfType(WebAppController.class) ;
      controller.service(req, res) ;
    } catch (Throwable t){
      throw new ServletException(t) ;
    } finally {
      try {
        PortalContainer.setInstance(null) ;
      } catch (Exception e) {
        log.warn("An error occured while cleaning the ThreadLocal", e);
      }      
    }
...

The <code><strong>WebAppController</strong></code> is also a simple class on which several handlers can be bound. We have already seen that the <code><strong>PortalRequestHandler</strong></code> was already added in the init method of the servlet.

...
/**
   * The WebAppControler along with the PortalRequestHandler defined in the init() method of the
   * PortalController servlet (controller.register(new PortalRequestHandler())) also add the
   * CommandHandler object that will listen for the incoming /command path in the URL
   * 
   * @throws Exception
   */
  public WebAppController() throws Exception {
    applications_ = new HashMap<String, Application>() ;
    attributes_ = new HashMap<String, Object>() ;
    handlers_ = new HashMap<String, WebRequestHandler>() ;
    register(new CommandHandler()) ;
  }
...

Then the service method - modelled according to the servlet specification is called:

...
 /**
   * This is the first method - in the GateIn web framework - reached by incoming HTTP request, it acts like a
   * servlet service() method
   * 
   * According to the servlet path used the correct handler is selected and then executed.
   * 
   * The event "exo.application.portal.start-http-request" and "exo.application.portal.end-http-request" are also sent 
   * through the ListenerService and several listeners may listen to it.
   * 
   * Finally a WindowsInfosContainer object using a ThreadLocal (from the portlet-container product) is created 
   */
  public void service(HttpServletRequest req, HttpServletResponse res) throws Exception {
    WebRequestHandler handler = handlers_.get(req.getServletPath()) ;
    if(log.isDebugEnabled()) {
      log.debug("Servlet Path: " + req.getServletPath());    
      log.debug("Handler used for this path: " + handler);
    }
    if(handler!images/= null) {
      ExoContainer portalContainer = ExoContainerContext.getCurrentContainer();
      List<ComponentRequestLifecycle> components = 
        portalContainer.getComponentInstancesOfType(ComponentRequestLifecycle.class) ;
      try {
        for(ComponentRequestLifecycle component : components) {
          component.startRequest(portalContainer);
        }
        WindowInfosContainer.createInstance(portalContainer, req.getSession().getId(), req.getRemoteUser());
        
        handler.execute(this, req, res) ;
      } finally {
        WindowInfosContainer.setInstance(null);
        for(ComponentRequestLifecycle component : components) {
          try {
            component.endRequest(portalContainer);
          } catch (Exception e) {
            log.warn("An error occured while calling the endRequest method", e);
          }
        }
      }      
    }
...

The handler in the portal case is the <code><strong>PortalRequestHandler</strong></code> which extends <code><strong>WebRequestHandler</strong></code> and that implement the abstract methods, mainly the execute() one.

/**
 * Created by The GateIn Platform SAS
 * Mar 21, 2007  
 * 
 * Abstract class that one must implement if it want to provide a dedicated handler for a custom servlet path
 * 
 * In case of portal the path is /portal but you could return your own from the getPath() method and hence the 
 * WebAppController would use your own handler
 * 
 * The execute method is to be overideen and the buisness logic should be handled here
 */
abstract public class WebRequestHandler {
  
  public void onInit(WebAppController controller) throws Exception{
    
  }
  
  abstract public String[] getPath() ;
  abstract public void execute(WebAppController app,  HttpServletRequest req, HttpServletResponse res) throws Exception ;
  
  public void onDestroy(WebAppController controler) throws Exception {
    
  }
}

Here is the main class and the entire algorithm is described in the javadoc:

/**
 * Created by The GateIn Platform SAS
 * Dec 9, 2006  
 * 
 * This class handle the request that target the portal paths /public and /private
 * 
 */
public class PortalRequestHandler extends WebRequestHandler {
  
  protected static Log log = ExoLogger.getLogger("portal:PortalRequestHandler");  
  static String[]  PATHS = {"/public", "/private"} ;
  public String[] getPath() { return PATHS ; }
  /**
   * This method will handle incoming portal request. It gets a reference to the WebAppController
   * 
   * Here are the steps done in the method:
   * 
   *   1) set the header Cache-Control to no-cache
   *   2) Get the PortalApplication reference from the controller
   *   3) Create a PortalRequestContext object that is a convenient wrapper on all the request information
   *   4) Set that context in a ThreadLocal to easily access it
   *   5) Get the collection of ApplicationLifecycle referenced in the PortalApplication and defined in the 
   *      webui-configuration.xml of the portal application
   *   6) Call onStartRequest() on each ApplicationLifecycle object
   *   7) Get the StateManager object from the PortalApplication (also referenced in the XML file) 
   *   8) Use the StateManager to get a reference on the root UI component: UIApplication; the method used is
   *      restoreUIRootComponent(context)
   *   9) If the UI component is not the current one in used in the PortalContextRequest, then replace it
   *   10) Process decode on the PortalApplication
   *   11) Process Action on the PortalApplication
   *   12) Process Render on the UIApplication UI component        
   *   11) call onEndRequest on all the ApplicationLifecycle 
   *   12) Release the context from the thread
   * 
   */
  @SuppressWarnings("unchecked")
  public void execute(WebAppController controller,  HttpServletRequest req, HttpServletResponse res) throws Exception {
    log.debug("Session ID = " + req.getSession().getId());
    res.setHeader("Cache-Control", "no-cache");
    
    PortalApplication app =  controller.getApplication(PortalApplication.PORTAL_APPLICATION_ID) ;
    WebuiRequestContext context = new  PortalRequestContext(app, req, res) ;  ;
    WebuiRequestContext.setCurrentInstance(context) ;
    List<ApplicationLifecycle> lifecycles = app.getApplicationLifecycle();
    try {
      for(ApplicationLifecycle lifecycle :  lifecycles) lifecycle.onStartRequest(app, context) ;
      UIApplication uiApp = app.getStateManager().restoreUIRootComponent(context) ;
      if(context.getUIApplication()!images/= uiApp) context.setUIApplication(uiApp) ;
      
      if(uiApp!images/= null) app.processDecode(uiApp, context) ;
      
      if(!images/context.isResponseComplete() &&!images/ context.getProcessRender()) {
        app.processAction(uiApp, context) ;
      }
      
      if(!context.isResponseComplete()) uiApp.processRender(context) ;
      
      if(uiApp!images/= null) uiApp.setLastAccessApplication(System.currentTimeMillis()) ;
    } catch(Exception ex){
      log.error("Error while handling request",ex);
    } finally {
      try {
        for(ApplicationLifecycle lifecycle :  lifecycles) lifecycle.onEndRequest(app, context) ;
      } catch (Exception exception){
    	log.error("Error while ending request on all ApplicationLifecycle",exception);
      }
      WebuiRequestContext.setCurrentInstance(null) ;
    }
  }

The PortalRequestContext class is an important one as it is used in many places. The PortalRequestContext class wraps most of the request information. Accessing it from everywhere is quite simple as the object is stored in a ThreadLocal one, which means it bound to the current request thread. Hence a single call to WebuiRequestContext.getCurrentContext() will return the correct PortalRequestContext.

As you can see, the PortalRequestContext extends the WebuiRequestContext one which also extends the abstract class RequestContext. Once again this hierarchy is to abstract the type of context in use , would it be a portal or portlet one.

/**
 * Created by The GateIn Platform SAS
 * May 7, 2006
 * 
 * This abstract class is a wrapper on top of the request information such as the Locale in use,
 * the application (for instance PortalApplication, PortletApplication...), an access to the JavascriptManager
 * as well as a reference to the URLBuilder in use.
 * 
 * It also contains a ThreadLocal object for an easy access.
 * 
 *  Context can be nested and hence a getParentAppRequestContext() is also available
 * 
 */
abstract public class RequestContext {
  
  final static public String ACTION   = "op"; 
  private  static ThreadLocal<RequestContext> tlocal_ = new ThreadLocal<RequestContext>()  ;
  
  private Application app_ ;
  protected RequestContext parentAppRequestContext_ ;
  private Map<String, Object> attributes ;
  
  protected URLBuilder urlBuilder;
  
  public RequestContext(Application app) {
    app_ =  app ;
  }
  
  public Application getApplication() { return  app_ ; }
  
  public Locale getLocale() { return parentAppRequestContext_.getLocale() ; }
  
  public ResourceBundle getApplicationResourceBundle() { return null; }
  
  abstract  public String getRequestParameter(String name)  ;
  abstract  public String[] getRequestParameterValues(String name)  ;
  
  public  JavascriptManager getJavascriptManager() { 
    return getParentAppRequestContext().getJavascriptManager() ;
  }
  
  abstract public URLBuilder getURLBuilder() ;
  
  public String getRemoteUser() { return parentAppRequestContext_.getRemoteUser() ; }
  public boolean isUserInRole(String roleUser) { return parentAppRequestContext_.isUserInRole(roleUser) ; }
  
  
  abstract public  boolean useAjax() ;
  public boolean getFullRender() { return true; }
  
  public ApplicationSession getApplicationSession()  {
    throw  new RuntimeException("This method is not supported");
  }
  
  public Writer getWriter() throws Exception { return parentAppRequestContext_.getWriter() ; }
  
  final public Object  getAttribute(String name) { 
    if(attributes == null) return null ;
    return attributes.get(name) ; 
  }
  
  final public void setAttribute(String name, Object value) {
    if(attributes == null) attributes = new HashMap<String, Object>() ;
    attributes.put(name, value) ; 
  }
  
  final public Object  getAttribute(Class type) { return getAttribute(type.getName()) ; }
  final public void    setAttribute(Class type, Object value) { setAttribute(type.getName(), value) ; }
 
  public RequestContext getParentAppRequestContext() { return parentAppRequestContext_ ; }
  public void setParentAppRequestContext(RequestContext context) { parentAppRequestContext_ = context ; }
  
  @SuppressWarnings("unchecked")
  public static <T extends RequestContext> T getCurrentInstance()  { return (T)tlocal_.get() ; }
  public static void setCurrentInstance(RequestContext ctx) { tlocal_.set(ctx) ; }
}

The WebuiRequestContext abstract class extends the RequestContext one and adds method for a Web environment such as accesses to the request and response objects or a list of components to update when using an Ajax call. More in the following header of the class:

/**
 * Created by The GateIn Platform SAS
 * May 7, 2006
 * 
 * The main class to manage the request context in a webui environment
 * 
 * It adds:
 * - some access to the root UI component (UIApplication)
 * - access to the request and response objects
 * - information about the current state of the request
 * - the list of object to be updated in an AJAX way
 * - an access to the ResourceResolver bound to an uri scheme
 * - the reference on the StateManager object
 */
abstract public class WebuiRequestContext extends RequestContext {
  
  protected UIApplication  uiApplication_ ;
  protected String sessionId_ ;
  protected ResourceBundle appRes_ ;
  private StateManager stateManager_ ;
  private boolean  responseComplete_ = false ;
  private boolean  processRender_ =  false ;
  private Throwable executionError_ ;
  private ArrayList<UIComponent>  uicomponentToUpdateByAjax ;
  
  public WebuiRequestContext(Application app) {
    super(app) ;
  }
  
  public String getSessionId() {  return sessionId_  ; }  
  protected void setSessionId(String id) { sessionId_ = id ;}
  
  @SuppressWarnings("unchecked")
  public UIApplication getUIApplication() { return uiApplication_ ; }  
  
  public void  setUIApplication(UIApplication uiApplication) throws Exception { 
    uiApplication_ = uiApplication ;
    appRes_ = getApplication().getResourceBundle(uiApplication.getLocale()) ;   
  }
  
  public Locale getLocale() {  return uiApplication_.getLocale() ;} 
  
  public ResourceBundle getApplicationResourceBundle() {  return appRes_ ; }
  
  public  String getActionParameterName() {  return WebuiRequestContext.ACTION ; }
  
  public  String getUIComponentIdParameterName() {  return UIComponent.UICOMPONENT; }
  
  abstract public String getRequestContextPath() ;
  
  abstract  public <T> T getRequest() throws Exception ;
  
  abstract  public <T> T getResponse() throws Exception ;
  
  public Throwable  getExecutionError()  { return executionError_ ; }
  
  public List<UIComponent>  getUIComponentToUpdateByAjax() {  return uicomponentToUpdateByAjax ; }
  
  public boolean isResponseComplete() { return responseComplete_ ;}
  
  public void    setResponseComplete(boolean b) { responseComplete_ = b ; }
  
  public boolean getProcessRender() { return processRender_ ;}
  
  public void    setProcessRender(boolean b) { processRender_ = b; }
  
  public void addUIComponentToUpdateByAjax(UIComponent uicomponent) {   
    if(uicomponentToUpdateByAjax == null) {
      uicomponentToUpdateByAjax =  new ArrayList<UIComponent>() ;
    }
    uicomponentToUpdateByAjax.add(uicomponent) ;
  }
 
  public ResourceResolver getResourceResolver(String uri) {
    Application app = getApplication() ;
    while(app!images/= null) {
      ApplicationResourceResolver appResolver = app.getResourceResolver() ;
      ResourceResolver resolver =  appResolver.getResourceResolver(uri) ;
      if(resolver !images/= null)  return resolver ;  
      RequestContext pcontext = getParentAppRequestContext() ;
      if(pcontext!images/= null) app = pcontext.getApplication() ;
      else app =null ;
    }
    return null ;
  }
  
  public StateManager  getStateManager() { return stateManager_; }
  public void  setStateManager(StateManager manager) { stateManager_ =  manager ; }
}

The PortalRequestContext mainly implements the abstract method already shown and only add few ones such as a reference to the portal owner or some information on the current navigation node path and the state of the portal (PUBLIC or PRIVATE ones)

The PortalRequestHandler then tries to restore the UI component tree by calling the method restoreUIRootComponent(). The first time, there is nothing to restore and in that case the following part of code in the method is used:

    if(state == null) {
      synchronized(uiApplications) {
        ConfigurationManager cmanager = app.getConfigurationManager() ;
        String uirootClass = cmanager.getApplication().getUIRootComponent() ;
        Class type = Thread.currentThread().getContextClassLoader().loadClass(uirootClass) ;
        UserPortalConfig config = getUserPortalConfig(pcontext) ;
        if(config == null) {
          HttpServletResponse response = pcontext.getResponse();
          response.sendRedirect("/portal/portal-warning.html");
          pcontext.setResponseComplete(true);
          return null;
        }
        pcontext.setAttribute(UserPortalConfig.class, config);
        UIPortalApplication uiApplication = 
          (UIPortalApplication)app.createUIComponent(type, config.getPortalConfig().getFactoryId(), null, context) ;
        state = new PortalApplicationState(uiApplication, pcontext.getAccessPath()) ;
        uiApplications.put(context.getSessionId(), state) ;
        PortalContainer pcontainer = (PortalContainer) app.getApplicationServiceContainer() ;
        pcontainer.createSessionContainer(context.getSessionId(), uiApplication.getOwner()) ;
      }
    }

The configuration manager object bound to the PortalApplication one is used to get the type for the root component which is then instanciated. the UserPortalConfig object - which is wrapper around the portal information for a given user - is also used and stored as an attribute in the PortalRequestContext. The UIPortalApplication is then created using the method createUIComponent() that is responsible of instanciating the component but also to configure it.

  public <T extends UIComponent> T createUIComponent(Class<T> type, String configId, String id, WebuiRequestContext context)  throws Exception{
    Component config = configManager_.getComponentConfig(type, configId) ;
    if(config == null) {
      throw new Exception("Cannot find the configuration for the component " + type.getName() + ", configId " +configId) ;  
    }
    T uicomponent =   Util.createObject(type, config.getInitParams());
    uicomponent.setComponentConfig(id, config) ;
    config.getUIComponentLifecycle().init(uicomponent, context) ;
    return type.cast(uicomponent) ;
  }

The ConfigurationManager method getComponentConfig() returns the Component object filled, it is a wrapper that contains all the information on the parameters for the class. Annotations are used to configure the instance as shown here:

@ComponentConfigs({
  @ComponentConfig (
    lifecycle = UIPortalApplicationLifecycle.class,
    template = "system:/groovy/portal/webui/workspace/UIPortalApplication.gtmpl",
    initParams = @ParamConfig(name = "public.showControlWorkspace", value = "true" )
  ),
  @ComponentConfig (
    id = "office" ,
    lifecycle = UIPortalApplicationLifecycle.class,
    template = "system:/groovy/portal/webui/workspace/UIPortalApplication.gtmpl",
    initParams = @ParamConfig( name = "public.showControlWorkspace", value = "false" )    
  )
})

The processDecode() method of the UIPortalApplication is doing 3 actions:

The first case it simply does nothing. Note that the super.processDecode() goes up to the UIComponent which also calls the processDecode() method on the Lifecycle object that can be associated with the UIComponent

The processAction() method of the UIPortalApplication is then called, as there is no method in the object itself it will call the processAction() of the UIPortalApplicationLifecycle bound to the UI component:

  public void processAction(UIComponent uicomponent, WebuiRequestContext context) throws Exception {
    UIPortalApplication uiApp = (UIPortalApplication) uicomponent ;
    String componentId =  context.getRequestParameter(context.getUIComponentIdParameterName()) ;
    if(componentId == null)  return;
    UIComponent uiTarget =  uiApp.findComponentById(componentId);  
    if(uiTarget == null)  return ;
    if(uiTarget == uicomponent)  super.processAction(uicomponent, context) ;
    uiTarget.processAction(context) ;
  }

If no uicomponent object is targeted, which is the case the first time (unless a bookmarked link is used) then nothing is done. Otherwise, the targeted component is extracted and a call of its processAction() method is executed.

Then it is time to render the content and this is done inside the processRender() method. The method of the UIPortalApplication is shown here and it is the one that handles either full portal generation or AJAX request:

  /**
   * The processrender() method handles the creation of the returned HTML either for a full
   * page render or in the case of an AJAX call
   * 
   * The first request, Ajax is not enabled (means no ajaxRequest parameter in the request) and 
   * hence the super.processRender() method is called. This will hence call the processrender() of 
   * the Lifecycle object as this method is not overidden in UIPortalApplicationLifecycle. There we 
   * simply render the bounded template (groovy usually). Note that bounded template are also defined
   * in component annotations, so for the current class it is UIPortalApplication.gtmpl
   * 
   * On second calls, request have the "ajaxRequest" parameter set to true in the URL. In that case 
   * the algorithm is a bit more complex:
   * 
   *    a) The list of components that should be updated is extracted using the 
   *       context.getUIComponentToUpdateByAjax() method. That list was setup during the process action
   *       phase
   *    b) Portlets and other UI components to update are split in 2 different lists
   *    c) Portlets full content are returned and set with the tag <div class="PortalResponse">
   *    d) Block to updates (which are UI components) are set within 
   *       the <div class="PortalResponseData"> tag
   *    e) Then the scripts and the skins to reload are set in the <div class="PortalResponseScript">
   * 
   */
  public void  processRender(WebuiRequestContext context) throws Exception {
    Writer w =  context.getWriter() ;
    if(!context.useAjax()) {
      super.processRender(context) ;
    } else {
      PortalRequestContext pcontext = (PortalRequestContext)context;
      List<UIComponent> list = context.getUIComponentToUpdateByAjax() ;
      List<UIPortlet> uiPortlets = new ArrayList<UIPortlet>(3);
      List<UIComponent> uiDataComponents = new ArrayList<UIComponent>(5);
      if(list!images/= null) {
        for(UIComponent uicomponent : list) {
          if(uicomponent instanceof UIPortlet) uiPortlets.add((UIPortlet)uicomponent) ;
          else uiDataComponents.add(uicomponent) ;
        }
      }
      w.write("<div class=\"PortalResponse\">") ;
      if(!context.getFullRender()) {
        for(UIPortlet uiPortlet : uiPortlets) {
          uiPortlet.processRender(context) ;
        }
      }
      w.  write("<div class=\"PortalResponseData\">");
      for(UIComponent uicomponent : uiDataComponents) {
        renderBlockToUpdate(uicomponent, context, w) ;
      }
      String skin  = getAddSkinScript(list);
      w.  write("</div>");
      w.  write("<div class=\"PortalResponseScript\">"); 
      w.    write(pcontext.getJavascriptManager().getJavascript());
      w.    write("GateIn.core.Browser.onLoad();\n"); 
      w.    write(pcontext.getJavascriptManager().getCustomizedOnLoadScript()) ;
      if(skin!images/= null){
        w.  write(skin) ;
      }
      w.  write("</div>") ;
      w.write("</div>") ;
    }
  }

The RTL framework (Right-To-Left framework) provides a set of tools that can be leveraged by the user interface components to handle directionality gracefully.

<object width="400" height="300"><param name="allowfullscreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="movie" value="http://vimeo.com/moogaloop.swf?clipid=2870309&server=vimeo.com&showtitle=1&showbyline=1&showportrait=0&color=&fullscreen=1" /><embed src="http://vimeo.com/moogaloop.swf?clipid=2870309&server=vimeo.com&showtitle=1&showbyline=1&showportrait=0&color=&fullscreen=1" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="400" height="300"></embed></object><br /><a href="http://vimeo.com/">GateIn Portal: RTL - Arabic support</a> from <a href="http://vimeo.com/user896168">Benjamin Mestrallet</a> on <a href="http://vimeo.com">Vimeo</a>.

The orientation depends on the current locale and during a portal request the current orientation is made available by various means. The orientation is a Java 5 enum that provides a set of functionalities:

   LT, // Western Europe
   RT, // Middle East (Arabic, Hebrew)
   TL, // Japanese, Chinese, Korean
   TR; // Mongolian
   public boolean isLT() { ... }
   public boolean isRT() { ... }
   public boolean isTL() { ... }
   public boolean isTR() { ... }
}{code}
The object defining the current Orientation for the current request is the UIPortalApplication. However it should be accessed at runtime using the RequestContext that delegates to the UIPortalApplication. In the case of a PortalRequestContext it is a direct delegate as the PortalRequestContext has a reference to the current UIPortalApplication. In case of a different context such as the PortletRequestContext, it delegates to the parent context given the fact that the root RequestContext is always a PortalRequestContext.
h1. Usage in different layers
h2. Java
Orientation is obtained from the RequestContext:

Orientation is obtained from implicit variables defined by the groovy binding context:

The skin service handles stylesheet rewriting to accommodate the orientation. It works by appending -lt or -rt to the stylesheet name. For instance /web/skin/portal/webui/component/UIFooterPortlet/DefaultStylesheet-rt.css will return the same stylesheet as /web/skin/portal/webui/component/UIFooterPortlet/DefaultStylesheet.css but processed for the RT orientation. Obviously the -lt suffix is optional.

Stylesheet authors can annotate their stylesheet to create content that depends on the orientation.

In the example we need to use the orientation to modify the float attribute that will make the horizontal tabs either float on left or on right:

  float: left; /* orientation=lt */
  float: right; /* orientation=rt */
  font-weight: bold;
  text-align: center;
  white-space: nowrap;
}{code}
The LT output will be:

float: left; /orientation=lt{/ font-weight: bold; text-align: center; white-space: nowrap; }{code}

The RT output will be:

  float: right; /* orientation=rt */
  font-weight: bold;
  text-align: center;
  white-space: nowrap;
}{code}
In this example we need to modify the padding according to the orientation:

color: white; line-height: 24px; padding: 0px 5px 0px 0px; /orientation=lt/ padding: 0px 0px 0px 5px; /orientation=rt{/ }{code}

The LT output will be:

  color: white;
  line-height: 24px;
  padding: 0px 5px 0px 0px; /* orientation=lt */
}{code}
The RT output will be:

color: white; line-height: 24px; padding: 0px 0px 0px 5px; /orientation=rt{/ }{code}

Sometime it is necessary to create an RT version of an image that will be used from a template or from a stylesheet. However symmetric images can be automatically generated avoiding the necessity to create a mirrored version of an image and furthermore avoiding maintenance cost.

The web resource filter uses the same naming pattern than the skin service does. When an image ends with the -rt suffix the portal will attempt to locate the original image and create a mirror of it. For instance requesting the image /GateInResources/skin/DefaultSkin/webui/component/UITabSystem/UITabs/background/NormalTabStyle-rt.gif returns a mirror of the image /GateInResources/skin/DefaultSkin/webui/component/UITabSystem/UITabs/background/NormalTabStyle.gif and it works perfectly because the image is symmetric.

Here is an example combining stylesheet and images:

  line-height: 24px; 
  background: url('background/NavigationTab.gif') no-repeat right top; /* orientation=lt */
  background: url('background/NavigationTab-rt.gif') no-repeat left top; /* orientation=rt */
  padding-right: 2px; /* orientation=lt */
  padding-left: 2px; /* orientation=rt */
}{code}
h2. Client side JavaScript
Just use the *GateIn.core.I18n* object that provides the following methods:
* getOrientation() : returns either the string lt or rt
* getDir() : returns either the string ltr or rtl
* isLT() : returns true for LT
* isRT() : returns true of RT

All aspects of internationalization in GateIn products are covered. You should have a general knowledge of Internationalization in Java products. Sun created a good internationalization tutorial .

You should notice that

<listitem> Furthermore there are properties files in portal sub-folder, they form together the portal resource bundle .: ::/WEB-INF/classes/locale/portal </listitem>

The executive-boarden.properties{code:none}{code} The keys (example: ~~organization.newstaff~~) can have any name but must not contain spaces. The values (~~New Staff~~) contain the translation to the language of the resource file. The suffix "en" means in this case "English".

  1. info("There are also resource bundles in XML format Portal.XML Resource Bundles which are a proprietary format of GateIn Platform.")

1 LocalesConfig

In .../WEB-INF/conf/common/common-configuration.xml > http:--fisheye.exoplatform.org-browse-projects-portal-trunk-web-portal-src-main-webapp-WEB-INF-conf-common-common-configuration.xml?r=28705 you find:

 <component>
  <key>org.exoplatform.services.resources.LocaleConfigService</key> 
  <type>org.exoplatform.services.resources.impl.LocaleConfigServiceImpl</type> 
 <init-params>
 <value-param>
  <name>locale.config.file</name> 
  <value>war:/conf/common/locales-config.xml</value> 
  </value-param>
  </init-params>
  </component>

The configuration points to the locale configuration file like this one: ::/WEB-INF/conf/common/locales-config:xml {code:xml} <locale-config> <locale>ar</locale> <output-encoding>UTF-8</output-encoding> <input-encoding>UTF-8</input-encoding> <description>Default configuration for the Arabic locale</description> <orientation>rt</orientation> </locale-config> {code}

The locale has to be defined using ISO 639 > http:--ftp.ics.uci.edu-pub-ietf-http-related-iso639.txt, in this example "ar" is Arabic.

This configuration defines also the list of languages in the "Change Language" section of the portal.

1.1 Encoding It's highly recommended to always use UTF-8. You should also encode all property files in UTF-8.

In the java implementation, the encoding parameters will be used for the request response stream. The input-encoding parameter will be used for request setCharacterEncoding(..).

1.1 Orientation The default orientation of text and images is Left-To-Right. As you know GateIn support Right-To-Left orientation. Therefore for Arabic you define

  • <orientation>rt</orientation>

1 ResourceBundleService

The resource bundle service is configured here: // http://fisheye.exoplatform.org/browse/projects/portal/trunk/web/portal/src/main/webapp/WEB-INF/conf/common/common-configuration.xml?r=28705

Caution: Other GateIn products like DMS use dedicated configuration file called "resource-bundle-configuration.xml".

A typical configuration looks like this one: {code:xml} <component> <key>org.exoplatform.services.resources.ResourceBundleService</key> <type>org.exoplatform.services.resources.jcr.ResourceBundleServiceImpl</type> <init-params>

<values-param> <name>classpath.resources</name> <description>The resources that start with the following package name should be loaded from file system</description> <value>locale.portlet</value> </values-param>

<values-param> <name>init.resources</name> <description>Store the following resources in the DB for the first launch </description> <value>locale.portal.expression</value> <value>locale.portal.services</value> <value>locale.portal.webui</value> <value>locale.portal.custom</value>

<value>locale.navigation.portal.classic</value> <value>locale.navigation.group.platform.administrators</value> <value>locale.navigation.group.platform.users</value> <value>locale.navigation.group.platform.guests</value> <value>locale.navigation.group.organization.management.executive-board</value> </values-param>

<values-param> <name>portal.resource.names</name> <description>The properties files of the portal, these files will be merged into one ResoruceBundle properties </description> <value>locale.portal.expression</value> <value>locale.portal.services</value> <value>locale.portal.webui</value> <value>locale.portal.custom</value> </values-param>

</init-params> </component>

There are three parameters: *classpath.resources*, *init.resources*, and *portal.resource.names*. We will talk later about _classpath.resources_. 
In _init.resources_ you have to define _*all resources*_ that you want use in the product, independently of the fact that they belong to the portal or to the navigation. All these resources are stored in JCR at the first launch of your product. After that, you only can modify these resources using the [Portal:Internationalization Portlet].
h2. Portal Resource Bundle
The parameter *portal.resource.names* defines all resources that belong to the *Portal Resource Bundle*. This means that these resources are merged to a *single resource bundle* which is accessible from anywhere in GateIn products. As mentioned, all these keys are located in the same bundle, which is separated from the navigation resource bundles.
h2. Navigation Resource Bundles
There is a resource bundle for each navigation. A navigation can exist for user, groups, and portal. In the example above you see bundle definitions for the navigation of the classic portal and of four different groups. Each of these resource bundles lives in a different sphere, they are independent of each other and they do not belong to the portal.resource.names parameter (because they are not mentioned in _portal.resource.names_).
As you learned in the introduction you must put the properties for a group in the _WEB-INF/classes/locale/navigation/group/_ folder.
Example: 
*.../portal/trunk/web/portal/src/main/webapp/WEB-INF/classes/locale/navigation/group/organization/management/executive-board_en.properties* 
The folder and file names must correspond to the group hierarchy. The group name "executive-board" is followed by the iso 639 code. For each language you defined in the LocalesConfig you must provide a resource file.
If you ever change the name of a group you also need to change the name of the folder and/or files of the correspondent navigation resource bundles.
You already know the content of _executive-board_en.properties_:
{code:none}
organization.title=Organization
organization.newstaff=New Staff
organization.management=Management

This resource bundle is only accessible for the navigation of the ~~organization.management.executive-board~~ group.

1 Portlet

1.1 classpath.resources

Portlets are independent application and they deliver their own resource files. You can find an example for the GadgetPortlet: .../WEB-INF/classes/locale/portlet/gadget/GadgetPortleten.properties > http:--fisheye.exoplatform.org-browse-projects-portal-trunk-portlet-dashboard-src-main-webapp-WEB-INF-classes-locale-portlet-gadget-GadgetPortleten.properties?r=24413

All portlet resources are located in the locale/portlet subfolder. The ResourceBundleService parameter classpath.resources defines exactly this subfolder. Doing so the resource file that are in ~~locale/portlet~~ will never be stored in the JCR and reloaded at each start of the application server.

<values-param>
  <name>classpath.resources</name>
  <description>The resources that start with the following package name should
               be loaded from file system</description>
  <value>locale.portlet</value>      
</values-param>  

Let's suppose you want to add a Spanish translation to the GadgetPortlet.

Create the file in:

In portlet.xml, add Spanish as a supported-locale, the resource-bundle is already declared and is the same for all languages : {code:xml} <supported-locale>en</supported-locale> <supported-locale>es</supported-locale> <resource-bundle>locale.portlet.gadget.GadgetPortlet</resource-bundle> {code}

Find more details about portlet internationalization > http:--developers.sun.com-portalserver-reference-techart-i18n-portlets.html.

1.1 Standard Portlet Resource Keys There are three standard keys defined : Title, Short Title and Keywords. Keywords contain a comma-separated list of keywords.

 javax.portlet.title=Breadcrumbs Portlet
 javax.portlet.short-title=Breadcrumbs
 javax.portlet.keywords=Breadcrumbs, Breadcrumb

Whenever you want to display a property in the user language you use its key. Using the below access method the translation is returned in the preferred language of the current http session:

WebuiRequestContext context = WebuiRequestContext.getCurrentInstance() ;
ResourceBundle res = context.getApplicationResourceBundle() ;
String translatedString = res.getString(key);

When an application needs to be translated, it is never obvious to find out the right key for a given translated property. When the portal is executed in debug mode it is possible to select among the available languages a special language called Magic locale.

This feature translates a key to the same key value. For instance, the translated value for the key "organization.title" is simply the value "organization.title". Selecting that language allows to use the portal and its applications with all the keys visible and it is easy to find out the correct key for a given label in the portal page.

Usually resource bundles are stored in property files however as property files are plain files it raise issues with the encoding of the file. The XML resource bundle format has been developped to provide an alternative to property files.

In order to be loaded by the portal at runtime (actually the resource bundle service), the name of the file must be the same as a property file but instead of ending with the .properties suffix, it ends with the .xml suffix. For instance AccountPortlet ar.xml instead of AccountPortlet ar.properties .

The XML format is very simple and has been developed based on the DRY (Don't Repeat Yourself) principle. Usually resource bundle keys are hierarchically defined and we can leverage the hierarchic nature of the XML for that purpose. Here is an example of turning a property file into an XML resource bundle file:

<STYLE type="text/css"> .code {width: 97%} </STYLE>

UIAccountForm.tab.label.AccountInputSet = ...
UIAccountForm.tab.label.UIUserProfileInputSet = ...
UIAccountForm.label.Profile = ...
UIAccountForm.label.HomeInfo= ...
UIAccountForm.label.BusinessInfo= ...
UIAccountForm.label.password= ...
UIAccountForm.label.Confirmpassword= ...
UIAccountForm.label.email= ...
UIAccountForm.action.Reset= ...
<?xml version="1.0" encoding="UTF-8"?>
<bundle>
  <UIAccountForm>
    <tab>
      <label>
        <AccountInputSet>...</AccountInputSet>
        <UIUserProfileInputSet>...</UIUserProfileInputSet>
      </label>
    </tab>
    <label>
      <Profile>...</Profile>
      <HomeInfo>...</HomeInfo>
      <BusinessInfo>...</BusinessInfo>
      <password>...</password>
      <Confirmpassword>...</Confirmpassword>
      <email>...</email>
    </label>
    <action>
      <Reset>...</Reset>
    </action>
  </UIAccountForm>
</bundle>

Indeed, the usual way of rendering a portal page is a static one where you need a template, usually a jsp page, for each layout (2 columns, 3 columns and so on). That makes you depend on the integrator or developers as for each new layout you will need to ask for a custom development.

GateIn, with its dynamic way that creates a tree of nested UI containers that contain portlets as shown in the picture below. Each container is responsible for rendering its children. In the picture, the main container renders its children in several rows while the nested container displays them as columns.

Furthermore, by manipulating the tree using the WYSIWYG editor, it allows you to create new containers, define how they will render their children, add new portlets.

As most portal use the static layout mechanism, they can only drag portlets from one static location, let's say a column, to another one.

With GateIn Portal, it is possible to also drag the UI containers and the portlets and drop them in containers that are deeper or upper in the Portal component tree. This feature is unique and not just a tool!

With this innovative concept of dynamic layout, you can easily create portal pages with complex layout.

This kind of communication is made to allow applications within a page to exchange data. This library is made for broadcasting messages on topic. This is basically based on 3 functions : subscribe, publish and unsubscribe.

When you subscribe to a topic, you receive all the subtopic message. for example, if I subscribe to "/GateIn/application", and an application send a message on "/GateIn/application/map", i will receive it, but if another application send a message on "/GateIn", i will not receive it.

It contains all the events generated by the platform.

When a message is sent on this topic, a popup message appears on the top right of the screen.

Send a message on this channel to change (and to be notified) the title of the portal.

Receive a message when a page is loaded.

Receive a message when a page is unloaded.

Receive a message when an application is loaded in the page.

Receive a message when an application is unloaded in the page.

The inter application communication http://fisheye.exoplatform.org/projects/browse/projects/portal/trunk/web/GateInResources/src/main/webapp/javascript/GateIn/core/Topic.js

subscribe is used to subscribe a callback to a topic
*Parameters:*
* topic is the topic that will be listened
* obj is the context object
* funcName is the name of the function of obj to call when a message is received on the topic
funcName have to be a function that take an Object in parameter. the event received have this format: 
{code:javascript}
{
  senderId:senderId,
  message:message,
  topic: topic
}
publish is used to publish an event to the other subscribers to the given channels
*Parameters:*
* senderId is a string that identify the sender
* topic is the topic that the message will be published
* message is the message that's going to be delivered to the subscribers to the topic

unsubscribe is used to unsubscribe a callback to a topic

Topic Demo

In this article, you will learn how to :

The service is defined by the class : org.exoplatform.upload.UploadService;

You can configure it with the following xml code :

<component>
   <type>org.exoplatform.upload.UploadService</type>
     <init-params>
       <value-param>
        <name>upload.limit.size</name>
        <description>Maximum size of the file to upload in MB</description>
        <value>10</value>
      </value-param>
    </init-params>  
  </component>

As you can see, you can configure a default upload size limit for the service. The value unit is in MegaBytes. This limit will be used by default by all applications if no specific limit is set. You will see in the next chapter how to set a different limit for your application.

If you set the value at 0, the upload size will be unlimited.

To use the component, you must create an object of type org.exoplatform.webui.form.UIFormUploadInput, using one of the two available constructors :

public UIFormUploadInput(String name, String bindingExpression)

or:

public UIFormUploadInput(String name, String bindingExpression, int limit)

Here is an example using the second form : {code} PortletRequestContext pcontext = (PortletRequestContext)WebuiRequestContext.getCurrentInstance(); PortletPreferences portletPref = pcontext.getRequest().getPreferences(); int limitMB = Integer.parseInt(portletPref.getValue("uploadFileSizeLimitMB", "").trim()); UIFormUploadInput uiInput = new UIFormUploadInput("upload", "upload", limitMB) ;

To get the limit from the xml configuration, you can add this piece of code in the files portlet.xml or portlet-preferences.xml :
{code:xml}
<preference>
  <name>uploadFileSizeLimitMB</name>
  <value>30</value>
  <read-only>false</read-only>
</preference> 

Again, a 0 value means unlimited upload size, and the value unit is set in MegaBytes.

To get the uploaded data use the ~~getUploadDataAsStream()~~ method: {code} UIFormUploadInput input = (UIFormUploadInput)uiForm.getUIInput("upload"); InputStream inputStream = input.getUploadDataAsStream(); ... jcrData.setValue(inputStream) ; {code}

1 Clean the uploaded file

The upload service stores a temporary file on the filesystem during the process. When the upload is finished, you must clean the service in order to :

To do that, use the ~~removeUpload()~~ method defined in the upload service, like this :

UploadService uploadService = uiForm.getApplicationComponent(UploadService.class) ;
UIFormUploadInput uiChild = uiForm.getChild(UIFormUploadInput.class) ;
uploadService.removeUpload(uiChild.getUploadId()) ;

In this article, you will learn:

To generate script to make an asynchronous ajax-call, we use uicomponent.doAsync() method instead of uicomponent.event() method.

Here is an example:

<a href="<%=uicomponent.doAsync(action, beanId, params)%>" alt="">Asynchronous</a>

Method doAsync() automatically adds a parameter into parameters list. Parameter asyncparam = new Parameter(AJAXASYNC,"true"); (AJAXASYNC == "ajaxasync")

After all, its call method event() to generate script that make Ajax Request. This request is asynchronous and ajax-loading mask will not displayed.

Note:

1. You still also make an asynchronous request by using method uicomponent.event(). By this way, you must add asyncparam manually.

2. GUI is blocked so that user can do only one action at a time (Request seems to be synchronous). But in fact ajax request always be asynchronous. See Synchronous issue section.

Almost web browser such as (IE, Chrome, Safari .. ) told that ajax request may used in two modes: Synchronous / Asynchronous with boolean value of bAsyn parameter. View reference.

var bAsync = false; // Synchronous

request.open(instance.method, instance.url, bAsync);

But Mozilla say no. They doesn't support synchronous request. var bAsync = false; // Synchronous

request.open(instance.method, instance.url, bAsync); // Firefox will not execute

So we decide to set bAsync always true (Ajax request always be asynchronous).

// Asynchronous request

request.open(instance.method, instance.url, true);

It is cause that Ajax Request always be asynchronous.

To retrieve the logged in user you can do as follows :

// Alternative context: WebuiRequestContext context =
		WebuiRequestContext.getCurrentInstance() ;
		PortalRequestContext context = PortalRequestContext.getCurrentInstance() ;
		// Get the id of the user logged
		String userId = context.getRemoteUser();
		// Request the information from OrganizationService:
		OrganizationService orgService = getApplicationComponent(OrganizationService.class) ;
		if(userId!images/= null) {
		User user = orgService.getUserHandler().findUserByName(userId) ;
		if (user!images/= null) {
		String firstName = user.getFirstName();
		String lastName = user.getLastName();
		String email = user.getEmail();
		}
		}

Alternatives for retrieving the Organization Service