Chapter 14. Portal Extension

When extending an existing portal container, the name of the portal in the extension configuration is the same as the name of the existing portal container. The configuration (and other aspects) of the existing portal container can therefore be shadowed by the extension. Using this approach, many aspects of an available portal container can be customized, such as,
  • Customize groovy template for a portlet.
  • Customize skin for a portlet.
  • Customize CSS and images.
  • Customize navigation and pages.
  • Internationalization of navigation nodes.
  • Customize sign-in page.

14.1. How the Shadowing Mechanism Works

The following diagram illustrates how the shadowing mechanism works.
Diagram explaining how shadowing works, linking portal.war to my-extension.war inside the customized extension, and linking the main gatein/configuration.xml file with the conf/configuration.xml file in the customized extension.

Figure 14.1. Shadowing Mechanism

The extension's EAR conf/configuration.xml file plays a crucial role in shadowing.

Example 14.1. configuration.xml

<configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.exoplaform.org/xml/ns/kernel_1_2.xsd http://www.exoplaform.org/xml/ns/kernel_1_2.xsd" xmlns="http://www.exoplaform.org/xml/ns/kernel_1_2.xsd">
 <external-component-plugins>
     <!-- The full qualified name of the PortalContainerConfig -->
     <target-component>org.exoplatform.container.definition.PortalContainerConfig</target-component>
     <component-plugin>
         <!-- The name of the plugin -->
         <name>Add PortalContainer Definitions</name>
         <!-- The name of the method to call on the PortalContainerConfig in order to register the PortalContainerDefinitions -->
         <set-method>registerPlugin</set-method>
         <!-- The full qualified name of the PortalContainerDefinitionPlugin -->
         <type>org.exoplatform.container.definition.PortalContainerDefinitionPlugin</type>
         <init-params>
             <object-param>
                 <name>portal</name>
                 <object type="org.exoplatform.container.definition.PortalContainerDefinition">
                     <!-- The name of the Portal Container: note that a Portal Container called "portal" 
                          already exists in the default GateIn installation. Therefore, we actually redefine 
                          that existing Portal Container here. -->
                     <field name="name">
                         <string>portal</string>
                     </field>
                     <!-- The name of the context name of the rest web application -->
                     <field name="restContextName">
                         <string>rest</string>
                     </field>
                     <!-- The name of the realm -->
                     <field name="realmName">
                         <string>gatein-domain</string>
                     </field>
                     <field name="externalSettingsPath">
                         <string>configuration.properties</string>
                     </field>
                     <!--
                         The list of all the context names that are needed to initialize the Portal Container properly.
                         The order of the dependencies will define the initialization order and also the order for 
                         loading resources. When a resource with the same path, say /dir/subdir/resource, is available 
                         in more than one of the listed contexts, the one from the context closest to the end of this list 
                         will be chosen. Here we want the resources available in gatein-portal-extension to win over all 
                         other resources. Therefore we have added gatein-portal-extension as the last element of the list.
                     -->
                     <field name="dependencies">
                         <collection type="java.util.ArrayList">
                             <value>
                                 <string>eXoResources</string>
                             </value>
                             <value>
                                 <string>portal</string>
                             </value>
                             <value>
                                 <string>dashboard</string>
                             </value>
                             <value>
                                 <string>exoadmin</string>
                             </value>
                             <value>
                                 <string>eXoGadgets</string>
                             </value>
                             <value>
                                 <string>gwtGadgets</string>
                             </value>
                             <value>
                                 <string>eXoGadgetServer</string>
                             </value>
                             <value>
                                 <string>rest</string>
                             </value>
                             <value>
                                <string>web</string>
                             </value>
                             <value>
                                 <string>gatein-portal-extension</string>
                             </value>
                         </collection>
                     </field>
                 </object>
             </object-param>
         </init-params>
     </component-plugin>
 </external-component-plugins>
</configuration>
In this code extract, the name parameter of the PortalContainerDefinition is set to "portal" . Because a portal container called "portal" already exists in the default portal installation, the container name is redefined in the above file.
Other resources available in an existing portal container can be customized in a similar way. See the following sections for more details.

14.2. Custom Groovy Template for a Portlet

Shadowing of built-in Groovy templates is controlled by the order of <dependencies> in the conf/configuration.xml file.
If a template with the same path (for example, /dir/subdir/template.gtmpl) is available in more than one of the dependencies, the template from the context closest to the end of the list of dependencies is chosen. To ensure that the template created in Procedure 14.1, “Customizing HomePagePortlet” is chosen, gatein-portal-extension is added as the last element in the list.

