Chapter 4. Creating Your Own Portal

Create your extension project
Portal, pages and menus structure
Page layout
Visibility of pages
Customize your portal
Add/remove languages
Add a new language
Remove languages
How to create custom look and feel
How to structure the stylesheet?
How to configure you skin in Gatein?
How to configure your skin in WCM?

When working with eXo, it is important to not modify the source code. This will ensure compatibility with future upgrades, and will simplify support.

To customize your portal, you need to create an extension project by providing your own artifacts as a set of wars/jars/ears.

A custom extension contains two mandatory items:

A sample extension package is provided here: http://anonsvn.jboss.org/repos/gatein/portal/trunk/examples/extension/

Once you have modified the sample extension to build your own, use "maven clean install" to create the archive files.

To deploy your extension in Tomcat, follow these steps:

  • Add the file sample-ext.war from sample/extension/war/target/ to the tomcat/webapps directory.

  • Add the folder starter from starter/war/target/ to the tomcat/webapps directory.

  • Rename the directory (unzipped folder) starter to starter.war.

Note

This will only work if the starter.war is the last war file to be loaded, so you may need to rename it if your war files are loaded in alphabetical order.

  • Add the jar file exo.portal.sample.extension.config-X.Y.Z.jar from sample/extension/config/target/ to the tomcat/lib directory.

  • Add the jar file exo.portal.sample.extension.jar-X.Y.Z.jar from sample/extension/jar/target/ to the tomcat/lib directory.

For JBoss deployment and more details, refer to the Reference Guide.

You can create as many pages as you want within a single portal. Permissions can be defined to make them visible only to specific groups and/or users. This chapter describes how to define this structure.

The configuration of the "classic" portal can be found in the directory /src/main/webapp/WEB-INF/conf/sample-ext/portal/portal/classic of your extension webapp.

The portal.xml file describes the layout and portlets common to all the pages of the portal.

As you can see, each portlet can be configured with a set of preferences, which will be further detailed.

The pages.xml file is used to describe the content of the pages of your portal. In other words, what will be inside the <page-body> tag of the portal.xml file above. Here is an example of the classic portal pages.xml.

The navigation.xml is used to associate the links in your navigation (called page-node) with your portal pages.

If the pattern #{} is used then the label of the link will be loaded from the portal resource bundle (link to the ref guide about resource bundles

<?xml version="1.0" encoding="UTF-8"?>
<node-navigation>
<owner-type>portal</owner-type>
<owner-id>classic</owner-id>
<priority>1</priority>
<page-nodes>
<node>
<uri>home</uri>
<name>home</name>
<label>#{portal.classic.home}</label>
<page-reference>portal::classic::homepage</page-reference>
</node>
<node>
<uri>webexplorer</uri>
<name>webexplorer</name>
<label>#{portal.classic.webexplorer}</label>
<page-reference>portal::classic::webexplorer</page-reference>
</node>
</page-nodes>
</node-navigation>

This navigation tree can have multiple views inside portliest, such as breadcrumbs that render the current view node, the site map or the menu portlets.

Note

For top nodes, the URI and the navigation node name must have the same value. For the other nodes, the URI is composed like <uri>contentmanagement/fileexplorer</uri> where 'contentmanagement' is the name of the parent node and 'fileexplorer' the name of the node (<name>fileexplorer</name>).

One of the first steps in any web project is to integrate a graphic chart. This can be done entirely within your extension, by customizing the portal configuration.

In order to add a JavaScript library (for example JQuery), follow these steps:

For example:

All languages are put in the myextension.war/WEB-INF/conf/common/locales-config.xml directory. Information of each language consists of key, output-encoding, input-encoding, description and orientation. Different languages will be defined in corresponding resource bundle file with keys specified in the locale-config.xml file. All languages defined in the locale-config.xml file will be listed in the Interface Language Settings.

The complete skinning of a page can be decomposed into three main parts:

Portal Skin

The portal skin contains the styles for html tags (ex div,th,td...) and the portal UI (including the toobar). This should include all the UI components except for the window decorators and portlet specific styles.

Window Styles

The CSS styles associated with the porlet window decorators. The window decorators contain the control buttons and borders surrounding each portlet. Individual portlets can have their own window decorator selected, or be rendered without one.

