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.
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.
- Copy
$JPP_HOME/gatein/gatein.ear/portal.war/templates/groovy/webui/component/UIHomePagePortlet.gtmpl - 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. - Make the desired changes to the text and layout of the HomePagePortlet in the custom extension.
- 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; }
See Also:
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” :
- 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.

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.
See Also:
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
- Copy
$JPP_HOME/gatein/gatein.ear/portal.war/login/jsp/login.jsp. - 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. - Edit the
login.jspfile with the customization required for the extension.
Procedure 14.3. Creating a Custom Modal Window
- Copy
$JPP_HOME/gatein/portal.war/groovy/portal/webui/UILoginForm.gtmpl. - 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. - Edit the
UILoginForm.gtmplfile 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.