Note

This section mentions code from the Portal Extension Examples, which is available for download as part of a valid Red Hat subscription.

Procedure 14.1. Customizing HomePagePortlet

Complete this procedure to change the text and layout used in the default HomePagePortlet shipped with the platform in a custom extension.
  1. Copy $JPP_HOME/gatein/gatein.ear/portal.war/templates/groovy/webui/component/UIHomePagePortlet.gtmpl
  2. Paste the file into the war/src/main/webapp/templates/groovy/webui/component/ folder of the extension EAR.

    Important

    The file location in the custom extension must mirror the original location of the file for shadowing to work correctly.
  3. Make the desired changes to the text and layout of the HomePagePortlet in the custom extension.
  4. Deploy the extension EAR to the portal platform. The HomePagePortlet mirrors the changes made in the custom extension.

14.3. Custom Skin for a Portlet

Note

This section mentions code from the Portal Extension Examples available for download as part of a valid Red Hat subscription.
The same guidelines that apply when creating a new portlet skin also apply when creating a custom skin for a built-in portlet distributed with the portal.
Declare the skin first in the gatein-resources.xml file, and then create the CSS and other supporting files such as backgrounds.
Shadowing of built-in Groovy templates is controlled by the order of <dependencies> in the configuration of the Portal Container (or Extension).
When the HomePagePortlet skin defined in web.war is available in more than one of the dependencies, the template from the context closest to the end of the list of dependencies is chosen. To ensure that the skin available in gatein-portal-extension is chosen, gatein-portal-extension is added as the last element in the list.

14.3.1. gatein-resources.xml

gatein-resources.xml is located in the war/src/main/webapp/WEB-INF sub-folder of the Portal Extension Quickstart project.

Example 14.2. gatein-resources.xml


<gatein-resources xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.gatein.org/xml/ns/gatein_resources_1_3 http://www.gatein.org/xml/ns/gatein_resources_1_3"
xmlns="http://www.gatein.org/xml/ns/gatein_resources_1_3">

<!-- The platform will load this CSS for HomePagePortlet instead of the default one. -->
<portlet-skin>
    <!-- 
        The name of the web application containing the portlet.xml file in which
        BannerPortlet is definded. The application name is usually the same as 
        the name of the WAR through which the application was deployed. 
    -->
    <application-name>web</application-name>
    <!-- <portlet-name> value from the portlet.xml referenced above -->
    <portlet-name>HomePagePortlet</portlet-name>
    <skin-name>Default</skin-name>
    <css-path>/templates/skin/webui/component/UIHomePagePortlet/DefaultStylesheet.css</css-path>
</portlet-skin>

14.3.2. CSS and Images

In the gatein-resources.xml file in Section 14.3.1, “gatein-resources.xml”, the custom skin is set to use the CSS file /templates/skin/webui/component/UIHomePagePortlet/DefaultStylesheet.css which translates to the Portal Extension Quickstart project war/src/main/webapp/templates/skin/webui/component/UIHomePagePortlet/DefaultStylesheet.css file. In the CSS file itself, image files use relative paths as demonstrated in Example 14.3, “DefaultStylesheet.css”:

Example 14.3. DefaultStylesheet.css


.UIHomePagePortlet .TRContainer .DotLine {
 background: url("DefaultSkin/background/Line.gif") no-repeat -2px top;
 height: 1px;
 width: 182px;
 margin: 7px auto;
}

14.4. Custom Navigation and Pages

Note

This section mentions code from the Portal Extension Examples available for download as part of a valid Red Hat subscription.

14.4.1. portal-configuration.xml

This configuration file is located in the war/src/main/webapp/WEB-INF/conf/sample-ext/portal directory of the Portal Extension quickstart. It is the starting point for navigation customization. There is normally no need to change any configuration in this file, although it is useful to know the location of this file.
For more information about portal-configuration.xml, see the "Portal Navigation Configuration" section in the Administration and Configuration Guide.

14.5. Navigation Node Types

There are three types of navigation nodes, as described in Figure 14.2, “Types of Navigation Nodes” :
The JBoss Portal Platform home screen, with the Site tab (1), Group menu (2), and Dashboard menu (3) called out.

Figure 14.2. Types of Navigation Nodes

1 (Site)
Navigation nodes of the current site.
2 (User Group)
Navigation nodes of user groups which the current user is a member.
3 (User)
Navigation nodes of the current user.

Example 14.4. Adding a Navigation Node to a Site Navigation