Portlet Skins

The portlet skins effect how portlets are rendered on the page. There are two main ways this can be affected:

The portlet specification defines a set of css classes that should be available to portlets. Platform provides these classes as part of the portal skin. This allows each portal skin to define its own look and feel for these default values.

  • Portlet Skins

Platform provides a means for portlet CSS files to be loaded based on the current portal skin. This allows a portlet to provide different CSS styles to better match the current portal look and feel.

Note

The window decorators and the default portlet specification CSS classes should be considered separate types of skinning components, but they need to be included as part of the overall portal skin. The portal skin must include these component's CSS classes or they will not be displayed correctly. A portlet skin doesn't need to be included as part of the portal skin and can be included within the portlets web application.

The skin folder structure must be prepared as soon as you start the design. Follow these conventions and best practices to ease the integration of your design in Platform.

Portal skin The portal skin will appear as a single link to a CSS file. This link will contain contents from all the portal skin classes merged into one file. This allows the portal skin to be transfered more quickly as a single file instead of many smaller files included with every page render.

/webapp/skin/NameOfPortalSkin/portal

ie:

/webapp/skin/DefaultSkin/portal

The main entry CSS file hould be placed right in the main portal skin folder.The file is the main entry point to the CSS class definitions for the skin:

/webapp/skin/NameOfPortalSkin/Stylesheet.css

ie :

/webapp/skin/SkinBlue/Stylesheet.css

/webapp/skin/SkinBlue/webui/component/YourUIComponentName

ie:

/webapp/skin/SkinBlue/webui/component/UIToolbarContainer

webapp/skin/PortletThemes/Stylesheet.css

The images for portal skin should be put in the background foler right in the Portal skin folder and for each UI component.

ie:

/webapp/skin/SkinBlue/webui/component/UIProfileUser/SkinBlue/background

In summary, the folder structure for a new portal skin should be:

webapp

ie:

webapp

Portlet skin

Each portlet on a page may contribute its own style. The link to the portlet skin will only appear on the page if that portlet is loaded on the current page. A page may contain many portlet skin CSS links or none. The link ID will be named like . For example: ContentPortlet in content.war, will give id="contentContentPortlet"

/webapp/skin/portlet/webui/component/YourUIPortletName

and for groovy skin: /webapp/groovy/portlet/webui/component/YourUIPortletName/

ie:

/webapp/skin/portlet/webui/component/UIBannerPortlet

/webapp/groovy/portlet/webui/component/UIBannerPortlet

/webapp/skin/portlet/YourUIPortletName/PortalSkinName/background

ie:

/webapp/skin/portlet/UIBannerPortlet/BlueSkin/background

Main entry css:/webapp/skin/PortletThemes/Stylesheet.css

/webapp/skin/PortletThemes/background

/webapp/skin/PortletThemes/icons

GateIn 3.1 provides support for skinning the entire User Interface (UI) of portal, including all common portal elements, custom skins and window decorator for individual portlets. Skins are designed to help you pack and reuse common graphic resources.

The GateIn 3.1 skin not only contains CSS styles for the portal's components, but also shares components that may be reused in portlets. When GateIn 3.1 generates the page markup of portal, it inserts stylesheet links in the page's head tag. There are two main types of CSS links which appear in the head tag: one to the portal skin CSS file and the other to the portlet skin CSS file.

In the code fragment below, you can see two types of links:

SkinService in GateIn 3.1 is to manage various types of skins. It is used to discover and deploy skins into the portal.

The default skin of GateIn 3.1 is located as part of the eXoResource.war. The main files associated with the skin include:

The following block of CSS illustrates content of the Stylesheet.css file:

In which, (1) Skin of portal page. UIPortalApplicationSkin.css defines CSS classes shared by all the portal pages. (2) Skins of various portal-owned components, such as WorkingWorkspace, MaskWorkspace, PortalForm, and more. (3) Window decorator skins. (4) The portlet specification CSS classes. (The CSS styles defined in Portlet Specification JSR286)