The file war/src/main/webapp/WEB-INF/conf/sample-ext/portal/portal/classic/navigation.xml contains the classic site navigation which can be modified.

<node-navigation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.gatein.org/xml/ns/gatein_objects_1_3 http://www.gatein.org/xml/ns/gatein_objects_1_3" xmlns="http://www.gatein.org/xml/ns/gatein_objects_1_3">
 <!-- Priority has no significance in site navigations. But it is required. See GTNPORTAL-2751 -->
 <priority>1</priority>
 
 <!-- 
     In this file, we are adding a navigation node to the navigation of the site called "classic". 
     The site-wide navigation is used e.g. in NavigationPortlet. 
     Visibility of navigation nodes depends on <access-permissions> set in the sibling pages.xml file.
     See also portal.configuration section of the file ../../portal-configuration.xml. 
 -->
 <page-nodes>
     <node>
         <name>page-just-added</name>
         <!-- 
             #{portal.extension.justAddedPage} is  place holder for an internationalized string.
             See WEB-INF/classes/locale/navigation/portal/classic_en.properties
         -->
         <label>#{portal.extension.justAddedPage}</label>
     </node>
</page-nodes>
</node-navigation>
By appending the page-just-added navigation node into the portal navigation, the new site navigation node is now visible in the navigation bar.
Navigation menu, with Page Just Added now visible as a site navigation option.
The page-just-added navigation node refers to portal::classic::justAddedPage which is defined in the pages.xml file located in the same directory as the navigation.xml file.
<page-set xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.gatein.org/xml/ns/gatein_objects_1_3 http://www.gatein.org/xml/ns/gatein_objects_1_3"
 xmlns="http://www.gatein.org/xml/ns/gatein_objects_1_3">
 <page>
     <name>justAddedPage</name>
     <title>Page Just Added</title>
     <!-- This page and any navigation nodes refering to it will be visible to all visitors (signed in or anonymous). -->
     <access-permissions>Everyone</access-permissions>
     <edit-permission>*:/platform/administrators</edit-permission>
     <portlet-application>
         <portlet>
             <application-ref>gatein-portal-extension</application-ref>
             <portlet-ref>JustAddedPortlet</portlet-ref>
         </portlet>
         <title>Just Added Portlet</title>
         <access-permissions>Everyone</access-permissions>
         <show-info-bar>false</show-info-bar>
         <show-application-state>false</show-application-state>
         <show-application-mode>false</show-application-mode>
     </portlet-application>
 </page>
</page-set>
Note that page-just-added navigation node and the related pages are visible to all portal visitors (signed in or anonymous) due to <access-permissions> set to Everyone in the above file.

Example 14.5. Adding a Navigation Node to a Group Navigation

To add a new navigation node visible to the members of /platform/administrators group, create a navigation.xml file located in war/src/main/webapp/WEB-INF/conf/sample-ext/portal/group/platform/administrators/navigation.xml. The name of the user group /platform/administrators forms part of the path.

<node-navigation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.gatein.org/xml/ns/gatein_objects_1_3 http://www.gatein.org/xml/ns/gatein_objects_1_3" xmlns="http://www.gatein.org/xml/ns/gatein_objects_1_3">
<!-- Priority is currently ignored for importMode merge. But it is required. See GTNPORTAL-2751 -->
<priority>1</priority>

<page-nodes>
    <node>
        <name>admins-sitemap</name>
        <!-- 
            #{platform.administrators.adminsSitemap} is  place holder for an internationalized string.
            See WEB-INF/classes/locale/navigation/group/platform/administrators_en.properties
        -->
        <label>#{platform.administrators.adminsSitemap}</label>
        <page-reference>group::/platform/administrators::adminsSitemap</page-reference>
    </node>
</page-nodes>
</node-navigation>
The navigation node admins-sitemap refers to adminsSitemap page defined in the sibling pages.xml file:
<page-set xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.gatein.org/xml/ns/gatein_objects_1_3 http://www.gatein.org/xml/ns/gatein_objects_1_3"
 xmlns="http://www.gatein.org/xml/ns/gatein_objects_1_3">
<!-- 
    adminsSitemap page contains just SiteMapPortlet. There is nothing special about it. 
    It is here just to demonstrate the adding of nodes to group navigation in the sibling 
    navigation.xml file.