To make a default skin flexible and highly reusable, instead of defining all CSS classes in this file, CSS classes are arranged in nested stylesheet files, based on the @import statement. This makes easier for new skins to reuse parts of the default skin. To reuse a CSS stylesheet from the default portal skin, you need to refer to the default skin from eXoResources. For example, to include the window decorators from the default skin within a new portal skin, you need to use the following import:

It is required to add a new skin to the portal through the skin service. Web applications containing the skin need to be properly configured for the skin service to discover them, meaning that the gatein-resources.xml must be configured correctly.

You can preview the appearance of portal skin when selecting it. To display the preview image of deployed skins, the current skin must be aware of all those icons. Hence, each skin must contain preview images of all other skins.

For any portal skin, the paths to preview images are specified in CSS class UIChangeSkinForm:

eXoResources/src/main/webapp/skin/DefaultSkin/portal/webui/component/customization/UIChangeSkinForm/Stylesheet.css

For the portal named MySkin, it is required to define the following CSS classes:

The default skin would be aware of skin icons if the preview screenshot is placed in: eXoResources.war:/skin/DefaultSkin/portal/webui/component/customization/UIChangeSkinForm/background.

The CSS stylesheet for the default portal needs to have the following updated with the preview icon CSS class. For the skin named MySkin, it is required to update the following: eXoResources.war:/skin/DefaultSkin/portal/webui/component/customization/UIChangeSkinForm/Stylesheet.css. For now, amending deployed package eXoResources is inevitable (modifying default war/jar breaches development convention of GateIn-based products). The problem would be resolved in future GateIn versions in which different skin modules are fully independent, for example, there will be no preview image duplication.

Window style is the CSS applied to the window decorator. When the administrator selects a new application to add to a page, he can decide which style of decorator surrounding the window if any.

Note

This section is related to the configuration. You can see a sample here. You can leave all the portlet's preferences as blank, that means the default value will be taken and you do not need to care about it at this time.

  • For example, you will have a layout like this:

In which:

  • Branding: A branding application

  • Top navigation: A top navigation application

A table column container with three nested containers:

  • Left Column and Right Column: contain one application for each.

  • Main content: contains the page body.

And here is the "short version" of portal.xml .

<myportalpath>/src/main/webapp/WEB-INF/conf/myportal/portal/portal/mysite/portal.xml

<!-- ... -->

  <portlet-application>
    <!-- Branding application. You can use WCM web content *exo:webContent* for the content and SCV portlet to display -->
  </portlet-application>

  <portlet-application>
    <!-- navigation application. You can use WCM web content *exo:webContent* for the content and SCV portlet to display -->
  </portlet-application>

  <container id="MySite" template="system:/groovy/portal/webui/container/UITableColumnContainer.gtmpl">
    <container id="LeftColumn" template="system:/groovy/portal/webui/container/UIContainer.gtmpl">
      <!-- One or more application(s) here -->
      <portlet-application>
      </portlet-application>
    </container>

    <container template="system:/groovy/portal/webui/container/UIContainer.gtmpl">
      <page-body>
      </page-body>
    </container>

    <container id="RightColumn" template="system:/groovy/portal/webui/container/UIContainer.gtmpl">
      <!-- One or more application(s) here -->
      <portlet-application>
      </portlet-application>
    </container>
  </container>

  <portlet-application>
    <!-- Footer application. You can use WCM web content *exo:webContent* for the content and SCV portlet to display -->
  </portlet-application>

<!-- ... -->

As you see in the portal.xml file above, every container tag has an id attribute, for example "<container id = 'RightColumn'>". When you create a CSS file, the property applied for this container should have the following name manner:

${container_id}TDContainer

and the details of this container:

RightColumnTDContainer

The reason is, when you have a look in the file system:/groovy/portal/webui/container/UITableColumnContainer.gtmpl_* shown above, you will see this code fragment:

 <table class="UITableColumnContainer" style="table-layout: fixed; margin: 0px auto; $style">
	<tr class="TRContainer">
		<% for(uiChild in uicomponent.getChildren()) {%>
			<td class="${uiChild.id}TDContainer TDContainer"><% uicomponent.renderUIComponent(uiChild) %></td>
		<% } %>
	</tr>
</table>

So, in the table element (which represents the outer container), there are many td elements, each of which has the class attribute that equals to the id of the corresponding child component plus the "TDContainer" string literal.

To learn more, see Customize portal and page's style.

Note

This section is related to the configuration. You can see a sample here. You can leave all the portlet's preferences as blank, that means the default value will be taken and you do not need to care about it at this time.

  • Like portal.xml , you can define the layout for each page in your site as shown in the following example:

<!-- ... -->

  <portlet-application>
    <!-- A custom document for content and SCV portlet to display -->
  </portlet-application>

  <portlet-application>
    <!-- A CLV portlet with a custom template. -->
  </portlet-application>

  <portlet-application>
    <!-- A CLV portlet with another custom template. -->
  </portlet-application>

<!-- ... -->
  • Apply your HTML/Groovy template code for this template. For example:

<myportalpath>/src/main/webapp/WEB-INF/conf/myportal/customized/template/list/ACustomizedCLVTemplate.gtmpl

<div id="$uicomponent.id" class="ACustomizedCLVTemplate">
  <div class="ListContents">
    <!-- something here -->
  </div>
</div>
  • Now, we need to import this template to the database.

<myportalpath>/src/main/webapp/WEB-INF/conf/myportal/customized/template/configuration.xml

  <external-component-plugins>
    <target-component>org.exoplatform.services.cms.views.ApplicationTemplateManagerService</target-component>
    <component-plugin>
      <name>ACustomizedCLVTemplate</name>
      <set-method>addPlugin</set-method>
      <type>org.exoplatform.services.cms.views.PortletTemplatePlugin</type>
      <description>This is a sample customized CLV template</description>
      <init-params>
        <value-param>
          <name>portletName</name>
          <value>Content List Viewer</value>
        </value-param>
        <value-param>
          <name>portlet.template.path</name>
          <value>war:/conf/myportal/customized/template</value>
        </value-param>
        <object-param>
          <name>default.folder.list.viewer</name>
          <description>Default folder list viewer groovy template</description>
          <object type="org.exoplatform.services.cms.views.PortletTemplatePlugin$PortletTemplateConfig">
            <field name="templateName">
              <string>ACustomizedCLVTemplate.gtmpl</string>
            </field>
            <field name="category">
              <string>list</string>
            </field>
          </object>
        </object-param>
      </init-params>
    </component-plugin>
  </external-component-plugins>

- First, you need to create a new document definition.

<myportalpath>/src/main/webapp/WEB-INF/conf/myportal/customized/document/ACustomizedDocument.xml

	node type name :exo:customizedDocument
	properties: exo:name(type : String), exo:title(type : String), exo:content(type : String)

You also need to configure it to make sure it is imported to the database.

<myportalpath>/src/main/webapp/WEB-INF/conf/myportal/customized/document/definition-configuration.xml

 
<external-component-plugins>
    <target-component>org.exoplatform.services.jcr.RepositoryService</target-component>
    <component-plugin>
      <name>ACustomizedDocument</name>
      <set-method>addPlugin</set-method>
      <type>org.exoplatform.services.jcr.impl.AddNodeTypePlugin</type>
      <priority>200</priority>
      <init-params>
        <values-param>
          <name>autoCreatedInNewRepository</name>
          <description>ACustomizedDocument document definition</description>
          <value>war:/conf/myportal/customized/document/ACustomizedDocument.xml</value>
        </values-param>
      </init-params>
    </component-plugin>
  </external-component-plugins>
  • Now it is time to create the templates for this document, including:

  • Dialog: see the sample here)

<myportalpath>/src/main/webapp/WEB-INF/conf/myportal/customized/document/dialog.gtmpl

<div class="UIForm ACustomizedDocument">
  <% uiform.begin() %>
    <!-- Document dialog content is here -->
  <% uiform.end() %>
  • View: see the sample here)

<myportalpath>/src/main/webapp/WEB-INF/conf/myportal/customized/document/view.gtmpl

 <style>
   <% _ctx.include(uicomponent.getTemplateSkin("exo:customizedDocument", "Stylesheet")); %>
</style>
<!-- Document view template content is here -->
  • Stylesheet: see the sample here)

Warning

This document should contain ONLY the stylesheet for THIS template.

<myportalpath>/src/main/webapp/WEB-INF/conf/myportal/customized/document/stylesheet.css