-->
<page>
    <name>adminsSitemap</name>
    <title>Admins' Sitemap</title>
    <access-permissions>*:/platform/administrators</access-permissions>
    <edit-permission>*:/platform/administrators</edit-permission>
    <portlet-application>
        <portlet>
            <application-ref>web</application-ref>
            <portlet-ref>SiteMapPortlet</portlet-ref>
        </portlet>
        <title>Sitemap</title>
        <access-permissions>*:/platform/administrators</access-permissions>
        <show-info-bar>false</show-info-bar>
    </portlet-application>
</page>
</page-set>

Example 14.6. Adding a Navigation Node to a User Navigation

To add a new navigation node visible to a particular user, for example root create a navigation.xml file located in war/src/main/webapp/WEB-INF/conf/sample-ext/portal/user/root/navigation.xml . Note that the name of the user root is a part of the path.
<node-navigation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.gatein.org/xml/ns/gatein_objects_1_3 http://www.gatein.org/xml/ns/gatein_objects_1_3" xmlns="http://www.gatein.org/xml/ns/gatein_objects_1_3">
<!-- Priority has no significance in user navigations. But it is required. See GTNPORTAL-2751 -->
<priority>1</priority>

<page-nodes>
    <node>
        <name>roots-extra-dashboard</name>
        <label>#{user.root.RootsExtraDashboard}</label>
        <page-reference>user::root::roots-dashboard</page-reference>
    </node>
</page-nodes>
</node-navigation>
The navigation node roots-extra-dashboard refers to roots-dashboard page defined in the sibling pages.xml file:
<page-set xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.gatein.org/xml/ns/gatein_objects_1_3 http://www.gatein.org/xml/ns/gatein_objects_1_3"
xmlns="http://www.gatein.org/xml/ns/gatein_objects_1_3">
<!-- 
    roots-dashboard page contains just DashboardPortlet. There is nothing special about it. 
    It is here just to demonstrate the adding of nodes to user navigation in the sibling 
    navigation.xml file.
-->
<page>
    <name>roots-dashboard</name>
    <title>Root's Extra Dashboard</title>
    <access-permissions>*:/platform/users</access-permissions>
    <edit-permission>*:/platform/administrators</edit-permission>
    <portlet-application>
        <portlet>
            <application-ref>dashboard</application-ref>
            <portlet-ref>DashboardPortlet</portlet-ref>
        </portlet>
        <title>Root's Extra Dashboard</title>
        <access-permissions>*:/platform/users</access-permissions>
        <show-info-bar>false</show-info-bar>
    </portlet-application>
</page>
</page-set>

14.6. Internationalization of Navigation Nodes

As you can see in the above navigation.xml files the labels of navigation nodes can be internationalized. The files containing translation resources live under war/src/main/webapp/WEB-INF/classes/locale/navigation . The directory layout is similar to the one used for navigation.xml and pages.xml files:
Note that the navigation resource bundles also need to be named in init.resources parameter of BaseResourceBundlePlugin in war/src/main/webapp/WEB-INF/conf/sample-ext/common/common-configuration.xml :

Example 14.7. common-configuration.xml containing declarations of navigation resource bundles

<configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.exoplaform.org/xml/ns/kernel_1_2.xsd http://www.exoplaform.org/xml/ns/kernel_1_2.xsd" xmlns="http://www.exoplaform.org/xml/ns/kernel_1_2.xsd">
<external-component-plugins>
    <!-- The full qualified name of the ResourceBundleService -->
    <target-component>org.exoplatform.services.resources.ResourceBundleService</target-component>
    <component-plugin>
        <!-- The name of the plugin -->
        <name>Sample ResourceBundle Plugin</name>
        <!-- The name of the method to call on the ResourceBundleService in order to register the ResourceBundles -->
        <set-method>addResourceBundle</set-method>
        <!-- The full qualified name of the BaseResourceBundlePlugin -->
        <type>org.exoplatform.services.resources.impl.BaseResourceBundlePlugin</type>
        <init-params>
            <values-param>
                <name>init.resources</name>
                <description>Initiate the following resources during the first launch.</description>
                <value>locale.portal.extension</value>
                <value>locale.navigation.user.root</value>
                <!-- 
                    Note that we actually do not need to name locale.navigation.portal.classic and 
                    locale.navigation.group.platform.administrators here as they are in init.resources 
                    of the default GateIn installation. But it makes no harm to include them once again here.
                -->
                <value>locale.navigation.portal.classic</value>
                <value>locale.navigation.group.platform.administrators</value>
           </values-param>
        </init-params>
    </component-plugin>

14.7. Custom Internationalization Resource Bundles

Note

This section mentions code from the Portal Extension Examples available for download as part of a valid Red Hat subscription.
There are two resource bundle customization scenarios possible with a Portal Extension:
  • to add new resource bundle items to the ones available in the default portal installation.
  • to assign new values to resource bundle items available in default portal installation.
Both scenarios can be demonstrated using the locale.portal.extension resource bundle. To use this bundle, include both init.resources and portal.resource.names parameters for the BaseResourceBundlePlugin<type> directive in war/src/main/webapp/WEB-INF/conf/sample-ext/common/common-configuration.xml.

Example 14.8. common-configuration.xml containing declarations of navigation resource bundles

<configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.exoplatform.org/xml/ns/kernel_1_2.xsd http://www.exoplatform.org/xml/ns/kernel_1_2.xsd" xmlns="http://www.exoplatform.org/xml/ns/kernel_1_2.xsd">
<external-component-plugins>
    <!-- The full qualified name of the ResourceBundleService -->
    <target-component>org.exoplatform.services.resources.ResourceBundleService</target-component>
    <component-plugin>
        <!-- The name of the plugin -->
        <name>Sample ResourceBundle Plugin</name>
        <!-- The name of the method to call on the ResourceBundleService in order to register the ResourceBundles -->
        <set-method>addResourceBundle</set-method>
        <!-- The full qualified name of the BaseResourceBundlePlugin -->
        <type>org.exoplatform.services.resources.impl.BaseResourceBundlePlugin</type>
        <init-params>
            <values-param>
                <name>init.resources</name>
                <description>Initiate the following resources during the first launch.</description>
                <value>locale.portal.extension</value>
                <value>locale.navigation.user.root</value>
                <!-- 
                    Note that we actually do not need to name locale.navigation.portal.classic and 
                    locale.navigation.group.platform.administrators here as they are in init.resources 
                    of the default GateIn installation. But it makes no harm to include them once again here.
                -->
                <value>locale.navigation.portal.classic</value>
                <value>locale.navigation.group.platform.administrators</value>
            </values-param>
            <values-param>
                <name>portal.resource.names</name>
                <description>These resources are merged to a single resource bundle which is accessible from anywhere 
                    in GateIn. All these keys are located in the same bundle, which is separated from the navigation 
                    resource bundles.</description>
                <value>locale.portal.extension</value>
            </values-param>
        </init-params>
    </component-plugin>
</external-component-plugins>
</configuration>
The English version of locale.portal.extension is located in war/src/main/webapp/WEB-INF/classes/locale/portal/extension_en.properties.

Example 14.9. English version of locale.portal.extension

UIHomePagePortlet.Label.Slogan=Congratulations!
UIHomePagePortlet.Label.SubSlogan=You have just installed the GateIn Portal Extension
UIHomePagePortlet.Label.Title=Sign in as:
UIHomePagePortlet.Label.SubSlogan is a new key which is not normally available in the default portal installation. However, UIHomePagePortlet.Label.Slogan is redefined in extension_en.properties shown above.
In $JPP_HOME/gatein/gatein.ear/web.war/WEB-INF/classes/locale/portlet/web/GroovyPortlet_en.properties it is already defined as:
UIHomePagePortlet.Label.Slogan=The Best of eXo and JBoss Portal<div>GateIn #{gatein.version}</div>
Within the Portal Extension, the new value Congratulations! is appended to it.

14.8. Custom Sign-in Page

It is possible to customize the sign-in page displayed to a user by configuring an extension.

Procedure 14.2. Creating a Custom Sign-in Page

  1. Copy $JPP_HOME/gatein/gatein.ear/portal.war/login/jsp/login.jsp.
  2. Create a custom extension, and paste the file into Custom_Extension.ear/Custom-WAR.war/login/jsp/login.jsp.

    Important

    All logic relating to the sign-in page must be contained within the login page. Failing to do this may cause the portal to operate incorrectly.

    Note

    The file path must be the same in both the primary path and the extension path for mirroring to work correctly.
  3. Edit the login.jsp file with the customization required for the extension.

Procedure 14.3. Creating a Custom Modal Window

  1. Copy $JPP_HOME/gatein/portal.war/groovy/portal/webui/UILoginForm.gtmpl.
  2. In the custom extension created in Procedure 14.2, “Creating a Custom Sign-in Page”, paste the file into Custom_Extension.ear/Custom-WAR.war/groovy/portal/webui/UILoginForm.gtmpl.

    Note

    The file path must be the same in both the primary path and the extension path for mirroring to work correctly.
  3. Edit the UILoginForm.gtmpl file with the customization required for the extension.
Once these modifications are made, deploy the custom extension and test the functionality of the sign in page.