/* ... */

.ACustomizedDocument {
  /* ... */
}

/* ... */
  • You also need to import them to database.

<myportalpath>/src/main/webapp/WEB-INF/conf/myportal/customized/document/template-configuration.xml

<external-component-plugins>
    <target-component>org.exoplatform.services.cms.templates.TemplateService</target-component>
    <component-plugin>
      <name>addTemplates</name>
      <set-method>addTemplates</set-method>
      <type>org.exoplatform.services.cms.templates.impl.TemplatePlugin</type>
      <init-params>
        <value-param>
          <name>autoCreateInNewRepository</name>
          <value>true</value>
        </value-param>
        <value-param>
          <name>storedLocation</name>
          <value>war:/conf/myportal/customized/document</value>
        </value-param>
        <value-param>
          <name>repository</name>
          <value>repository</value>
        </value-param>
        <object-param>
          <name>template.configuration</name>
          <description>configuration for the localtion of nodetypes templates to inject in jcr</description>
          <object type="org.exoplatform.services.cms.templates.impl.TemplateConfig">
            <field name="nodeTypes">
              <collection type="java.util.ArrayList">
                <value>
                  <object type="org.exoplatform.services.cms.templates.impl.TemplateConfig$NodeType">
                    <field name="nodetypeName">
                      <string>exo:customizedDocument</string>
                    </field>
                    <field name="documentTemplate">
                      <boolean>true</boolean>
                    </field>
                    <field name="label">
                      <string>Customized Document</string>
                    </field>
                    <field name="referencedView">
                      <collection type="java.util.ArrayList">
                        <value>
                          <object type="org.exoplatform.services.cms.templates.impl.TemplateConfig$Template">
                            <field name="templateFile">
                              <string>view.gtmpl</string>
                            </field>
                            <field name="roles">
                              <string>*</string>
                            </field>
                          </object>
                        </value>
                      </collection>
                    </field>
                    <field name="referencedDialog">
                      <collection type="java.util.ArrayList">
                        <value>
                          <object type="org.exoplatform.services.cms.templates.impl.TemplateConfig$Template">
                            <field name="templateFile">
                              <string>dialog.gtmpl</string>
                            </field>
                            <field name="roles">
                              <string>webdesigner:/platform/web-contributors</string>
                            </field>
                          </object>
                        </value>
                      </collection>
                    </field>
		    <field name="referencedSkin">
                      <collection type="java.util.ArrayList">
                        <value>
                          <object type="org.exoplatform.services.cms.templates.impl.TemplateConfig$Template">
                            <field name="templateFile">
                              <string>stylesheet.css</string>
                            </field>
                            <field name="roles">
                              <string>*</string>
                            </field>
                          </object>
                        </value>
                      </collection>
                    </field>
                  </object>
                </value>
              </collection>
            </field>
          </object>
        </object-param>
      </init-params>
    </component-plugin>
  </external-component-plugins>
  • Finally, you should create some initial contents and export them to XML files.

To import this XML into database, you can set up for the deployment like this:

<external-component-plugins>
 <target-component>org.exoplatform.services.wcm.deployment.WCMContentInitializerService</target-component>    
 <component-plugin> 
  <name>Content Initializer Service</name>
  <set-method>addPlugin</set-method>
  <type>org.exoplatform.services.wcm.deployment.plugins.XMLDeploymentPlugin</type>
  <description>XML Deployment Plugin</description>
  <init-params>
   <object-param>        
    <name>ACME Logo data</name>
    <description>Deployment Descriptor</description>
    <object type="org.exoplatform.services.deployment.DeploymentDescriptor">
     <field name="target">
      <object type="org.exoplatform.services.deployment.DeploymentDescriptor$Target">
       <field name="repository"><string>repository</string></field>
       <field name="workspace"><string>collaboration</string></field>
       <field name="nodePath"><string>/sites content/live/acme/web contents/site artifacts</string></field>
      </object>
     </field>
     <field  name="sourcePath"><string>war:/conf/wcm/artifacts/site-resources/acme/Logo.xml</string></field>
    </object>
   </object-param>
  </init-params>  
 </component-plugin> 
</external-component-plugins>