JBoss.orgCommunity Documentation
This is a very rough documentation issued from the merge, content still has to be validated, we still hope that it can help the beta testers. Thanks !
is a merge of two mature projects; JBoss Portal and eXo Portal. It takes the best of both offerings and incorporates them into a single new project. The aim is to provide an intuitive portal for as-is use and a framework to build upon depending on your needs.
This book explains how to configure various aspects of , including navigation, skins and database variables.
Refer to the Installation Guide for information on downloading and installing the product or the User Guide for information on basic usage.
GateIn project homepage. GateIn 3.0 is the community project that is based on. http://www.gatein.org
GateIn downloads: www.jboss.org/gatein/downloads.html
Further documentation about the Red Hat and other Red Hat products can be found at www.redhat.com/docs
GateIn 3.0 uses the PicketLink IDM component to retain necessary identity information (user, groups, memberships, etc.). While legacy interfaces are still used (org.exoplatform.services.organization) for identity management, the wrapper implementation delegates to the PicketLink IDM framework.
This section doesn't provide information about PicketLink IDM and its configuration. Please refer to the appropriate project documentation (http://jboss.org/picketlink/IDM.html) for further information.
It is important to fully understand the concepts behind this framework design before changing the default configuration.
The identity model represented in 'org.exoplatform.services.organization' interfaces and the one used in PicketLink IDM have some major differences.
For example: the PicketLink IDM provides greater abstraction. It is possible for groups in the IDM framework to form memberships with many parents (which requires recursive ID translation) while the GateIn model allows only pure tree like membership structures.
Additionally the GateIn membership concept needs to be translated into the IDM Role concept. Therefore PicketLink IDM model is used in a limited way. All these translations are applied by the integration layer.
The main configuration file is idm-configuration:
<configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.exoplaform.org/xml/ns/kernel_1_0.xsd http://www.exoplaform.org/xml/ns/kernel_1_0.xsd"
xmlns="http://www.exoplaform.org/xml/ns/kernel_1_0.xsd">
ml_plain">
<component>
<key>org.exoplatform.services.organization.idm.PicketLinkIDMService</key>
<type>org.exoplatform.services.organization.idm.PicketLinkIDMServiceImpl</type>
<init-params>
<value-param>
<name>config</name>
<value>war:/conf/organization/idm-config.xml</value>
</value-param>
<value-param>
<name>portalRealm</name>
<value>realm${container.name.suffix}</value>
</value-param>
</init-params>
</component>
ml_plain"> <component>
<key>org.exoplatform.services.organization.OrganizationService</key>
<type>org.exoplatform.services.organization.idm.PicketLinkIDMOrganizationServiceImpl</type>
<init-params>
<object-param>
<name>configuration</name>
<object type="org.exoplatform.services.organization.idm.Config">
<field name="useParentIdAsGroupType">
<boolean>true</boolean>
</field>
<field name="forceMembershipOfMappedTypes">
<boolean>true</boolean>
</field>
<field name="pathSeparator">
<string>.</string>
</field>
<field name="rootGroupName">
<string>GTN_ROOT_GROUP</string>
</field>
<field name="groupTypeMappings">
<map type="java.util.HashMap">
<entry>
<key><string>/</string></key>
<value><string>root_type</string></value>
</entry>
<!-- Sample mapping -->
<!--
<entry>
<key><string>/platform/*</string></key>
<value><string>platform_type</string></value>
</entry>
<entry>
<key><string>/organization/*</string></key>
<value><string>organization_type</string></value>
</entry>
-->
</map>
</field>
<field name="associationMembershipType">
<string>member</string>
</field>
<field name="ignoreMappedMembershipType">
<boolean>false</boolean>
</field>
</object>
</object-param>
</init-params>
</component>
</configuration>
![]() | The org.exoplatform.services.organization.idm.PicketLinkIDMServiceImpl service has following options:
|
![]() | The org.exoplatform.services.organization.idm.PicketLinkIDMOrganizationServiceImpl key is a main entrypoint implementing org.exoplatform.services.organization.OrganizationService and is dependant on org.exoplatform.services.organization.idm.PicketLinkIDMService org.exoplatform.services.organization.idm.PicketLinkIDMOrganizationServiceImpl service has following options defined as fields of object-param type org.exoplatform.services.organization.idm.Config:
Additionally JBossIDMOrganizationServiceImpl uses those defaults to perform identity management operations
|
A sample PicketLink IDM configuration file is shown below. To understand all the options present in it please refer to the PicketLink IDM Reference Guide
<jboss-identity xmlns="urn:jboss:identity:idm:config:v1_0_beta" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:jboss:identity:idm:config:v1_0_alpha identity-config.xsd"> <realms> <realm> <id>PortalRealm</id> <repository-id-ref>PortalRepository</repository-id-ref> <identity-type-mappings> <user-mapping>USER</user-mapping> </identity-type-mappings> </realm> </realms> <repositories> <repository> <id>PortalRepository</id> <class>org.jboss.identity.idm.impl.repository.WrapperIdentityStoreRepository</class> <external-config/> <default-identity-store-id>HibernateStore</default-identity-store-id> <default-attribute-store-id>HibernateStore</default-attribute-store-id> </repository> </repositories> <stores> <attribute-stores/> <identity-stores> <identity-store> <id>HibernateStore</id> <class>org.jboss.identity.idm.impl.store.hibernate.HibernateIdentityStoreImpl</class> <external-config/> <supported-relationship-types> <relationship-type>JBOSS_IDENTITY_MEMBERSHIP</relationship-type> <relationship-type>JBOSS_IDENTITY_ROLE</relationship-type> </supported-relationship-types> <supported-identity-object-types> <identity-object-type> <name>USER</name> <relationships/> <credentials> <credential-type>PASSWORD</credential-type> </credentials> <attributes/> <options/> </identity-object-type> </supported-identity-object-types> <options> <option> <name>hibernateSessionFactoryRegistryName</name> <value>hibernateSessionFactory</value> </option> <option> <name>allowNotDefinedIdentityObjectTypes</name> <value>true</value> </option> <option> <name>populateRelationshipTypes</name> <value>true</value> </option> <option> <name>populateIdentityObjectTypes</name> <value>true</value> </option> <option> <name>allowNotDefinedAttributes</name> <value>true</value> </option> <option> <name>isRealmAware</name> <value>true</value> </option> </options> </identity-store> </identity-stores> </stores> </jboss-identity>
The default portal will be accessed if the user doesn't specify another portal. For example: http://hostname:port/portal/
. The default portal also be used at startup to determine if the tomcat database is empty or not.
The example configuration file below is found at: "02portal.war:/WEB-INF/conf/portal/portal-configuration.xml
".
<component>
<key>org.exoplatform.portal.config.UserPortalConfigService</key>
<type>org.exoplatform.portal.config.UserPortalConfigService</type>
<component-plugins>
<component-plugin>
<name>new.portal.config.user.listener</name>
<set-method>initListener</set-method>
<type>org.exoplatform.portal.config.NewPortalConfigListener</type>
<description>this listener init the portal configuration</description>
<init-params>
<value-param>
<name>default.portal</name>
<description>The default portal for checking db is empty or not</description>
<value>classic</value>
</value-param>
..........
</init-params>
</component-plugin>
</component-plugins>
</component>
In this example the classic portal has been set as the default. Note that the definition should be as a initial parameter of the NewPortalConfigListener component-plugin.
Three types of navigation are available to portal users:
These navigations are configured with standard XML syntax in the file; "02portal.war:/WEB-INF/conf/portal/portal-configuration.xml
".
<component>
<key>org.exoplatform.portal.config.UserPortalConfigService</key>
<type>org.exoplatform.portal.config.UserPortalConfigService</type>
<component-plugins>
<component-plugin>
<name>new.portal.config.user.listener</name>
<set-method>initListener</set-method>
<type>org.exoplatform.portal.config.NewPortalConfigListener</type>
<description>this listener init the portal configuration</description>
<init-params>
<value-param>
<name>default.portal</name>
<description>The default portal for checking db is empty or not</description>
<value>classic</value>
</value-param>
<object-param>
<name>portal.configuration</name>
<description>description</description>
<object type="org.exoplatform.portal.config.NewPortalConfig">
<field name="predefinedOwner">
<collection type="java.util.HashSet">
<value><string>classic</string></value>
<value><string>webos</string></value>
</collection>
</field>
<field name="ownerType"><string>portal</string></field>
<field name="templateLocation"><string>war:/conf/portal</string></field>
</object>
</object-param>
<object-param>
<name>group.configuration</name>
<description>description</description>
<object type="org.exoplatform.portal.config.NewPortalConfig">
<field name="predefinedOwner">
<collection type="java.util.HashSet">
<value><string>platform/administrators</string></value>
<value><string>platform/users</string></value>
<value><string>platform/guests</string></value>
<value><string>organization/management/executive-board</string></value>
</collection>
</field>
<field name="ownerType"><string>group</string></field>
<field name="templateLocation"><string>war:/conf/portal</string></field>
</object>
</object-param>
<object-param>
<name>user.configuration</name>
<description>description</description>
<object type="org.exoplatform.portal.config.NewPortalConfig">
<field name="predefinedOwner">
<collection type="java.util.HashSet">
<value><string>root</string></value>
<value><string>john</string></value>
<value><string>mary</string></value>
<value><string>demo</string></value>
</collection>
</field>
<field name="ownerType"><string>user</string></field>
<field name="templateLocation"><string>war:/conf/portal</string></field>
</object>
</object-param>
</init-params>
</component-plugin>
</component-plugins>
This XML file defines, for the three navigations, which sets of portals, groups or users will have XML files inside the apprpriate war
. Those files will be used to create an initial navigation the first time the portal is launched. That information will then be stored in the JCR and is then modifiable from the portal UI.
The portal navigation incorporates the pages that can be accessed when the user is not logged in (if the applicable permissions allow public access). For example; several portal navigations are used when a company has multiple trademarks and each trade has its own website.
The classic portal is configured by four XML files in the portal/WEBINF/conf/portal/portal/classic
directory:
This file describes the layout and portlets that will be shown on all pages. Usually the layout contains the banner, footer, menu and breadcrumbs portlets. is extremely configurable as every area (even the banner and footer) is a portlet.
<?xml version="1.0" encoding="ISO-8859-1"?>
<portal-config>
<portal-name>classic</portal-name>
<locale>en</locale>
<factory-id>office</factory-id>
<access-permissions>Everyone</access-permissions>
<edit-permission>*:/platform/administrators</edit-permission>
<creator>root</creator>
<portal-layout>
<application>
<instance-id>portal#classic:/web/BannerPortlet/banner</instance-id>
<show-info-bar>false</show-info-bar>
</application>
<application>
<instance-id>portal#classic:/web/NavigationPortlet/toolbar</instance-id>
<show-info-bar>false</show-info-bar>
</application>
<application>
<instance-id>portal#classic:/web/BreadcumbsPortlet/breadcumbs</instance-id>
<show-info-bar>false</show-info-bar>
</application>
<page-body> </page-body>
<application>
<instance-id>portal#classic:/web/FooterPortlet/footer</instance-id>
<show-info-bar>false</show-info-bar>
</application>
</portal-layout>
</portal-config>
It is also possible to apply a nested container that can also contain portlets. Row, column or tab containers are then responsible of the layout of their child portlets.
Each application references a portlet using the id portal#{portalName}:/{portletWarName}/{portletName}/{uniqueId}
Use the page-body
tag to define where should render the current page.
The defined classic portal is accessible to "Everyone" (at /portal/public/classic
) but only members of the group /platform/administrators
can edit it.
This file defines all the navigation nodes the portal will have. The syntax is simple, using nested node tags. Each node references a page that is defined in the next XML file.
If the label #{}
node label is used, the internationalization mechanism is activated and the actual label to render is taken from an associated properties file for the current locale.
<?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 portlets (such as the breadcrumbs portlet) that render the current view node, the site map or the menu portlets.
For top nodes, the uri and the name of your navigation nodes must have the same value. For the other nodes the uri is composed like contentmanagement/fileexplorer
where 'contentmanagement
' is the name of the parent node and 'fileexplorer
' is the name of the node ( <name>fileexplorer</name> ).
This XML file structure is very similar to portal.xml
and it can also contain container tags. Each application can decide whether to render the portlet border, the window state icons or the mode.
<?xml version="1.0" encoding="ISO-8859-1"?>
<page-set>
<page>
<page-id>portal::classic::homepage</page-id>
<owner-type>portal</owner-type>
<owner-id>classic</owner-id>
<name>homepage</name>
<title>Home Page</title>
<access-permissions>Everyone</access-permissions>
<edit-permission>*:/platform/administrators</edit-permission>
<application>
<instance-id>portal#classic:/web/HomePagePortlet/homepageportlet</instance-id>
<title>Home Page portlet</title>
<show-info-bar>false</show-info-bar>
<show-application-state>false</show-application-state>
<show-application-mode>false</show-application-mode>
</application>
</page>
<page>
<page-id>portal::classic::webexplorer</page-id>
<owner-type>portal</owner-type>
<owner-id>classic</owner-id>
<name>webexplorer</name>
<title>Web Explorer</title>
<access-permissions>*:/platform/users</access-permissions>
<edit-permission>*:/platform/administrators</edit-permission>
<application>
<instance-id>group#platform/users:/web/BrowserPortlet/WebExplorer</instance-id>
<title>Web Explorer</title>
<show-info-bar>false</show-info-bar>
</application>
</page>
</page-set>
Porlet instances can be associated with portlet-preferences
that override the one defined in portlet.xml
file of the portlet application war
.
<?xml version="1.0" encoding="ISO-8859-1"?>
<portlet-preferences-set>
<portlet-preferences>
<owner-type>portal</owner-type>
<owner-id>classic</owner-id>
<window-id>portal#classic:/web/BannerPortlet/banner</window-id>
<preference>
<name>template</name>
<value>par:/groovy/groovy/webui/component/UIBannerPortlet.gtmpl</value>
<read-only>false</read-only>
</preference>
</portlet-preferences>
<portlet-preferences>
<owner-type>portal</owner-type>
<owner-id>classic</owner-id>
<window-id>portal#classic:/web/NavigationPortlet/toolbar</window-id>
<preference>
<name>useAJAX</name>
<value>true</value>
<read-only>false</read-only>
</preference>
</portlet-preferences>
<portlet-preferences>
<owner-type>portal</owner-type>
<owner-id>classic</owner-id>
<window-id>portal#classic:/web/FooterPortlet/footer</window-id>
<preference>
<name>template</name>
<value>par:/groovy/groovy/webui/component/UIFooterPortlet.gtmpl</value>
<read-only>false</read-only>
</preference>
</portlet-preferences>
<portlet-preferences>
<owner-type>portal</owner-type>
<owner-id>classic</owner-id>
<window-id>portal#classic:/web/GroovyPortlet/groovyportlet</window-id>
<preference>
<name>template</name>
<value>par:/groovy/groovy/webui/component/UIGroovyPortlet.gtmpl</value>
<read-only>false</read-only>
</preference>
</portlet-preferences>
</portlet-preferences-set>
Group navigations are dynamically added to the user navigation at login. This allows users to see all the pages assigned to any groups they belong to in the menu.
The group navigation menu is configured by three XML files (navigation.xml
, pages.xml
and portlet-preferences.xml
). The syntax used in these files is the same as those covered in Section 2.3.2, “Portal Navigation”.
They are located in the portal/WEB-INF/conf/portal/group
directory (For example; /group-name-path/
portal/WEB-INF/conf/portal/group/platform/administrators/
).
The user navigation is the set of nodes and pages that are owned by a user. They are part of the user dashboard.
Three files configure the user navigation (navigation.xml
, pages.xml
and portlet-preferences.xml
). They are located in the directory "portal/WEB-INF/conf/portal/users/{userName}
".
This directory also contains a gadgets.xml
file (which was formerly called widgets.xml
). This file defines the gadgets located in the user workspace.
The user workspace is located at the left hand side of the page and access is restricted to some privileged users, see Section 2.4, “Predefined User Configuration”
<?xml version="1.0" encoding="ISO-8859-1"?>
<widgets>
<owner-type>user</owner-type>
<owner-id>root</owner-id>
<container id="Information">
<name>Information</name>
<description>Information's Description</description>
<application>
<instance-id>user#root:/GateInWidgetWeb/WelcomeWidget/WelcomeWidget1</instance-id>
<application-type>GateInWidget</application-type>
</application>
<application>
<instance-id>user#root:/GateInWidgetWeb/StickerWidget/StickerWidget</instance-id>
<application-type>GateInWidget</application-type>
</application>
<application>
<instance-id>user#root:/GateInWidgetWeb/InfoWidget/InfoWidget1</instance-id>
<application-type>GateInWidget</application-type>
</application>
</container>
<container id="Calendar">
<name>Calendar</name>
<description>Calendar's Description</description>
<application>
<instance-id>user#root:/GateInWidgetWeb/CalendarWidget/CalendarWidget</instance-id>
<application-type>GateInWidget</application-type>
</application>
</container>
</widgets>
If you wish to add a link to a URL outside the portal you you first have to define a "page" which will not be used. Then add the URL to the navigation.
<page> <owner-type>portal</owner-type> <owner-id>website</owner-id> <name>documentation</name> <title>Documentation</title> <access-permissions>Everyone</access-permissions> <edit-permission>*:/platform/administrators</edit-permission> </page>
<node> <uri>http://wiki.exoplatform.com/xwiki/bin/view/Main/WebHome</uri> <name>documentation</name> <label>#{portal.classic.documentation}</label> <page-reference>portal::website::documentation</page-reference> </node>
Currently you cannot modify the URL using the portal interface, you must change it in the configuration files or modify the underlying database table.
To specify the initial Organization configuration, the content of 02portal.war:/WEB-INF/conf/organization/organization-configuration.xml
should be edited. This file uses the portal XML configuration schema. It lists several configuration plugins.
The plugin of type org.exoplatform.services.organization.OrganizationDatabaseInitializer
specifies the list of users, groups and membership types to be created.
The initialization parameter "checkDatabaseAlgorithm
" determines how the creation is triggered.
The value "entry
" means that each user, group and membership listed in the configuration file is checked each time is started. If the entry doesn't exist, it is created. The value "empty
" sets the configuration file to be processed only if the database is empty.
The predefined membership types are specified in the "membershipType
" field of the "OrganizationConfig
" plugin parameter.
<field name="membershipType">
<collection type="java.util.ArrayList">
<value>
<object type="org.exoplatform.services.organization.OrganizationConfig$MembershipType">
<field name="type">
<string>member</string>
</field>
<field name="description">
<string>member membership type</string>
</field>
</object>
</value>
<value>
<object type="org.exoplatform.services.organization.OrganizationConfig$MembershipType">
<field name="type">
<string>owner</string>
</field>
<field name="description">
<string>owner membership type</string>
</field>
</object>
</value>
<value>
<object type="org.exoplatform.services.organization.OrganizationConfig$MembershipType">
<field name="type">
<string>validator</string>
</field>
<field name="description">
<string>validator membership type</string>
</field>
</object>
</value>
</collection>
</field>
The predefined groups are specified in the "group" field of the "OrganizationConfig
" plugin parameter.
<field name="group">
<collection type="java.util.ArrayList">
<value>
<object type="org.exoplatform.services.organization.OrganizationConfig$Group">
<field name="name">
<string>portal</string>
</field>
<field name="parentId">
<string></string>
</field>
<field name="type">
<string>hierachy</string>
</field>
<field name="description">
<string>the /portal group</string>
</field>
</object>
</value>
<value>
<object type="org.exoplatform.services.organization.OrganizationConfig$Group">
<field name="name">
<string>community</string>
</field>
<field name="parentId">
<string>/portal</string>
</field>
<field name="type">
<string>hierachy</string>
</field>
<field name="description">
<string>the /portal/community group</string>
</field>
</object>
</value>
...
</collection>
</field>
The predefined users are specified in the "membershipType
" field of the "OrganizationConfig
" plugin parameter.
<field name="user">
<collection type="java.util.ArrayList">
<value>
<object type="org.exoplatform.services.organization.OrganizationConfig$User">
<field name="userName"><string>root</string></field>
<field name="password"><string>exo</string></field>
<field name="firstName"><string>root</string></field>
<field name="lastName"><string>root</string></field>
<field name="email"><string>exoadmin@localhost</string></field>
<field name="groups"><string>member:/admin,member:/user,owner:/portal/admin</string></field>
</object>
</value>
<value>
<object type="org.exoplatform.services.organization.OrganizationConfig$User">
<field name="userName"><string>exo</string></field>
<field name="password"><string>exo</string></field>
<field name="firstName"><string>site</string></field>
<field name="lastName"><string>site</string></field>
<field name="email"><string>exo@localhost</string></field>
<field name="groups"><string>member:/user</string></field>
</object>
</value>
...
</collection>
</field>
The plugin of type org.exoplatform.services.organization.impl.NewUserEventListener
specifies which groups should join all newly created users. It notably specifies the groups and memberships to be used. It also specifies a list of users that should be excepted.
<component-plugin>
<name>new.user.event.listener</name>
<set-method>addListenerPlugin</set-method>
<type>org.exoplatform.services.organization.impl.NewUserEventListener</type>
<description>this listener assign group and membership to a new created user</description>
<init-params>
<object-param>
<name>configuration</name>
<description>description</description>
<object type="org.exoplatform.services.organization.impl.NewUserConfig">
<field name="group">
<collection type="java.util.ArrayList">
<value>
<object type="org.exoplatform.services.organization.impl.NewUserConfig$JoinGroup">
<field name="groupId"><string>/user</string></field>
<field name="membership"><string>member</string></field>
</object>
</value>
</collection>
</field>
<field name="ignoredUser">
<collection type="java.util.HashSet">
<value><string>exo</string></value>
<value><string>root</string></value>
<value><string>company</string></value>
<value><string>community</string></value>
</collection>
</field>
</object>
</object-param>
</init-params>
</component-plugin>
The permission configuration for the portal is defined in the file 02portal.war:/WEB-INF/conf/portal/portal-configuration.xml
. The component UserACL is described there along with other portal component configurations.
It defines 5 permissions types:
The super user has all the rights on the platform, this user is referred to as root.
This list defines all groups that will be able to manage the different portals. Members of this group also have the permission to create new portals. The format is "membership:/group/subgroup
".
Defines the membership type of the group managers. The group managers have the permission to create and edit group pages and they can modify the group navigation.
Any anonymous user automatically becomes a member of this group whent they enter the public pages.
Defines the users that have access to the control workspace. In the demo version the control workspace is accessible only to 'root' and 'john'. They can expand/collapse the workspace at the left hand side. The format is "membership:/group/subgroup", An asterisk '' gives permission to all memberships.
<component>
<key>org.exoplatform.portal.config.UserACL</key>
<type>org.exoplatform.portal.config.UserACL</type>
<init-params>
<value-param>
<name>super.user</name>
<description>administrator</description>
<value>root</value>
</value-param>
<value-param>
<name>portal.creator.groups</name>
<description>groups with membership type have permission to manage portal</description>
<value>*:/platform/administrators,*:/organization/management/executive-board</value>
</value-param>
<value-param>
<name>navigation.creator.membership.type</name>
<description>specific membership type have full permission with group navigation</description>
<value>manager</value>
</value-param>
<value-param>
<name>guests.group</name>
<description>guests group</description>
<value>/platform/guests</value>
</value-param>
<value-param>
<name>access.control.workspace</name>
<description>groups with memberships that have the right to access the User Control Workspace</description>
<value>*:/platform/administrators,*:/organization/management/executive-board</value>
</value-param>
</init-params>
</component>
<external-component-plugins>
<target-component>org.exoplatform.portal.config.UserACL</target-component>
<component-plugin>
<name>addPortalACLPlugin</name>
<set-method>addPortalACLPlugin</set-method>
<type>org.exoplatform.portal.config.PortalACLPlugin</type>
<description>setting some permission for portal</description>
<init-params>
<values-param>
<name>access.control.workspace.roles</name>
<value>*:/platform/administrators</value>
<value>*:/organization/management/executive-board</value>
</values-param>
<values-param>
<name>portal.creation.roles</name>
<value>*:/platform/administrators</value>
<value>*:/organization/management/executive-board</value>
</values-param>
</init-params>
</component-plugin>
</external-component-plugins>
GateIn 3.0 has two different database dependencies. One is the identity service configuration, which depends on the Hibernate. The other database dependency is Java content repository (JCR) service, which depends on the native JDBC API and it can integrate with any existing datasource implementation.
When you change the database configuration for the first time, GateIn will automatically generate the proper schema (assuming that the database user has the appropriate permissions).
GateIn assumes the default encoding for your database is
latin1
. You will need to change this parameter for
your database in order to work properly.
To configure the databaseused by JCR you will need to edit the file:
gatein.ear/02portal.war/WEB-INF/conf/jcr/jcr-configuration.xml
And edit the values of driverClassName, url, username and password with the values for your JDBC connection (Please refer to your database JDBC driver documentation).
<properties-param>
<name>ref-addresses</name>
<description>ref-addresses</description>
<property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
<property name="url" value="jdbc:hsqldb:file:${gatein.db.data.dir}/data/jdbcjcr${container.name.suffix}"/>
property name="username" value="sa"/>
<property name="password" value=""/>
</properties-param>
In that case, the name of the database is "jdbcjcr${container.name.suffix}", ${container.name.suffix} should be part of the database name, as it is dynamically replaced by the name of the portal extension (for instance gatein-sample-portal.ear defines "sample-portal" as container name and the default portal defines "portal" as container name).
In the case of HSQL the databases are created automatically, for any other database you will need to create a database named jdbcjcr_portal (and "jdbcjcr_sample-portal" if you kept gatein-sample-portal.ear in $JBOSS_HOME/server/default/deploy. Note that some database wont accept '-' in a database name and you will have to delete $JBOSS_HOME/server/default/deploy/gatein-sample-portal.ear)
Make sure the user has rights to create tables on jdbcjcr_portal and to update them as during the first startup they will be automatically created.
Also add the JDBC driver into the classpath, for instance in $JBOSS_HOME/server/default/lib (or $TOMCAT_HOME/lib if you are running on Tomcat)
MySQL example:
Let's configure our JCR to store data in MySQL, let's pretend we have a user named "gateinuser" with a password "gateinpassword". We would create a database "mygateindb_portal" (Remember that _portal is required) and assign him the rights to create tables.
Then we need to add the MySQL JDBC connector in the classpath and finally edit gatein.ear/02portal.war/WEB-INF/conf/jcr/jcr-configuration with:
<properties-param> <name>ref-addresses</name> <description>ref-addresses</description> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mygateindb${container.name.suffix}"/> <property name="username" value="gateinuser"/> <property name="password" value="gateinpassword"/> </properties-param>
By default users are stored in database. To change the database to store users, you will need to edit the file:
gatein.ear/02portal.war/WEB-INF/conf/organization/idm-configuration.xml
You will find the same configuration as in jcr-configuration.xml:
<properties-param> <name>ref-addresses</name> <description>ref-addresses</description> <property name="driverClassName" value="org.hsqldb.jdbcDriver"/> <property name="url" value="jdbc:hsqldb:file:${gatein.db.data.dir}/data/jdbcidm${container.name.suffix}"/> property name="username" value="sa"/> <property name="password" value=""/> </properties-param>
It is recommended to use a different database than the one used by JCR.
Data-injector is a utility to initialize enterprise data for . It is packed as a .jar.
OrganizationInitializer is the service that allows creating a large organization with many groups and users. It also creates portal navigation and page(s) for each group and each user.
<!-- XML : generated by JHighlight v1.0 (http://jhighlight.dev.java.net) --> <span class="xml_tag_symbols"><</span><span class="xml_tag_name">configuration</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"><</span><span class="xml_tag_name">component</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"><</span><span class="xml_tag_name">key</span><span class="xml_tag_symbols">></span><span class="xml_plain">org.exoplatform.portal.initializer.organization.OrganizationInitializer</span><span class="xml_tag_symbols"></</span><span class="xml_tag_name">key</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"><</span><span class="xml_tag_name">type</span><span class="xml_tag_symbols">></span><span class="xml_plain">org.exoplatform.portal.initializer.organization.OrganizationInitializer</span><span class="xml_tag_symbols"></</span><span class="xml_tag_name">type</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"><</span><span class="xml_tag_name">init-params</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"><</span><span class="xml_tag_name">value-param</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"><</span><span class="xml_tag_name">name</span><span class="xml_tag_symbols">></span><span class="xml_plain">auto.create.group.page.navigation</span><span class="xml_tag_symbols"></</span><span class="xml_tag_name">name</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"><</span><span class="xml_tag_name">description</span><span class="xml_tag_symbols">></span><span class="xml_plain">true or false</span><span class="xml_tag_symbols"></</span><span class="xml_tag_name">description</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"><</span><span class="xml_tag_name">value</span><span class="xml_tag_symbols">></span><span class="xml_plain">true</span><span class="xml_tag_symbols"></</span><span class="xml_tag_name">value</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"></</span><span class="xml_tag_name">value-param</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"><</span><span class="xml_tag_name">value-param</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"><</span><span class="xml_tag_name">name</span><span class="xml_tag_symbols">></span><span class="xml_plain">auto.create.user.page.navigation</span><span class="xml_tag_symbols"></</span><span class="xml_tag_name">name</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"><</span><span class="xml_tag_name">description</span><span class="xml_tag_symbols">></span><span class="xml_plain">number of pages per user</span><span class="xml_tag_symbols"></</span><span class="xml_tag_name">description</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"><</span><span class="xml_tag_name">value</span><span class="xml_tag_symbols">></span><span class="xml_plain">10</span><span class="xml_tag_symbols"></</span><span class="xml_tag_name">value</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"></</span><span class="xml_tag_name">value-param</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"><</span><span class="xml_tag_name">object-param</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"><</span><span class="xml_tag_name">name</span><span class="xml_tag_symbols">></span><span class="xml_plain">organization</span><span class="xml_tag_symbols"></</span><span class="xml_tag_name">name</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"><</span><span class="xml_tag_name">description</span><span class="xml_tag_symbols">></span><span class="xml_plain">description</span><span class="xml_tag_symbols"></</span><span class="xml_tag_name">description</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"><</span><span class="xml_tag_name">object</span><span class="xml_plain"> </span><span class="xml_attribute_name">type</span><span class="xml_tag_symbols">=</span><span class="xml_attribute_value">"org.exoplatform.portal.initializer.organization.OrganizationConfig"</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"><</span><span class="xml_tag_name">field</span><span class="xml_plain"> </span><span class="xml_attribute_name">name</span><span class="xml_tag_symbols">=</span><span class="xml_attribute_value">"groups"</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"><</span><span class="xml_tag_name">collection</span><span class="xml_plain"> </span><span class="xml_attribute_name">type</span><span class="xml_tag_symbols">=</span><span class="xml_attribute_value">"java.util.ArrayList"</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"><</span><span class="xml_tag_name">value</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"><</span><span class="xml_tag_name">object</span><span class="xml_plain"> </span><span class="xml_attribute_name">type</span><span class="xml_tag_symbols">=</span><span class="xml_attribute_value">"org.exoplatform.portal.initializer.organization.OrganizationConfig$GroupsConfig"</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"><</span><span class="xml_tag_name">field</span><span class="xml_plain"> </span><span class="xml_attribute_name">name</span><span class="xml_tag_symbols">=</span><span class="xml_attribute_value">"group"</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"><</span><span class="xml_tag_name">object</span><span class="xml_plain"> </span><span class="xml_attribute_name">type</span><span class="xml_tag_symbols">=</span><span class="xml_attribute_value">"org.exoplatform.services.organization.OrganizationConfig$Group"</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"><</span><span class="xml_tag_name">field</span><span class="xml_plain"> </span><span class="xml_attribute_name">name</span><span class="xml_tag_symbols">=</span><span class="xml_attribute_value">"name"</span><span class="xml_tag_symbols">><</span><span class="xml_tag_name">string</span><span class="xml_tag_symbols">></span><span class="xml_plain">province</span><span class="xml_tag_symbols"></</span><span class="xml_tag_name">string</span><span class="xml_tag_symbols">></</span><span class="xml_tag_name">field</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"><</span><span class="xml_tag_name">field</span><span class="xml_plain"> </span><span class="xml_attribute_name">name</span><span class="xml_tag_symbols">=</span><span class="xml_attribute_value">"parentId"</span><span class="xml_tag_symbols">><</span><span class="xml_tag_name">string</span><span class="xml_tag_symbols">></span><span class="xml_plain">/africa/tanzania</span><span class="xml_tag_symbols"></</span><span class="xml_tag_name">string</span><span class="xml_tag_symbols">></</span><span class="xml_tag_name">field</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"><</span><span class="xml_tag_name">field</span><span class="xml_plain"> </span><span class="xml_attribute_name">name</span><span class="xml_tag_symbols">=</span><span class="xml_attribute_value">"description"</span><span class="xml_tag_symbols">><</span><span class="xml_tag_name">string</span><span class="xml_tag_symbols">></span><span class="xml_plain">Tanzania's province</span><span class="xml_tag_symbols"></</span><span class="xml_tag_name">string</span><span class="xml_tag_symbols">></</span><span class="xml_tag_name">field</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"><</span><span class="xml_tag_name">field</span><span class="xml_plain"> </span><span class="xml_attribute_name">name</span><span class="xml_tag_symbols">=</span><span class="xml_attribute_value">"label"</span><span class="xml_tag_symbols">><</span><span class="xml_tag_name">string</span><span class="xml_tag_symbols">></span><span class="xml_plain">Province</span><span class="xml_tag_symbols"></</span><span class="xml_tag_name">string</span><span class="xml_tag_symbols">></</span><span class="xml_tag_name">field</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"></</span><span class="xml_tag_name">object</span><span class="xml_tag_symbols">></span><span class="xml_plain"> </span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"></</span><span class="xml_tag_name">field</span><span class="xml_tag_symbols">></span><span class="xml_plain"> </span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"><</span><span class="xml_tag_name">field</span><span class="xml_plain"> </span><span class="xml_attribute_name">name</span><span class="xml_tag_symbols">=</span><span class="xml_attribute_value">"from"</span><span class="xml_tag_symbols">><</span><span class="xml_tag_name">string</span><span class="xml_tag_symbols">></span><span class="xml_plain">1</span><span class="xml_tag_symbols"></</span><span class="xml_tag_name">string</span><span class="xml_tag_symbols">></</span><span class="xml_tag_name">field</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"><</span><span class="xml_tag_name">field</span><span class="xml_plain"> </span><span class="xml_attribute_name">name</span><span class="xml_tag_symbols">=</span><span class="xml_attribute_value">"to"</span><span class="xml_tag_symbols">><</span><span class="xml_tag_name">string</span><span class="xml_tag_symbols">></span><span class="xml_plain">10</span><span class="xml_tag_symbols"></</span><span class="xml_tag_name">string</span><span class="xml_tag_symbols">></</span><span class="xml_tag_name">field</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"></</span><span class="xml_tag_name">object</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"></</span><span class="xml_tag_name">value</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"></</span><span class="xml_tag_name">collection</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"></</span><span class="xml_tag_name">field</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"><</span><span class="xml_tag_name">field</span><span class="xml_plain"> </span><span class="xml_attribute_name">name</span><span class="xml_tag_symbols">=</span><span class="xml_attribute_value">"users"</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"><</span><span class="xml_tag_name">collection</span><span class="xml_plain"> </span><span class="xml_attribute_name">type</span><span class="xml_tag_symbols">=</span><span class="xml_attribute_value">"java.util.ArrayList"</span><span class="xml_tag_symbols">></span><span class="xml_plain"> </span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"><</span><span class="xml_tag_name">value</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"><</span><span class="xml_tag_name">object</span><span class="xml_plain"> </span><span class="xml_attribute_name">type</span><span class="xml_tag_symbols">=</span><span class="xml_attribute_value">"org.exoplatform.portal.initializer.organization.OrganizationConfig$UsersConfig"</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"><</span><span class="xml_tag_name">field</span><span class="xml_plain"> </span><span class="xml_attribute_name">name</span><span class="xml_tag_symbols">=</span><span class="xml_attribute_value">"user"</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"><</span><span class="xml_tag_name">object</span><span class="xml_plain"> </span><span class="xml_attribute_name">type</span><span class="xml_tag_symbols">=</span><span class="xml_attribute_value">"org.exoplatform.services.organization.OrganizationConfig$User"</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"><</span><span class="xml_tag_name">field</span><span class="xml_plain"> </span><span class="xml_attribute_name">name</span><span class="xml_tag_symbols">=</span><span class="xml_attribute_value">"userName"</span><span class="xml_tag_symbols">><</span><span class="xml_tag_name">string</span><span class="xml_tag_symbols">></span><span class="xml_plain">user</span><span class="xml_tag_symbols"></</span><span class="xml_tag_name">string</span><span class="xml_tag_symbols">></</span><span class="xml_tag_name">field</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"><</span><span class="xml_tag_name">field</span><span class="xml_plain"> </span><span class="xml_attribute_name">name</span><span class="xml_tag_symbols">=</span><span class="xml_attribute_value">"password"</span><span class="xml_tag_symbols">><</span><span class="xml_tag_name">string</span><span class="xml_tag_symbols">></span><span class="xml_plain">GateInPlatform</span><span class="xml_tag_symbols"></</span><span class="xml_tag_name">string</span><span class="xml_tag_symbols">></</span><span class="xml_tag_name">field</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"><</span><span class="xml_tag_name">field</span><span class="xml_plain"> </span><span class="xml_attribute_name">name</span><span class="xml_tag_symbols">=</span><span class="xml_attribute_value">"firstName"</span><span class="xml_tag_symbols">><</span><span class="xml_tag_name">string</span><span class="xml_tag_symbols">></span><span class="xml_plain">First-Name</span><span class="xml_tag_symbols"></</span><span class="xml_tag_name">string</span><span class="xml_tag_symbols">></</span><span class="xml_tag_name">field</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"><</span><span class="xml_tag_name">field</span><span class="xml_plain"> </span><span class="xml_attribute_name">name</span><span class="xml_tag_symbols">=</span><span class="xml_attribute_value">"lastName"</span><span class="xml_tag_symbols">><</span><span class="xml_tag_name">string</span><span class="xml_tag_symbols">></span><span class="xml_plain">Last-Name</span><span class="xml_tag_symbols"></</span><span class="xml_tag_name">string</span><span class="xml_tag_symbols">></</span><span class="xml_tag_name">field</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"><</span><span class="xml_tag_name">field</span><span class="xml_plain"> </span><span class="xml_attribute_name">name</span><span class="xml_tag_symbols">=</span><span class="xml_attribute_value">"email"</span><span class="xml_tag_symbols">><</span><span class="xml_tag_name">string</span><span class="xml_tag_symbols">></span><span class="xml_plain">exo@localhost</span><span class="xml_tag_symbols"></</span><span class="xml_tag_name">string</span><span class="xml_tag_symbols">></</span><span class="xml_tag_name">field</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"><</span><span class="xml_tag_name">field</span><span class="xml_plain"> </span><span class="xml_attribute_name">name</span><span class="xml_tag_symbols">=</span><span class="xml_attribute_value">"groups"</span><span class="xml_tag_symbols">><</span><span class="xml_tag_name">string</span><span class="xml_tag_symbols">></span><span class="xml_plain">member:/africa</span><span class="xml_tag_symbols"></</span><span class="xml_tag_name">string</span><span class="xml_tag_symbols">></</span><span class="xml_tag_name">field</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"></</span><span class="xml_tag_name">object</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"></</span><span class="xml_tag_name">field</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"><</span><span class="xml_tag_name">field</span><span class="xml_plain"> </span><span class="xml_attribute_name">name</span><span class="xml_tag_symbols">=</span><span class="xml_attribute_value">"from"</span><span class="xml_tag_symbols">><</span><span class="xml_tag_name">string</span><span class="xml_tag_symbols">></span><span class="xml_plain">0</span><span class="xml_tag_symbols"></</span><span class="xml_tag_name">string</span><span class="xml_tag_symbols">></</span><span class="xml_tag_name">field</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"><</span><span class="xml_tag_name">field</span><span class="xml_plain"> </span><span class="xml_attribute_name">name</span><span class="xml_tag_symbols">=</span><span class="xml_attribute_value">"to"</span><span class="xml_tag_symbols">><</span><span class="xml_tag_name">string</span><span class="xml_tag_symbols">></span><span class="xml_plain">9</span><span class="xml_tag_symbols"></</span><span class="xml_tag_name">string</span><span class="xml_tag_symbols">></</span><span class="xml_tag_name">field</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"></</span><span class="xml_tag_name">object</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"></</span><span class="xml_tag_name">value</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"></</span><span class="xml_tag_name">collection</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"></</span><span class="xml_tag_name">field</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"></</span><span class="xml_tag_name">object</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"></</span><span class="xml_tag_name">object-param</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"></</span><span class="xml_tag_name">init-params</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br /> <span class="xml_plain"> </span><span class="xml_tag_symbols"></</span><span class="xml_tag_name">component</span><span class="xml_tag_symbols">></span><span class="xml_plain"> </span><br /> <span class="xml_tag_symbols"></</span><span class="xml_tag_name">configuration</span><span class="xml_tag_symbols">></span><span class="xml_plain"></span><br />
The name of the group.
The ID of the parent group. If the parent ID is null the group is at the first level. If parent groups do not exist, this ID will be created automatically.
A description of the group.
A label for the group.
This group can be cloned to may copies and each copy is marked with a number.
The user's ID.
The user's password.
last name.
The user's email address.
A list of the groups that user belongs to.
Users can be cloned and each copy is assigned a number. This allows the creation of a range of users that can be put into various groups based on the range value.
The " auto.create.group.page.navigation
" parameter value is true
or false
. If this value is set to true
it automatically creates portal navigations and pages for each group. If false
it does not.
The "auto.create.user.page.navigation
" parameter value is the number of pages that are automatically created for each user.
GateIn 3.0 provides support for skinning the entire portal User Interface (UI) including your own portlets. Skins are designed to help you pack and reuse common graphic resources.
Skins can be switched dynamically at runtime.
When you switch, the whole portal will be repainted and the new styles will be applied to the UI.
The complete skinning of a page can be decomposed into four parts:
CSS styles for html tags (ex div,th,td...) and the portal UI (including the toobar).
CSS styles for each portlet. Used when a portlet want to define it's own CSS.
CSS styles for the surrounding of windows (Which embed either gadgets or portlets).
GateIn 3.0 WebUI components styles are reused among most of the shipped-in portlets.
The main portal skin stylesheet
(/DefaultSkin/skin/Stylesheet.css
)is shown below as
an example:
@import url(/eXoResources/skin/PortletThemes/Stylesheet.css) ; @import url(DefaultSkin/portal/webui/component/UIPortalApplicationSkin.css) ; @import url(DefaultSkin/webui/component/Stylesheet.css) ;
A GateIn 3.0 skin contains css styles for the portal's components but
also shares components that may be reused in portlets. When GateIn 3.0
generates a portal page markup, it inserts stylesheet links in the page's
head
tag.
In the snipped code below you can see two types of links:
<head> ... <link id="CoreSkin" rel="stylesheet" type="text/css" href="/eXoResources/skin/Stylesheet.css" /> <link id="web_
FooterPortlet" rel="stylesheet" type="text/css" href= "/web/skin/portal/webui/component/UIFooterPortlet/DefaultStylesheet.css" /> <link id="web_NavigationPortlet" rel="stylesheet" type="text/css" href= "/web/skin/portal/webui/component/UINavigationPortlet/DefaultStylesheet.css" /> <link id="web_HomePagePortlet" rel="stylesheet" type="text/css" href= "/portal/templates/skin/webui/component/UIHomePagePortlet/DefaultStylesheet.css" /> <link id="web_BannerPortlet" rel="stylesheet" type="text/css" href= "/web/skin/portal/webui/component/UIBannerPortlet/DefaultStylesheet.css" /> ... </head>
![]() | Portal skin stylesheet
( |
![]() | Portlets skins stylesheets (all others). Each portlet within the page may contribute its own style(s) |
Window styles are included with the portal skin
The default skin of portal is called Default. To change this value
add a skin tag in the portal.xml
that defines your
portal:
<portal-config>
<portal-name>classic</portal-name>
<locale>en</locale>
<access-permissions>Everyone</access-permissions>
<edit-permission>*:/platform/administrators</edit-permission>
<skin>MySkin</skin>
<creator>root</creator>
...
The SkinService is an GateIn 3.0 service that manages portal skin, portlet styles and portlet themes (windows borders).
GateIn 3.0 automatically discovers web archives that contains a file descriptor for skins (WEB-INF/gatein-resources.xml).
Because of the Right-To-Left support all CSS files need to be retrieved through a Servlet filter and the web application need to be configured to activate this filter. This is already done for the 01eXoResources.war web application.
<filter>
<filter-name>ResourceRequestFilter</filter-name>
<filter-class>org.exoplatform.portal.application.ResourceRequestFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ResourceRequestFilter</filter-name>
<url-pattern>*.css</url-pattern>
</filter-mapping>
GateIn 3.0 automatically discovers web archives that contains a file descriptor for skins (WEB-INF/gatein-resources.xml).The full schema can be found in 02portal.war:/WEB-INF/gatein_resources_1-0.xsd
Here is an example:
<gatein-resources> <portal-skin> <skin-name>MySkin</skin-name> <css-path>/skin/myskin.css</css-path> <overwrite>false</overwrite> </portal-skin> </gatein-resources>
Here we defined a skin (MySkin) and the CSS location within the same web archive.
Default portal skin and window styles are defined in 01eXoResources.war:/WEB-INF/gatein-resources.xml and the part dedicated to skinning is defined like:
<gatein-resources xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.gatein.org/xml/ns/gatein_resources_1_0.xsd" xmlns="http://www.ga tein.org/xml/ns/gatein_resources_1_0.xsd"> <portal-skin> <skin-name>Default</skin-name> <css-path>/skin/Stylesheet.css</css-path> </portal-skin> <!-- Simple window style --> <window-style> <style-name>Simple</style-name> <style-theme> <theme-name>SimpleBlue</theme-name> </style-theme> <style-theme> <theme-name>SimpleViolet</theme-name> </style-theme> <style-theme> <theme-name>SimpleOrange</theme-name> </style-theme> <style-theme> <theme-name>SimplePink</theme-name> </style-theme> <style-theme> <theme-name>SimpleGreen</theme-name> </style-theme> </window-style>
When selecting a skin, an overview of the skin is possible. It has to be defined in the current skin used to display the form to change skins.
Put the screeshot of the skin in:
01eXoResources.war:/skin/DefaultSkin/portal/webui/component/customization/UIChangeSkinForm/background
and add the following in
01eXoResources.war:/skin/DefaultSkin/portal/webui/component/customization/UIChangeSkinForm/Stylesheet.css:
.UIChangeSkinForm .UIItemSelector .TemplateContainer .MySkinImage { margin: auto; width: 329px; height:204px; background: url('background/MySkin.jpg') no-repeat top; cursor: pointer ; }
Window styles are the CSS applied to window decoration. When an administrator choose a new application to add on a page he can decide which style of decoration would go around the window if any.
The code below illustrates the inclusion of all CSS resoucres for a
new theme from the example file
GateInResourcesCp060508/skin/MyPortalSkin/PortletThemes/Stylesheet.css
.
In 01eXoResources:/WEB-INF/gatein-resources.xml add the following snippet:
<window-style> <style-name>MyTheme</style-name> <style-theme> <theme-name>MyThemeBlue</theme-name> </style-theme> <style-theme> <theme-name>MyThemeRed</theme-name> </style-theme> </window-style>
Here we created a group of themes called 'MyTheme' and two themes that reference CSS classnames.
Create the CSS file:
/*---- MyTheme ----*/ .MyTheme .WindowBarCenter .WindowPortletInfo { margin-right: 80px; /* orientation=lt */ margin-left: 80px; /* orientation=rt */ } .MyTheme .WindowBarCenter .ControlIcon { float: right;/* orientation=lt */ float: left;/* orientation=rt */ width: 24px; height: 17px; cursor: pointer; background-image: url('background/MyTheme.png'); } .MyTheme .ArrowDownIcon { background-position: center 20px; } .MyTheme .OverArrowDownIcon { background-position: center 116px; } .MyTheme .MinimizedIcon { background-position: center 44px; } .MyTheme .OverMinimizedIcon { background-position: center 140px; } .MyTheme .MaximizedIcon { background-position: center 68px; } .MyTheme .OverMaximizedIcon { background-position: center 164px; } .MyTheme .RestoreIcon { background-position: center 92px; } .MyTheme .OverRestoreIcon { background-position: center 188px; } .MyTheme .NormalIcon { background-position: center 92px; } .MyTheme .OverNormalIcon { background-position: center 188px; } .UIPageDesktop .MyTheme .ResizeArea { float: right;/* orientation=lt */ float: left;/* orientation=rt */ width: 18px; height: 18px; cursor: nw-resize; background: url('background/ResizeArea18x18.gif') no-repeat left top; /* orientation=lt */ background: url('background/ResizeArea18x18-rt.gif') no-repeat right top; /* orientation=rt */ } .MyTheme .Information { height: 18px; line-height: 18px; vertical-align: middle; font-size: 10px; padding-left: 5px;/* orientation=lt */ padding-right: 5px;/* orientation=rt */ margin-right: 18px;/* orientation=lt */ margin-left: 18px;/* orientation=rt */ } .MyTheme .WindowBarCenter .WindowPortletIcon { background-position: left top; /* orientation=lt */ background-position: right top; /* orientation=rt */ padding-left: 20px; /* orientation=lt */ padding-right: 20px; /* orientation=rt */ height: 16px; line-height: 16px; } .MyTheme .WindowBarCenter .PortletName { font-weight: bold; color: #333333; overflow: hidden; white-space: nowrap; width: 100%; } .MyTheme .WindowBarLeft { padding-left: 12px; background-image: url('background/MyTheme.png'); background-repeat: no-repeat; background-position: left -148px; } .MyTheme .WindowBarRight { padding-right: 11px; background-image: url('background/MyTheme.png'); background-repeat: no-repeat; background-position: right -119px; } .MyTheme .WindowBarCenter { background-image: url('background/MyTheme.png'); background-repeat: repeat-x; background-position: left -90px; } .MyTheme .WindowBarCenter .FixHeight { height: 21px; padding-top: 8px; } .MyTheme .MiddleDecoratorLeft { padding-left: 12px; background: url('background/MyTheme.png') repeat-y left; } .MyTheme .MiddleDecoratorRight { padding-right: 11px; background: url('background/MyTheme.png') repeat-y right; } .MyTheme .MiddleDecoratorCenter { background: #ffffff; } .MyTheme .BottomDecoratorLeft { MyTheme: 12px; background-image: url('background/MyTheme.png'); background-repeat: no-repeat; background-position: left -60px; } .MyTheme .BottomDecoratorRight { padding-right: 11px; background-image: url('background/MyTheme.png'); background-repeat: no-repeat; background-position: right -30px; } .MyTheme .BottomDecoratorCenter { background-image: url('background/MyTheme.png'); background-repeat: repeat-x; background-position: left top; } .MyTheme .BottomDecoratorCenter .FixHeight { height: 30px; }
Portlets often require additionnal styles that may not be defined by
the portal skin. GateIn 3.0 allows portlets to define additional
stylesheets for each portlet and will append the corresponding
link
tags to the head
.
The link ID will be of the form
{portletAppName}{PortletName}
. For example:
ContentPortlet
in content.war
, will
give
id="content
ContentPortlet"
To define a new CSS file to include whenever a portlet is added, the following snippet need to be added in gatein-resources.xml
<portlet-skin> <application-name>portletAppName</application-name> <portlet-name>PortletName</portlet-name> <skin-name>Default</skin-name> <css-path>/skin/DefaultStylesheet.css</css-path> </portlet-skin> <portlet-skin> <application-name>portletAppName</application-name> <portlet-name>PortletName</portlet-name> <skin-name>OtherSkin</skin-name> <css-path>/skin/OtherSkinStylesheet.css</css-path> </portlet-skin>
The portal framework will include the CSS file corresponding to the skin in used.
Each portlet is represented by an icon that you can see in the portlet registry. This icon can be changed by adding an image to the directory:
skin/DefaultSkin/portletIcons
.
To be used correctly the icon must be named after the
portlet.icon_name.png
For example,the icon for an account portlet named AccountPortlet would be located at:
skin/DefaultSkin/portletIcons/AccountPortlet.png
By default, CSS files are cached and their imports are merged into a single CSS file at the server side. This reduces the number of HTTP requests from the browser to the server.
The optimization code is quite simple as all the CSS files are
parsed at the server startup time and all the @import and url(...)
references are rewritten to support a single flat file. The result is
stored in a cache directly used from the
ResourceRequestFilter
.
Although the optimization is useful for a production environments,
it may be easier to deactivate this optimization while debugging
stylesheets. To do so, set the java system property
exo.product.developing
to
true
.
For example, the property can passed as a JVM parameter with the
-D
option when running GateIn.
sh $JBOSS_HOME/bin/run.sh -Dexo.product.developing=true
warning("This is option may cause display bugs with certain browsers like Internet Explorer")
It is recommended that users have some experience with CSS before studying GateIn 3.0 CSS.
GateIn 3.0 relies heavily on CSS to create the layout and effects for the UI. Some common techniques for customizing GateIn 3.0's CSS are explained below.
The decorator is a pattern to create a contour or a curve around an area. In order to achieve this effect you need to create 9 cells. The BODY is the central area that you want to decorate. The other 8 cells are distributed around the BODY cell. You can use the width, height and background image properties to achieve any decoration effect that you want.
----------------------------------------------------------------------- | | | | | TopLeft | TopCenter | TopRight | | | | | ----------------------------------------------------------------------- | | | | | CenterLeft | BODY | CenterRight | | | | | ----------------------------------------------------------------------- | | | | | BottomLeft | BottomCenter | BottomRight | | | | | ----------------------------------------------------------------------- <div class="Parent"> <div class="TopLeft"> <div class="TopRight"> <div class="TopCenter"><span></span></div> </div> </div> <div class="CenterLeft"> <div class="CenterRight"> <div class="CenterCenter">BODY</div> </div> </div> <div class="BottomLeft"> <div class="BottomRight"> <div class="BottomCenter"><span></span></div> </div> <div> </div>
Left margin left pattern is a technique to create 2 blocks side by side. The left block will have a fixed size and the right block will take the rest of the available space. When the user resizes the browser the added or removed space will be taken from the right block.
| | | | | | | |<--- fixed width --->| | will expand to right ----> | | | | | | | | | | ---- <div class="Parent"> <div style="float: left; width: 100px"> </div> <div style="margin-left: 105px;"> <div> <div style="clear: left"><span></span></div> </div>
Managing Javascript scripts in an application like is a critical part of the configuration work. Configuring the scripts correctly will result in a faster response time from the portal.
Every portlet can have its own javscript code but in many cases it is more convenient to reuse some existing shared libraries. For that reason, has a mechanism to easily register the libraries that will be loaded when the first page will be rendered.
To do so, every WAR deployed in can register the js
files with the groovy script "WEB-INF/conf/script/groovy/JavascriptScript.groovy
".
The example file below is found in the 01eXoResources.war
JavascriptService.addJavascript("eXo", "/javascript/eXo.js", ServletContext); /* Animation Javascripts */ JavascriptService.addJavascript("eXo.animation.ImplodeExplode", "/javascript/eXo/animation/ImplodeExplode.js", ServletContext); /* Application descriptor */ JavascriptService.addJavascript("eXo.application.ApplicationDescriptor", "/javascript/eXo/application/ApplicationDescriptor.js", ServletContext); /* CORE Javascripts */ JavascriptService.addJavascript("eXo.core.Utils", "/javascript/eXo/core/Util.js", ServletContext); JavascriptService.addJavascript("eXo.core.DOMUtil", "/javascript/eXo/core/DOMUtil.js", ServletContext); JavascriptService.addJavascript("eXo.core.Browser", "/javascript/eXo/core/Browser.js", ServletContext); JavascriptService.addJavascript("eXo.core.MouseEventManager", "/javascript/eXo/core/MouseEventManager.js", ServletContext); JavascriptService.addJavascript("eXo.core.UIMaskLayer", "/javascript/eXo/core/UIMaskLayer.js", ServletContext); JavascriptService.addJavascript("eXo.core.Skin", "/javascript/eXo/core/Skin.js", ServletContext); JavascriptService.addJavascript("eXo.core.DragDrop", "/javascript/eXo/core/DragDrop.js", ServletContext); JavascriptService.addJavascript("eXo.core.TemplateEngine", "/javascript/eXo/core/TemplateEngine.js", ServletContext); /* Widget Javascripts */ JavascriptService.addJavascript("eXo.widget.UIWidget", "/javascript/eXo/widget/UIWidget.js", ServletContext); JavascriptService.addJavascript("eXo.widget.UIAddWidget", "/javascript/eXo/widget/UIAddWidget.js", ServletContext); JavascriptService.addJavascript("eXo.widget.UIExoWidget", "/javascript/eXo/widget/UIExoWidget.js", ServletContext); /* Desktop Javascripts */ JavascriptService.addJavascript("eXo.desktop.UIDockbar", "/javascript/eXo/desktop/UIDockbar.js", ServletContext); JavascriptService.addJavascript("eXo.desktop.UIDesktop", "/javascript/eXo/desktop/UIDesktop.js", ServletContext); /* WebUI Javascripts */ JavascriptService.addJavascript("eXo.webui.UIItemSelector", "/javascript/eXo/webui/UIItemSelector.js", ServletContext); JavascriptService.addJavascript("eXo.webui.UIForm", "/javascript/eXo/webui/UIForm.js", ServletContext); JavascriptService.addJavascript("eXo.webui.UIPopup", "/javascript/eXo/webui/UIPopup.js", ServletContext); JavascriptService.addJavascript("eXo.webui.UIPopupSelectCategory", "/javascript/eXo/webui/UIPopupSelectCategory.js", ServletContext); JavascriptService.addJavascript("eXo.webui.UIPopupWindow", "/javascript/eXo/webui/UIPopupWindow.js", ServletContext); JavascriptService.addJavascript("eXo.webui.UIVerticalScroller", "/javascript/eXo/webui/UIVerticalScroller.js", ServletContext); JavascriptService.addJavascript("eXo.webui.UIHorizontalTabs", "/javascript/eXo/webui/UIHorizontalTabs.js", ServletContext); JavascriptService.addJavascript("eXo.webui.UIPopupMenu", "/javascript/eXo/webui/UIPopupMenu.js", ServletContext); JavascriptService.addJavascript("eXo.webui.UIDropDownControl", "/javascript/eXo/webui/UIDropDownControl.js", ServletContext); /* Portal Javascripts */ JavascriptService.addJavascript("eXo.portal.PortalHttpRequest", "/javascript/eXo/portal/PortalHttpRequest.js", ServletContext); JavascriptService.addJavascript("eXo.portal.UIPortal", "/javascript/eXo/portal/UIPortal.js", ServletContext); JavascriptService.addJavascript("eXo.portal.UIWorkspace", "/javascript/eXo/portal/UIWorkspace.js", ServletContext); JavascriptService.addJavascript("eXo.portal.UIPortalControl", "/javascript/eXo/portal/UIPortalControl.js", ServletContext); JavascriptService.addJavascript("eXo.portal.PortalDragDrop", "/javascript/eXo/portal/PortalDragDrop.js", ServletContext); JavascriptService.addJavascript("eXo.portal.UIPortalNavigation", "/javascript/eXo/portal/UIPortalNavigation.js", ServletContext); JavascriptService.addJavascript("eXo.portal.UIMaskWorkspace", "/javascript/eXo/portal/UIMaskWorkspace.js", ServletContext); JavascriptService.addJavascript("eXo.portal.UIExoStartMenu", "/javascript/eXo/portal/UIExoStartMenu.js", ServletContext); /* Desktop Javascripts 2 */ JavascriptService.addJavascript("eXo.desktop.UIWindow", "/javascript/eXo/desktop/UIWindow.js", ServletContext);
Note that even registered dedicated javascripts will be merged into a single merged.js
file when the server loads. This reduces the number of HTTP calls as seen in the home page source code:
<script type="text/javascript" src="/portal/javascript/merged.js"></script>
Although this optimization is useful for a production environment, it may be easier to deactivate this optimization while debugging javascript problems.
To do this, set the java system property exo.product.developing
to true
.
To see or use the merged file set this property to false
.
The property can be passed as a JVM parameter with the -D
option in your GateIn.sh
or GateIn.bat
startup script.
Every javascript file is referenced with a module name of "eXo.core.DragDrop
" which acts as a namespace. Inside the associated files, global javascript functions are used following the same namespace convention:
eXo.core.DragDrop = new DragDrop() ;
It is also possible to use the eXo.require() javascript method to lazy load and evaluate some javascript code. This is quite useful from the portlet or widget applications that will use this javascript only once. Otherwise, if the library is reusable in several places it is better to reference it in the groovy file.
The following parameters are available controll the Dashboard configuration (when in edit mode):
if empty, everyone share the same dashboard and can edit it
if set to CURRENTUSER , every user has his own dashboard
if set to a username, everyone will see the dashboard of this person
if set to 1, only the owner of the dashboard can edit it
if set to 0, everyone can edit it
Token service is used in authentication.
The token system prevents user account information being sent in clear text mode for inbound requests. This increases authentication security.
Token service allows adminitrators to create, delete, retrieve and clean tokens as required. The service also defines the validity period of any given token. The token becomes invalid once this period has expired, . The life-time definition must be configured.
All token services used in 's authentication must be a subclass of an abstract class AbstractTokenService. The following example shows how the token-service manipulates its tokens.
public Token getToken(String id) throws PathNotFoundException, RepositoryException;
public Token deleteToken(String id) throws PathNotFoundException, RepositoryException;
public String[] getAllTokens();
public long getNumberTokens() throws Exception;
public String createToken(Credentials credentials) throws IllegalArgumentException,NullPointerException;
public Credentials validateToken(String tokenKey, boolean remove) throws NullPointerException;
Token services configuration includes specifying the validity period time of token in the configuration file. The token service is configured as a portal component.
In the example below, CookieTokenService is a subclass of AbstractTokenService so it has a property which specifies the validity period of the token.
The token service will initiate this validity property by looking for an init-param
named "service.configuration".
This property must have three values.
<component>
<key>org.exoplatform.web.security.security.CookieTokenService</key>
<type>org.exoplatform.web.security.security.CookieTokenService</type>
<init-params>
<values-param>
ml_plain"> <name>tokenTimeout</name>
ml_plain"> <value>jcr-token</value>
ml_plain"> <value>7</value>
<value>DAY</value>
</values-param>
</init-params>
</component>
![]() | Service name |
![]() | Amount of time |
![]() | Unit of time |
In this case, the service's name is "jcr-token" and the token's expiration time is a week.
supports four timing units:
SECOND
MINUTE
HOUR
DAY
, as an integration and aggregation platform provides some form of Single Sign On (SSO
).
When logging into the portal users gain access to many systems through portlets using a single identity. In many cases, however, the portal infrastructure must be integrated with other SSO enabled systems. There are many different Identity Management solutions available. In most cases each SSO framework provides a unique way to plug into a Java EE application.
In this tutorial, the SSO server is installed in a Tomcat installation. Tomcat can be obtained from http://tomcat.apache.org.
All the packages required for setup can be found in a zip file located at: http://repository.jboss.org/maven2/org/gatein/sso/sso-packaging
When manipulating gatein.ear
directly it is better to not run any portal extensions that could override the data.
Remove $JBOSS_HOME/server/default/deploy/gatein-sample-extension.ear
and $JBOSS_HOME/server/default/deploy/gatein-sample-portal.ear
which are packaged by default with .
This Single Sign On plugin enables seamless integration between and the CAS Single Sign On Framework. Details about CAS can be found here.
The integration consists of two parts; the first part consists of installing or configuring a CAS server, the second part consists of setting up the portal to use the CAS server.
First we will set up the server to authenticate against the portal login module. You can find more information about setting up the server by reading the official CAS documentation, here we will install the CAS server on Tomcat
You can download CAS from http://www.jasig.org/cas/download.
Once downloaded extract it in what we will call $CAS_HOME from now.
To simplify we will directly modify the sources so that the produced web archive is configured the way we want.
First we will want to change the authenticaton handler to use the portal authentication handler:
The CAS Server Plugin makes secure authentication callbacks to a RESTful service installed on the remote GateIn server in order to authenticate a user. In order for the plugin to function correctly, it needs to be properly configured to connect to this service. This configuration is done via the cas.war/WEB-INF/deployerConfigContext.xml file.
Open $CAS_HOME/cas-server-webapp/src/main/webapp/WEB-INF/deployerConfigContext.xml
Replace:
<!-- | Whereas CredentialsToPrincipalResolvers identify who it is some Credentials might authenticate, | AuthenticationHandlers actually authenticate credentials. Here e declare the AuthenticationHandlers that | authenticate the Principals that the CredentialsToPrincipalResolvers identified. CAS will try these handlers in turn | until it finds one that both supports the Credentials presented and succeeds in authenticating. +--> <property name="authenticationHandlers"> <list> <!-- | This is the authentication handler that authenticates services by means of callback via SSL, thereby validating | a server side SSL certificate. +--> <bean class="org.jasig.cas.authentication.handler.support.HttpBasedServiceCredentialsAuthenticationHandler" p:httpClient-ref="httpClient" /> <!-- | This is the authentication handler declaration that every CAS deployer will need to change before deploying CAS | into production. The default SimpleTestUsernamePasswordAuthenticationHandler authenticates UsernamePasswordCredentials | where the username equals the password. You will need to replace this with an AuthenticationHandler that implements your | local authentication strategy. You might accomplish this by coding a new such handler and declaring | edu.someschool.its.cas.MySpecialHandler here, or you might use one of the handlers provided in the adaptors modules. +--> <bean class="org.jasig.cas.authentication.handler.support.SimpleTestUsernamePasswordAuthenticationHandler" /> </list> </property>
With the following (Make sure to set the host, port and context with the values corresponding to your portal). Also available in $GATEIN_SSO/cas/plugin/WEB-INF/deployerConfigContext.xml
<!-- | Whereas CredentialsToPrincipalResolvers identify who it is some Credentials might authenticate, | AuthenticationHandlers actually authenticate credentials. Here we declare the AuthenticationHandlers that | authenticate the Principals that the CredentialsToPrincipalResolvers identified. CAS will try these handlers in turn | until it finds one that both supports the Credentials presented and succeeds in authenticating. +--> <property name="authenticationHandlers"> <list> <!-- | This is the authentication handler that authenticates services by means of callback via SSL, thereby validating | a server side SSL certificate. +--> <bean class="org.jasig.cas.authentication.handler.support.HttpBasedServiceCredentialsAuthenticationHandler" p:httpClient-ref="httpClient" /> <!-- | This is the authentication handler declaration that every CAS deployer will need to change before deploying CAS | into production. The default SimpleTestUsernamePasswordAuthenticationHandler authenticates UsernamePasswordCredentials | where the username equals the password. You will need to replace this with an AuthenticationHandler that implements your | local authentication strategy. You might accomplish this by coding a new such handler and declaring | edu.someschool.its.cas.MySpecialHandler here, or you might use one of the handlers provided in the adaptors modules. +--> <!-- Integrates with the Gatein Authentication Service to perform authentication --> <!-- | Note: Modify the Plugin Configuration based on the actual information of a GateIn instance. | The instance can be anywhere on the internet...Not necessarily on localhost where CAS is running +--> <bean class="org.gatein.sso.cas.plugin.AuthenticationPlugin"> <property name="gateInHost"><value>localhost</value></property> <property name="gateInPort"><value>8080</value></property> <property name="gateInContext"><value>portal</value></property> </bean> </list> </property>
Copy $GATEIN_SSO/cas/plugin/WEB-INF/lib/sso-cas-plugin-<VERSION>.jar and $GATEIN_SSO/cas/plugin/WEB-INF/lib/commons-httpclient-<VERSION>.jar into the newly created directory $CAS_HOME/cas-server-webapp/src/main/webapp/WEB-INF/lib
Get an installation of Tomcat and extract it in what we will call $TOMCAT_HOME. Change the default port to avoid a conflict with the default GateIn (for testing purposes). Edit $TOMCAT_HOME/conf/server.xml and replace the 8080 port to 8888.
If you are running GateIn with Tomcat on the same machine you will also need to change the port 8005 to something else to avoid port conflicts.
Go to $CAS_HOME/cas-server-webapp and do 'mvn install'
Copy $CAS_HOME/cas-server-webapp/target/cas.war into $TOMCAT_HOME/webapps
Now you should be able to start Tomcat and access http://localhost:8888/cas but at this stage you won't be able to login.
Copy all libraries from $GATEIN_SSO/cas/gatein.ear/lib into $JBOSS_HOME/server/default/deploy/gatein.ear/lib (Or if you are running GateIn in Tomcat, in $GATEIN_HOME/lib)
In JBoss AS, edit gatein.ear/META-INF/gatein-jboss-beans.xml and uncomment this section
<authentication> <login-module code="org.gatein.sso.agent.login.SSOLoginModule" flag="required"> </login-module> <login-module code="org.exoplatform.services.security.j2ee.JbossLoginModule" flag="required"> <module-option name="portalContainerName">portal</module-option> <module-option name="realmName">gatein-domain</module-option> </login-module> </authentication>
If you are running GateIn in Tomcat, edit $GATEIN_HOME/conf/jaas.conf and uncomment this section
org.gatein.sso.agent.login.SSOLoginModule required org.exoplatform.services.security.j2ee.JbossLoginModule required portalContainerName=portal realmName=gatein-domain
At this point, you can test the installation, start GateIn (assuming that the CAS server using Tomcat is still running) by going to http://localhost:8888/cas you should be able to login with username 'root' and password 'gtn' or any account created through the portal.
Now we want to tell GateIn to redirect all user authentication to the CAS server.
The CAS server can be located anywhere on the Internet, and this information must be properly configured within the GateIn instance. This configuration needs to be done in 3 files
<script> <%=uicomponent.event("Close");%> window.location = 'http://localhost:8888/cas/login?service=http://localhost:8080/portal/private/classic'; </script>
<html> <head> <script type="text/javascript"> window.location = 'http://localhost:8888/cas/login?service=http://localhost:8080/portal/private/classic'; </script> </head> <body> </body> </html>
<servlet> <servlet-name>InitiateLoginServlet</servlet-name> <servlet-class>org.gatein.sso.agent.GenericSSOAgent</servlet-class> <init-param> <param-name>ssoServerUrl</param-name> <param-value>http://localhost:8888/cas</param-value> </init-param> </servlet>
From now on, all links redirecting to the user authentication pages will redirect to the CAS centralized authentication form.
This Single Sign On plugin enables seamless integration between GateIn Portal and the JOSSO Single Sign On Framework. Details about OpenSSO can be found here.
The integration consitsts in two parts, the first part consists of installing or configuring a JOSSO server, the second part consists of setting up the portal to use the JOSSO server.
First we will set up the server to authenticate against the portal login module. You can find more information about setting up the server by reading the official JOSSO documentation, here we will install the JOSSO server on Tomcat
You can download JOSSO from http://sourceforge.net/projects/josso/files/ We will use the package that embeds Apache Tomcat.
Once downloaded extract it in what we will call $JOSSO_HOME from now.
Copy the files from $GATEIN_SSO/josso/plugin into the Tomcat directory ($JOSSO_HOME).
It should replace or add $JOSSO_HOME/lib/josso-gateway-config.xml $JOSSO_HOME/lib/josso-gateway-gatein-stores.xml $JOSSO_HOME/webapps/josso/WEB-INF/classes/gatein.properties and add required jars into $JOSSO_HOME/webapps/josso/WEB-INF/lib
Change the default Tomcat ports to avoid a conflict with the default GateIn (for testing purposes). Edit $TOMCAT_HOME/conf/server.xml and replace the 8080 port to 8888.
If you are running GateIn with Tomcat on the same machine you will also need to change other ports to something else to avoid port conflicts.
Now you should be able to start Tomcat and access http://localhost:8888/josso/signon/login.do but at this stage you won't be able to login.
Copy the libs from $GATEIN_SS)/josso/gatein.ear/lib into gatein.ear/lib (Or if you are running GateIn in Tomcat, in $GATEIN_HOME/lib)
In JBoss AS, edit gatein.ear/META-INF/gatein-jboss-beans.xml and uncomment this section
<authentication> <login-module code="org.gatein.sso.agent.login.SSOLoginModule" flag="required"> </login-module> <login-module code="org.exoplatform.services.security.j2ee.JbossLoginModule" flag="required"> <module-option name="portalContainerName">portal</module-option> <module-option name="realmName">gatein-domain</module-option> </login-module> </authentication>
If you are running GateIn in Tomcat, edit $GATEIN_HOME/conf/jaas.conf and uncomment this section
org.gatein.sso.agent.login.SSOLoginModule required org.exoplatform.services.security.j2ee.JbossLoginModule required portalContainerName=portal realmName=gatein-domain
At this point, you can test the installation, start GateIn (assuming that the JOSSO server using Tomcat is still running) by going to http://localhost:8888/josso/signon/login.do you should be able to login with username 'root' and password 'gtn' or any account created through the portal.
Now we want to tell GateIn to redirect all user authentication to the CAS server.
The CAS server can be located anywhere on the Internet, and this information must be properly configured within the GateIn instance. This configuration needs to be done in 3 files
<script> <%=uicomponent.event("Close");%> window.location = 'http://localhost:8888/josso/signon/login.do?josso_back_to=http://localhost:8080/portal/private/classic'; </script>
<html> <head> <script type="text/javascript"> window.location = 'http://localhost:8888/josso/signon/login.do?josso_back_to=http://localhost:8080/portal/private/classic'; </script> </head> <body> </body> </html>
<servlet> <servlet-name>InitiateLoginServlet</servlet-name> <servlet-class>org.gatein.sso.agent.GenericSSOAgent</servlet-class> <init-param> <param-name>ssoServerUrl</param-name> <param-value>http://localhost:8888/cas</param-value> </init-param> </servlet>
In gatein.ear/02portal.war/WEB-INF/web.xml remove the PortalLoginController servlet declaration and mapping
From now on, all links redirecting to the user authentication pages will redirect to the JOSSO centralized authentication form.
This Single Sign On plugin enables seamless integration between GateIn Portal and the OpenSSO Single Sign On Framework. Details about OpenSSO can be found here.
The integration consitsts in two parts, the first part consists of installing or configuring an OpenSSO server, the second part consists of setting up the portal to use the OpenSSO server.
First we will set up the server to authenticate against the portal login module. You can find more information about setting up the server by reading the official OpenSSO documentation, here we will install the OpenSSO server on Tomcat
You can download OpenSSO from https://opensso.dev.java.net/public/use/index.html.
Once downloaded extract it in what we will call $OPENSSO_HOME from now.
To simplify we will directly modify the sources so that the produced web archive is configured the way we want.
First we will want to add the GateIn Authentication Plugin:
The plugin makes secure authentication callbacks to a RESTful service installed on the remote GateIn server in order to authenticate a user. In order for the plugin to function correctly, it needs to be properly configured to connect to this service. This configuration is done via the opensso.war/config/auth/default/AuthenticationPlugin.xml file.
Get an installation of Tomcat and extract it in what we will call $TOMCAT_HOME. Change the default port to avoid a conflict with the default GateIn (for testing purposes). Edit $TOMCAT_HOME/conf/server.xml and replace the 8080 port to 8888.
If you are running GateIn with Tomcat on the same machine you will also need to change the port 8005 to something else to avoid port conflicts.
This is what the $TOMCAT_HOME/webapps/opensso/config/auth/default/AuthenticationPlugin.xml file should look like:
<?xml version='1.0' encoding="UTF-8"?> <!DOCTYPE ModuleProperties PUBLIC "=//iPlanet//Authentication Module Properties XML Interface 1.0 DTD//EN" "jar://com/sun/identity/authentication/Auth_Module_Properties.dtd"> <ModuleProperties moduleName="AuthenticationPlugin" version="1.0" > <Callbacks length="2" order="1" timeout="60" header="GateIn OpenSSO Login" > <NameCallback> <Prompt> Username </Prompt> </NameCallback> <PasswordCallback echoPassword="false" > <Prompt> Password </Prompt> </PasswordCallback> </Callbacks> </ModuleProperties>
Copy $GATEIN_SSO/opensso/plugin/WEB-INF/lib/sso-opensso-plugin-<VERSION>.jar , $GATEIN_SSO/opensso/plugin/WEB-INF/lib/commons-httpclient-<VERSION>.jar, and $GATEIN_SSO/opensso/plugin/WEB-INF/lib/commons-logging-<VERSION>.jar into the Tomcat Installation at: $TOMCAT_HOME/webapps/opensso/WEB-INF/lib
Copy $GATEIN_SSO/opensso/plugin/WEB-INF/classes/gatein.properties into the Tomcat Installation at: $TOMCAT_HOME/webapps/opensso/WEB-INF/classes
Now you should be able to start Tomcat and access http://localhost:8888/opensso/UI/Login?realm=gatein but at this stage you won't be able to login.
Copy all libraries from $GATEIN_SSO/opensso/gatein.ear/lib into $JBOSS_HOME/server/default/deploy/gatein.ear/lib (Or if you are running GateIn in Tomcat, in $GATEIN_HOME/lib)
In JBoss AS, edit gatein.ear/META-INF/gatein-jboss-beans.xml and uncomment this section
<authentication> <login-module code="org.gatein.sso.agent.login.SSOLoginModule" flag="required"> </login-module> <login-module code="org.exoplatform.services.security.j2ee.JbossLoginModule" flag="required"> <module-option name="portalContainerName">portal</module-option> <module-option name="realmName">gatein-domain</module-option> </login-module> </authentication>
If you are running GateIn in Tomcat, edit $GATEIN_HOME/conf/jaas.conf and uncomment this section
org.gatein.sso.agent.login.SSOLoginModule required org.exoplatform.services.security.j2ee.JbossLoginModule required portalContainerName=portal realmName=gatein-domain
At this point, you can test the installation, start GateIn (assuming that the OpenSSO server using Tomcat is still running) by going to http://localhost:8888/opensso/UI/Login?realm=gatein you should be able to login with username 'root' and password 'gtn' or any account created through the portal.
Now we want to tell GateIn to redirect all user authentication to the OpenSSO server.
The OpenSSO server can be located anywhere on the Internet, and this information must be properly configured within the GateIn instance. This configuration needs to be done in 3 files
<script> <%=uicomponent.event("Close");%> window.location = 'http://localhost:8888/opensso/UI/Login?realm=gatein&goto=http://localhost:8080/portal/private/classic'; </script>
<html> <head> <script type="text/javascript"> window.location = 'http://localhost:8888/opensso/UI/Login?realm=gatein&goto=http://localhost:8080/portal/private/classic'; </script> </head> <body> </body> </html>
<servlet> <servlet-name>InitiateLoginServlet</servlet-name> <servlet-class>org.gatein.sso.agent.GenericSSOAgent</servlet-class> <init-param> <param-name>ssoServerUrl</param-name> <param-value>http://localhost:8888/opensso</param-value> </init-param> <init-param> <param-name>ssoCookieName</param-name> <param-value>iPlanetDirectoryPro</param-value> </init-param> </servlet>
From now on, all links redirecting to the user authentication pages will redirect to the OpenSSO centralized authentication form.
This chapter describes the portal lifecycle from the application server start to its stop as well as how requests are handled.
A portal instance is simply a web application deployed as a WAR in an application server. Portlets are also part of an enhanced WAR called a portlet application.
doesn't require any particular setup for your portlet in most common scenarios and the web.xml
file can remain without any specific configuration.
During deployment, will automatically and transparently inject a servlet into the portlet application to be able to interact with it. This feature is dependent on the underlying servlet container but will work out of the box on the proposed bundles.
The servlet is the main entry point for incoming requests, it also includes some init code when the portal is launched. This servlet (org.gatein.wci.command.CommandServlet
) is automatically added during deployment and mapped to /tomcatgateinservlet
.
This is equivalent to adding the following into web.xml
.
As the servlet is already configured this example is for information only.
<servlet>
<servlet-name>TomcatGateInServlet</servlet-name>
<servlet-class>org.gatein.wci.command.CommandServlet</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>TomcatGateInServlet</servlet-name>
<url-pattern>/tomcatgateinservlet</url-pattern>
</servlet-mapping>
It is possible to filter on the CommandServlet by filtering the URL pattern used by the Servlet mapping.
The example below would create a servlet filter that calculates the time of execution of a portlet request.
The filter class:
package org.example;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class MyFilter implements javax.servlet.Filter {
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException
{
long beforeTime = System.currentTimeMillis();
chain.doFilter(request, response);
long afterTime = System.currentTimeMillis();
System.out.println("Time to execute the portlet request (in ms): " + (afterTime - beforeTime));
}
public void init(FilterConfig config) throws ServletException
{
}
public void destroy()
{
}
}
The Java EE web application configuration file (web.xml
) of the portlet on which we want to know the time to serve a portlet request. As mentioned above nothing specific to needs to be included, only the URL pattern to set has to be known.
<?xml version="1.0"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.5">
<filter>
<filter-name>MyFilter</filter-name>
<filter-class>org.example.MyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>MyFilter</filter-name>
<url-pattern>/tomcatgateinservlet</url-pattern>
<dispatcher>INCLUDE</dispatcher>
</filter-mapping>
</web-app>
It is important to set INCLUDE
as dispatcher as the portal will always hit the CommandServlet through a request dispatcher. Without this, the filter will not be triggered, unless direct access to a resource (such as an image).
The text orientation depends on the current locale setting. 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() { ... }
The object defining the 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 the 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
.
Orientation is defined by implicit variables in the groovy binding context:
The current orientation as an Orientation
The value of orientation.isLT()
The value of orientation.isRT()
The string 'ltr' if the orientation is LT or the string 'rtl' if the orientation is RT.
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. 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;
The LT produced output will be:
float: left; /* orientation=lt */ font-weight: bold; text-align: center; white-space: nowrap;
The RT produced output will be:
float: right; /* orientation=rt */ font-weight: bold; text-align: center; white-space: nowrap;
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 */
The LT produced output will be:
color: white; line-height: 24px; padding: 0px 5px 0px 0px; /* orientation=lt */
The RT produced output will be:
color: white; line-height: 24px; padding: 0px 0px 0px 5px; /* orientation=rt */
Sometimes 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 as the skin service. 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
.
It is important to consider whether the image to be mirrored is symmetrical as this will impact it's final appearance.
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 */
is fully configurable for internationalization, however users should have a general knowledge of Internationalization in Java products before attempting these configurations.
Sun Java hosts a comprehensive guide to internationalizing java products at http://java.sun.com/docs/books/tutorial/i18n/TOC.html.
All applications contain property files for various languages. They are packaged with the portlets applications in a WEB-INF/classes/locale/
directory.
These files are located in the classes
folder of the WEB-INF directory, so as to be loaded by the ClassLoader.
All resource files are in a subfolder named locale
.
For instance; the translations for the NavigationPortlet are located in web.war/WEB-INF/classes/locale/portlet/portal
NavigationPortlet_de.properties NavigationPortlet_en.properties NavigationPortlet_es.properties NavigationPortlet_fr.properties NavigationPortlet_nl.properties NavigationPortlet_ru.properties NavigationPortlet_uk.properties NavigationPortlet_ar.xml
Inside those file are typical key=value
Java EE properties. For example the French one:
javax.portlet.title=Portlet Navigation
There are also properties files in the portal itself. They form the portal resource bundle.
From a portlet you can then access translations from the portlet itself or shared at the portal level, both are aggregated when you need them.
It is also possible to use a proprietary XML format to define translations. This is a more convenient way to translate a document for some languages such as Japanese, Arabic or Russian. Property files have te be ASCII encoded, while the XML file can define its encoding. As a result it's easier for a human being to read (and fix) a translation in XML instead of having to decode and encode the property file.
For more information refer to: Section 5.4, “XML Resources Bundles”
Various languages are available in the portal package. The configuration below will define which languages are shown in the "Change Language" section and made available to users.
The 02portal.war:/WEB-INF/conf/common/common-configuration.xml
file of your installation contains the following section:
<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>
This configuration points to the locale configuration file.
The locale configuration file (02portal.war:/WEB-INF/conf/common/locales-config.xml
) contains the following code:
<?xml version="1.0" encoding="UTF-8"?> <locales-config> <locale-config> <locale>en</locale> <output-en
coding>UTF-8</output-encoding> <input-enc
oding>UTF-8</input-encoding> <descripti
on>Default configuration for english locale</description> </locale-config> <locale-config> <locale>fr</locale> <output-encoding>UTF-8</output-encoding> <input-encoding>UTF-8</input-encoding> <description>Default configuration for the french locale</description> </locale-config> <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> <orientati
on>rt</orientation> </locale-config> </locales-config>
![]() | locale The locale has to be defined such as defined here http://ftp.ics.uci.edu-pub-ietf-http-related-iso639.txt. In this example "ar" is Arabic. |
![]() | output-encoding deals with character encoding. It is recommended that UTF-8 be used. |
![]() | input-encoding 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(..). |
![]() | description Description for the language |
![]() | description The default orientation of text and images is Left-To-Right. supports Right-To-Left orientation. Modifying text orientation is explained in Section 5.2, “RTL (Right To Left) Framework”. |
The resource bundle service is configured in: 02portal.war:/WEB-INF/conf/common/common-configuration.xml
:
<component> <key>org.exoplatform.services.resources.ResourceBundleService</key> <type>org.exoplatform.services.resources.impl.SimpleResourceBundleService</type> <init-params> <values-param> <name>classpath.resources</name> <description>The resources that start with the following package name should be load from file system</description> <value>locale.portlet</value> </values-param> <values-param> <name>in
it.resources</name> <description>Initiate the following resources during 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>po
rtal.resource.names</name> <description>The properties files of the portal , those file 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>
![]() | classpath.resources are discussed in a later section. |
![]() | init.resources TODO |
![]() | portal.resource.names Defines all resources that belong to the Portal Resource Bundle. These resources are merged to a single resource bundle which is accessible from anywhere in . All these keys are located in the same bundle, which is separated from the navigation resource bundles. |
There is a resource bundle for each navigation. A navigation can exist for user, groups, and portal.
The previous example shows bundle definitions for the navigation of the classic portal and of four different groups. Each of these resource bundles occupies a different sphere, they are independent of each other and they are not included in the portal.resource.names
parameter.
The properties for a group must be in the WEB-INF/classes/locale/navigation/group/
folder. /WEB-INF/classes/locale/navigation/group/organization/management/executive-board_en.properties
, for example.
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 defined in LocalesConfig
must have a resource file defined. If the name of a group is changed the name of the folder and/or files of the correspondent navigation resource bundles must also be changed.
Content of executive-board_en.properties
:
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.
Portlets are independent applications and deliver their own resource files.
All shipped portlet resources are located in the locale/portlet subfolder. The ResourceBundleService parameter classpath.resources defines this subfolder.
Procedure 5.1. Example
To add a Spanish translation to the GadgetPortlet
.
Create the file GadgetPortlet_es.properties
in: WEB-INF/classes/locale/portlet/gadget/GadgetPortlet
.
In portlet.xml
, add Spanish
as a supported-locale ('es' is the 2 letters code for Spanish), the resource-bundle is already declared and is the same for all languages :
<supported-locale>en</supported-locale>
<supported-locale>es</supported-locale>
<resource-bundle>locale.portlet.gadget.GadgetPortlet</resource-bundle>
See the portlet specification for more details about portlet internationalization.
The portlet specifications defines three standard keys: Title, Short Title and Keywords. Keywords is formatted as a comma-separated list of tags.
javax.portlet.title=Breadcrumbs Portlet javax.portlet.short-title=Breadcrumbs javax.portlet.keywords=Breadcrumbs, Breadcrumb
These keys are used to display a property in the user language.
The following access method enables translation to the preferred language for the connected user:
Groovy Template
TODO
Java
WebuiRequestContext context = WebuiRequestContext.getCurrentInstance() ; ResourceBundle res = context.getApplicationResourceBundle() ; String translatedString = res.getString(key);
When translating an application it can sometimes be difficult to find the right key for a given property.
Execute the portal in debug mode and select, from the available languages, select the special language; Magic locale.
This feature translates a key to the same key value.
For example, the translated value for the key "organization.title
" is simply the value "organization.title
". Selecting that language allows use of the portal and its applications with all the keys visible. This makes it easier to find out the correct key for a given label in the portal page.
Resource bundles are usually stored in property files. However, as property files are plain files, issues with the encoding of the file may arise. The XML resource bundle format has been developed to provide an alternative to property files.
The XML format declares the encoding of the file. This avoids use of the native2ascii program which can interfere with encoding.
Property files generally use ISO 8859-1 character encoding which does not cover the full unicode charset. As a result, languages such as Arabic would not be natively supported.
Tooling for XML files is better supported than the tooling for Java property files and thus the XML editor copes well with the file encoding.
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:
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>
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 and it must use the .xml suffix.
For example; for the Account Portlet to be displayed in Arabic, the resource bundle would be AccountPortlet_ar.xml rather than AccountPortlet_ar.properties.
The traditional way of rendering a portal page is static, using a templat (usually a jsp page) for each layout (2 columns, 3 columns and so on). This method depends on the integrator or developers as each new layout will need a custom development.
has a dynamic rendering method which creates a tree of nested UI containers that contain portlets (as shown in the image below).
Each container is responsible for rendering its children. In the below example the main container renders its children in several rows while the nested container displays them as columns.
By manipulating the tree using a WYSIWYG editor new containers can be added, their child rendering can be defined and new portlets can be nested.
As most portals use the static layout mechanism, portlets can only be dragged from one static location, such as a column, to another static location.
In , it is possible to drag UI containers (and their portlets) and drop them in containers that are higher or lower in the Portal component tree. This feature is unique to
JavaScript Inter Application Communication is designed to allow applications within a page to exchange data. This library is made for broadcasting messages on topic.
It is based on 3 functions:
Subscribe.
Publish.
Unsubscribe.
A subscription to a topic will receive any subtopic messages. For example; An application subscribed to "/eXo/application
" will receive messages sent on the "/eXo/application/map
" topic. A message sent on "/eXo
", however, would not be received.
The Inter Application Communication library is found in 01eXoResources.war:/javascript/eXo/core/Topic.js
/** * publish is used to publish an event to the other subscribers to the given channels * @param {Object} senderId is a string that identify the sender * @param {String} topic is the topic that the message will be published * @param {Object} message is the message that's going to be delivered to the subscribers to the topic */ Topic.prototype.publish = function(/*Object*/ senderId, /*String*/ topicName, /*Object*/ message ) { ... } /** * isSubscribed is used to check if a function receive the events from a topic * @param {String} topic The topic. * @param {Function} func is the name of the function of obj to call when a message is received on the topic */ Topic.prototype.isSubscribed = function(/*String*/ topic, /*Function*/ func) { ... } /** * subscribe is used to subscribe a callback to a topic * @param {String} topic is the topic that will be listened * @param {Function} func is the name of the function of obj to call when a message is received on the topic * * func is a function that take a Object in parameter. the event received have this format: * {senderId:senderId, message:message, topic: topic} * */ Topic.prototype.subscribe = function(/*String*/ topic, /*Function*/ func) { ... } /** * unsubscribe is used to unsubscribe a callback to a topic * @param {String} topic is the topic * @param {Object} id is the id of the listener we want to unsubscribe */ Topic.prototype.unsubscribe = function(/*String*/ topic, /*Object*/ id) { ... } Topic.prototype.initCometdBridge = function() { ... }
The three messaging functions require particular objects and definitions in their syntax:
The subscribe
function is used to subscribe a callback to a topic. It uses the following parameters:
The topic that will be listened for.
The name of the object function to call when a message is received on the topic. It has to be a function that takes an Object parameter. The event received will have this format:
{ senderId:senderId, message:message, topic: topic }
The publish
function is used to publish an event to the other subscribered applications through the given channels. Its parameters are:
This is a string that identifies the sender.
The topic that the message will be published.
This is the message body to be delivered to the subscribers to the topic.
The unsubscribe
function is used to unsubscribe a callback to a topic. The required parameters are:
The topic that will is to be unsubscribed from.
This is the context object.
<%@ taglib uri="http://java.sun.com/portlet" prefix="portlet" %> <portlet:defineObjects/> <div> <p> Received messages: <div id="received_<portlet:namespace/>"> </div> </p> <p> Send message: <input type="text" id="msg_<portlet:namespace/>"/> <a href="#" onclick="send_<portlet:namespace/>();">send</a> </p> </div> <script type="text/javascript"> Function.prototype.bind = function(object) { var method = this; return function() { method.apply(object, arguments); } } function send_<portlet:namespace/>() { var msg = document.getElementById("msg_<portlet:namespace/>").value; eXo.core.Topic.publish("<portlet:namespace/>", "/demo", msg); } function Listener_<portlet:namespace/>(){ } Listener_<portlet:namespace/>.prototype.receiveMsg = function(event) { document.getElementById("received_<portlet:namespace/>").innerHTML = document.getElementById("received_<portlet:namespace/>").innerHTML + "<br />* " + event.senderId + ": " + event.message; } function init_<portlet:namespace/>() { var listener_<portlet:namespace/> = new Listener_<portlet:namespace/>(); eXo.core.Topic.subscribe("/demo", listener_<portlet:namespace/>.receiveMsg.bind(listener_<portlet:namespace/>)); } init_<portlet:namespace/>(); </script>
The service is defined by the class: org.exoplatform.upload.UploadService
;
This can be configured 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>
This code allows for a default upload size limit for the service to be configured. The value unit is in MegaBytes.
This limit will be used by default by all applications if no application-specific limit is set. Setting a different limit for applications is discussed in a later section.
If the value is set at 0
the upload size is unlimited.
Procedure 5.2. How to use the upload component
Create an object type org.exoplatform.webui.form.UIFormUploadInput
.
Two constructors are available for this:
public UIFormUploadInput(String name, String bindingExpression)
or:
public UIFormUploadInput(String name, String bindingExpression, int limit)
This is an example using the second form :
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 obtain the limit from the xml
configuration, this piece of code can be added to the either portlet.xml
or portlet-preferences.xml
:
<preference>
<name>uploadFileSizeLimitMB</name>
<value>30</value>
<read-only>false</read-only>
</preference>
Again, a 0
value means an unlimited upload size, and the value unit is set in MegaBytes.
Use the getUploadDataAsStream()
method to get the uploaded data:
UIFormUploadInput input = (UIFormUploadInput)uiForm.getUIInput("upload");
InputStream inputStream = input.getUploadDataAsStream();
...
jcrData.setValue(inputStream);
The upload service stores a temporary file on the filesystem during the upload process. When the upload is finished, the service must be cleaned in order to:
Delete the temporary file.
Delete the classes used for the upload.
Use theremoveUpload()
method defined in the upload service to purge the file:
UploadService uploadService = uiForm.getApplicationComponent(UploadService.class) ;
UIFormUploadInput uiChild = uiForm.getChild(UIFormUploadInput.class) ;
uploadService.removeUpload(uiChild.getUploadId()) ;
Ensure the file is saved before the service is cleaned.
The loading mask layer is deployed after an ajax-call. Its purpose is to block the GUI in order to prevent further user actions until the the ajax-request has been completed.
However, the mask layer may need to be deactivated in instances where the portal requires user instructions before previous instructions have been carried out.
Procedure 5.3. How to deactivate the ajax-loading mask
Generate a script to make an asynchronous ajax-call. Use the uicomponent.doAsync()
method rather than the uicomponent.event()
method.
For example:
<a href="<%=uicomponent.doAsync(action, beanId, params)%>" alt="">Asynchronous</a>
The doAsync()
method automatically adds the following new parameter into the parameters list; asyncparam = new Parameter(AJAX ASYNC,"true"); (AJAX ASYNC == "ajax async")
This request is asynchronous and the ajax-loading mask will not deployed.
An asynchronous request can still be made using the uicomponent.event()
. When using this method, however, the asyncparam must be added manually.
The GUI will be blocked to ensure a user can only request one action at a time and while the request seems to be synchronous, all ajax requests are, in fact always asynchronous. For further information refer to Section 5.8.2, “Synchronous issue”.
Most web browsers support ajax requests in two modes: Synchronous and Asynchronous. This mode is specified with a boolean bAsync
parameter.
var bAsync = false; // Synchronous request.open(instance.method, instance.url, bAsync);
However, in order to work with browsers that do not support Synchronous requests, bAsync
is set to always be true (Ajax request will always be asynchronous).
// Asynchronous request request.open(instance.method, instance.url, true);
The following code retrieves the details for a logged-in user:
// 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 != null)
{
User user = orgService.getUserHandler().findUserByName(userId) ;
if (user != null)
{
String firstName = user.getFirstName();
String lastName = user.getLastName();
String email = user.getEmail();
}
}
Below are two alternatives for retrieving the Organization Service:
OrganizationService service = (OrganizationService)
ExoContainerContext.getCurrentContainer().getComponentInstanceOfType(OrganizationService.class);
OrganizationService service = (OrganizationService)
PortalContainer.getInstance().getComponentInstanceOfType(OrganizationService.class);
WebUI is the name of 's webframework.
Using a self-contained framework ensures that the portal's architecture does not interfere with other web technologies deployed by the portlets.
Using a particular technology raises potential difficulties for users with a differing version (either newer or older) of the same technology.
The WebUI framework is a component tree based framework. The key aspects of WebUI are :
Events based flow.
Components configuration by annotation
Section 6.3, “Groovy Templates” for rendering
Section 6.2, “AJAX Framework” support
Portlet API friendly.
Section 6.4, “Portlet Lifecycle”: portlets are built with WebUI
Section 6.4, “Portlet Lifecycle”: portal itself is a WebUI application
Section 6.6, “Create a WebUI Portlet”: Learn how to write your own app with WebUI
Ajax calls are easily managed in 's framework. Lines can be added to the relevant template file and java class. A simple Ajax update of a component does not require any JavaScript code to be written.
portlets can use specific ActionListener
s to receive and process Ajax calls. To set a portlet up to recieve Ajax calls, follow this procedure:
Create an inner static class named with the following convention: action name
followed by ActionListener
.
Example : ParentClass
is the class being wrtitten in.
static public class SaveActionListener extends EventListener<ParentClass>
Declare this listener in the configuration of the portlet:
listeners = ParentClass.SaveActionListener.class
Add the correct annotation ComponentConfig
, EventConfig
, etc.
For example; the configuration below is for UIAccountForm
:
...
@ComponentConfig(
lifecycle = UIFormLifecycle.class,
template = "system:/groovy/webui/form/UIFormTabPane.gtmpl",
initParams = {
@ParamConfig(
name = "AccountTemplateConfigOption",
value = "app:/WEB-INF/conf/uiconf/account/webui/component/model/AccountTemplateConfigOption.groovy"
),
@ParamConfig(
name = "help.UIAccountFormQuickHelp",
value = "app:/WEB-INF/conf/uiconf/account/webui/component/model/UIAccountFormQuickHelp.xhtml"
)
},
events = {
@EventConfig(listeners = UIAccountForm.SaveActionListener.class ),
@EventConfig(listeners = UIAccountForm.ResetActionListener.class, phase = Phase.DECODE),
@EventConfig(listeners = UIAccountForm.SearchUserActionListener.class, phase = Phase.DECODE)
}
)
...
Create an execute
method inside this class:
public void execute(Event<ParentClass> event) throws Exception
This method is called every time the listener gets an event from the client. Hence you can process this event in it, using the even attribute. Use it to get parameters from a form in your client, to modify the status of your portlet, etc.,
Possible ways to use the event attribute :
String value = event.getRequestContext().getRequestParameter("name"); // to get a value from a form
ParentClass parent = event.getSource(); // to get the parent object (the portlet that threw and caugth the event)
UIMyComponent portal = parent.getAncestorOfType(UIMyComponent.class); // to get any node in the hierarchy of UIComponents
If your action has to update an element on your client's interface, you must call addUIComponentToUpdateByAjax() at the end of the execute
method:
event.getRequestContext().addUIComponentToUpdateByAjax(uicomponent);
The target component must be provided as parameter (the component that will be updated). We will come back on this later.
You must create one inner action listener class for each Ajax call you want to handle on this portlet. All these classes must be declared in the configuration annotations of the main class, otherwise you will get an error.
Done. Your portlet is ready to accept Ajax calls from your client.
Once the server is configured to receive Ajax calls, you must configure the client interface to make these calls.
Add the following code to the groovy template file associated with the appropriate portlet class ( ParentClass
here):
uicomponent.event("YourOperation");
YourOperation
is the same as in the ActionListener
class (save in our example above).
The event function will create an url starting with javascript:
. Make sure this code can be executed in the environment where the portal is running.
If your operation must update the content of a component, make sure that the target component is well rendered. The following is recommended:
uicomponent.renderChild(UITargetComponent.class) ;
The uicomponent
must be of the UITargetComponent
type as the component class;
event.getRequestContext().addUIComponentToUpdateByAjax(uicomponent) ;
will be updated when UITargetComponent
is called.
If this component is not rendered by default, set its rendered
attribute (in the constructor of the portlet) to false when the portlet loads:
mycomponent.setRendered(false);
All javascript in is managed by the file 02eXoresources:javascript/eXo/portal/PortalHttpRequest.js
.
In this class, there are four functions/classes:
and 6 functions:
This is the main entry method for every Ajax calls to the portal It is simply a dispatcher method that completes some init
fields before calling the doRequest()
method.
This method is called instead of an HTTP POST
when, in an AJAX case, some manipulations are needed. Once the content of the form is placed into a string object, the call is delegated to the doRequest()
method
The doRequest()
method takes incoming request from ajaxGet
and ajaxPost
calls. The second argument is the URL to target on the server The third argument is the query string object which is created out of a form element, this value is null except when there is a POST request.
An AjaxRequest
object is instantiated. It holds the reference to the XHR method.
An HttpResponseHandler
object is instantiated and its methods (ajaxResponse
, ajaxLoading
, ajaxTimeout
) are associated with the one from the AjaxRequest
and will be called by the XHR during the process method.
Cancels the current request.
Allows to create and execute a sync or async GET request.
A simple javascript redirection with window.location.href
that are the entry points of these classes. These functions need not be called explicitly, as the template file and the portlet class manage everything.
This class doesn't contain any methods. On creation, it retrieves the response elements from the xml returned by Ajax, and stores them in the corresponding attributes :
The component ID.
The component title.
The mode the component runs in. The options are: View, Edit, Help or Config.
The processing state of the component. The options are: Decode, Render.
The updated data to put in the component.
The javascript code to update the component.
An array containing the containers to update with this script.
Attributes can be accessed by calling them from the PortletResponse
instance.
This is an array of PortletResponse
s (portletResponses
) and two other attributes:
Data to update.
Javascript code to update.
By far the most important class of this file. It makes the XMLHttpRequest
object easier to use by wrapping it with functions and attributes. You can find further documentation on this class here.
This class provides methods to handle the Ajax response.
Used to execute javascript code.
Updates html
components.
This function is called when the timeout of the ajax call exceeds its set limit.
This function creates a PortalResponse
object from the data in the Ajax request.
This function shows the loading pop-up and mask layer.
{PortalResponse} | |--->{PortletResponse} | |--->{PortletResponse} | |-->{portletId} | |-->{portletTitle} | |-->{portletMode} | |-->{portletState} | | | |-->{Data} | | | | | |--->{BlockToUpdate} | | | |-->{blockId} | | | |-->{data} | | | | | |--->{BlockToUpdate} | |--->{Script} | |--->{Data} | | | |--->{BlockToUpdate} | | |-->{blockId} | | |-->{data} | | | |--->{BlockToUpdate} |--->{Script}
If there are several actions that need to appear in a pop-up, the following technique can be used to manage the different pop-up windows:
Create a UIPopupAction
in the main portlet class:
addChild(UIPopupAction.class, null, null);
Render this action in your template file:
uicomponent.renderChild(UIPopupAction.class) ;
(This creates an empty container (pop-up) that will receive the new content by Ajax)
Put this component in your action listener class and update its content:
UIPopupAction uiPopupAction = uiMainPortlet.getChild(UIPopupAction.class) ; uiPopupAction.activate(UIReferencesList.class, 600) ;
UIReferenceList
is the component that will appear in the pop-up. It does not have to declared in the main portlet class.
The activate method creates the component and its rendering in the pop-up window.
Allow this component to be updated by Ajax:
event.getRequestContext().addUIComponentToUpdateByAjax(uiPopupAction) ;
Add an action listener class (and repeat the steps above with the appropriate component type) for each component required to appear in a pop-up window .
In addition to the content in this chapter, refer also to Section 6.2, “AJAX Framework” in order to better understand the communication between the Groovy Template and the portlet.
The structure of a template is quite simple. It consists of only two elements:
The HTML code and;
zero or more groovy language code blocks, enclosed by <% ... %> enclosures.
The HTML code in the template doesn't have to contain the html
, or body
tags. This allows groovy templates for one component to also be rendered in another component.
For example:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <% import org.exoplatform.webui.core.UIComponent; def currentPage = uicomponent.getCurrentPage(); ... %> ... <div class="$uicomponent.skin" id="UIPortalApplication"> <%uicomponent.renderChildren();%> </div>
Groovy is a scripting language for Java. Some usage examples are included in this section, however further information is available at http://groovy.codehaus.org/Documentation .
Some examples of Groovy
int min = 1; def totalPage = uicomponent.getAvailablePage(); String name = "uiPortlet"; categories = uicomponent.getItemCategories(); String columns = uicomponent.getColumns();
for(category in categories) { ... } for(i in min..max) { ... } // min and max are int variables println "</div>" ; println """ <div class="Item"> <div class="OverflowContainer"> """; <%=uicomponent.getToolbarStyle();%> // <%= to avoid a call of println method import org.exoplatform.portal.config.model.PageNode;
The configuration of a portlet is partly made with ComponentConfig
annotations (others are ComponentConfigs
, EventConfig
, etc).
One of the parameters of this annotation is template
. This is where the path to the template file associated with this portlet is defined.
To specify this parameter, add this statement to the configuration annotation.
For example, in src:/portlet/exoadmin/src/main/java/org/exoplatform/applicationregistry/webui/component/ is the UIApplicationForm.java file:
@ComponentConfig( lifecycle = UIFormLifecycle.class, template = "system:/groovy/webui/form/UIFormWithTitle.gtmpl", events = { @EventConfig(listeners = UIApplicationForm.SaveActionListener.class), @EventConfig(phase = Phase.DECODE, listeners = UIApplicationForm.CancelActionListener.class) } )
The path is in the "system
" namespace. This is a reference to the portal webapp.
This webapp contains reusable groovy templates in the folder; src:/web/portal/src/main/webapp/groovy/webui/form/ .
Use the following steps to create a new template;
Create a groovy file in the appropriate webbapp and refer to it.
Use the namespace "app" for refering to the same webapp as the component.
stores the component templates in a folder that follows this placement convention: "/webapp/groovy/
".
your_portlet_name
/webui/component
template = "app:/groovy/your_portlet_name/webui/component/your_component.gtmpl"
Edit the template file according to the information in the Section 6.3.3.2, “The template file”.
Component template files are composed in HTML code and groovy code blocks. There are a few things more that you need to know to fully link your portlet with your template.
To successfully link a portlet with a template, please ensure the following:
If the template defines the UI of a component, use the java object variable uicomponent
to access this component instance.
This should be the case in most instances, but it is recommended that the java class inherits from UIComponent
are checked before this variable is used.
Using the uicomponent
variable, you can access all the attributes and functions of a component to use them in your template.
Take, for example UIPageIterator.gtmpl
:
<% def currentPage = uicomponent.getCurrentPage(); %> ... <a href="<%=uicomponent.event("ShowPage","$currentPage")%>" class="Icon LastTopPageIcon"> <span></span> </a>
The following example shows how uicomponent
can be used to make Ajax calls using the event
method. See Section 6.2, “AJAX Framework” for more details.
Another variable available is ctx
. This variable gives access to the context in which the template is processed.
Use this variable to retrieve elements such as; the request, the Javscript manager or the resource resolver (ctx.appRes
).
Some usage examples are:
<% def rcontext = ctx.getRequestContext() ; context.getJavascriptManager().importJavascript('GateIn.webui.UIPopupWindow'); ctx.appRes(popupId + ".title."+ title); %>
If the template defines the user interface of a component that includes a form use an instance of UIForm
in a variable named uiform
.
The UIForm
class provides the methods begin()
and end()
which write the HTML tags of the form.
The form class must inherit from UIForm
. In this class add the input elements (fields, checkboxes, lists) which are required in the form.
Render the input elements in the groovy template using uiform.renderField(field)
.
This chapter does not to refer to the Portlet API specification lifecycle but focuses on the UI framework.
This web framework has been developed specifically for and, while it is not necessary to use the native web framework to build portlets, all portlets packaged with are developed using this framework.
This consistency allows portlets to use several UI components that can be used in different abstracted contexts (such as the portal itself or some portlets).
This chapter is intended for advanced developers. It covers code implementation and logic. It is not a tutorial on how to write portlets.
Refer to Section 5.1, “Portal Lifecycle” for information on concepts that are similar and top hierarchy classes that are shared.
The main entry point for configuring a portlet is in the portlet.xml file located in the portlet application WAR.
Each portlet built using the web framework must reference the PortletApplicationController .
The portlet configuration (such as the root component) is defined in configuration.xml
. The path to this file is defined in the init-param "webui.configuration" of portlet.xml
.
<portlet> <description xml:lang="EN">Content Portlet</description> <portlet-name>ContentPortlet</portlet-name> <display-name xml:lang="EN">Content Portlet</display-name> <portlet-class>org.exoplatform.webui.application.portlet.PortletApplicationController</portlet-class> <init-param> <name>webui.configuration</name> <value>/WEB-INF/conf/portlet/content/ContentPortlet/webui/configuration.xml</value> </init-param> </portlet>
The structure of the configuration.xml
file is exactly the same as the webui-configuration.xml
which was introduced in Section 5.1, “Portal Lifecycle”.
In the case of the content portlet it is formatted as:
<webui-configuration> <application> <ui-component-root>org.exoplatform.content.webui.component.UIContentPortlet</ui-component-root> <state-manager>org.exoplatform.webui.application.portlet.ParentAppStateManager</state-manager> </application> </webui-configuration>
The PortletApplicationController
class extends the GenericPortlet
class defined in the Portlet API specification.
All methods (like processAction()
or render()
) are delegated to the PortletApplication
. The creation and caching inside the WebController
object is shown in the example below:
/** * try to obtain the PortletApplication from the WebAppController. * * If it does not exist a new PortletApplication object is created, init and cached in the * controller */ private PortletApplication getPortletApplication() throws Exception { PortalContainer container = PortalContainer.getInstance() ; WebAppController controller = (WebAppController)container.getComponentInstanceOfType(WebAppController.class) ; PortletApplication application = controller.getApplication(applicationId_) ; if(application == null) { application = new PortletApplication(getPortletConfig()) ; application.onInit() ; controller.addApplication(application) ; } return application ; }
When a portlet using the native web framework is deployed in , all methods calls go through the PortletApplication
object which extends the WebuiApplication
.
The code of the method in PortletApplication
is described below. The business logic is shown in the javadoc:
/** * The processAction() method is the one modelled according to the Portlet API specification * * The process is quite simple and here are te different steps done in the method: * * 1) The current instance of the WebuiRequestContext (stored in a ThreadLocal in the class) is referenced * 2) A new request context of type PortletRequestContext (which extends the class WebuiRequestContext) is * created as a child of the current context instance * 3) The new context is place inside the ThreadLocal and hence overides its parent one there, * only for the portlet request lifeciclye * 4) The method onStartRequest() is called in all the ApplicationLifecycle objects referenced in the webui * configuration XML file * 5) The StateManager object (in case of portlet it is an object of type ParentAppStateManager) is used to get the RootComponent * also referenced in the XML configuration file * 6) The methods processDecode(UIApplication, WebuiRequestContext) and processAction(UIApplication, WebuiRequestContext) * are then called * 7) Finally, a flag, to tell that the processAction phase was done, in the context is set to true and the parent * context is restored in the Threadlocal */ public void processAction(ActionRequest req, ActionResponse res) throws Exception { WebuiRequestContext parentAppRequestContext = WebuiRequestContext.getCurrentInstance() ; PortletRequestContext context = createRequestContext(req, res, parentAppRequestContext) ; WebuiRequestContext.setCurrentInstance(context) ; try { for(ApplicationLifecycle lifecycle : getApplicationLifecycle()) { lifecycle.onStartRequest(this, context) ; } UIApplication uiApp = getStateManager().restoreUIRootComponent(context) ; context.setUIApplication(uiApp) ; processDecode(uiApp, context) ; if(!images/context.isResponseComplete() &&!images/ context.getProcessRender()) { processAction(uiApp, context) ; } } finally { context.setProcessAction(true) ; WebuiRequestContext.setCurrentInstance(parentAppRequestContext) ; } }
The PortletRequestContext
extends the WebuiRequestContext
class and acts as a wrapper on all the portlet request information:
/** * In this method we try to get the PortletRequestContext object from the attribute map of the parent * WebuiRequestContext. * * If it is not cached then we create a new instance, if it is cached then we init it with the correct * writer, request and response objects * * We finally cache it in the parent attribute map * */ private PortletRequestContext createRequestContext(PortletRequest req, PortletResponse res, WebuiRequestContext parentAppRequestContext) throws IOException { String attributeName = getApplicationId() + "$PortletRequest" ; PortletRequestContext context = (PortletRequestContext) parentAppRequestContext.getAttribute(attributeName) ; Writer w = null ; if(res instanceof RenderResponse){ RenderResponse renderRes = (RenderResponse)res; renderRes.setContentType("text/html; charset=UTF-8"); w = renderRes.getWriter() ; } if(context!images/= null) { context.init(w, req, res) ; } else { context = new PortletRequestContext(this, w, req, res) ; parentAppRequestContext.setAttribute(attributeName, context) ; } context.setParentAppRequestContext(parentAppRequestContext) ; return context; }
In the PortletApplication
, the line;
UIApplication uiApp = getStateManager().restoreUIRootComponent(context);
asks the StateManager
defined for the portlet to get the UI root component. In the case of a portlet the root component must extend UIPortletApplication
.
public class ParentAppStateManager extends StateManager { /** * This method simply delegate the call to the same method of the parent WebuiRequestContext */ @SuppressWarnings("unchecked") public UIApplication restoreUIRootComponent(WebuiRequestContext context) throws Exception { WebuiRequestContext pcontext = (WebuiRequestContext) context.getParentAppRequestContext() ; return pcontext.getStateManager().restoreUIRootComponent(context) ; }
Hence this is the PortalStateManager that will also handle the extraction of the root component.
public UIApplication restoreUIRootComponent(WebuiRequestContext context) throws Exception { context.setStateManager(this) ; WebuiApplication app = (WebuiApplication)context.getApplication() ; /* * If the request context is of type PortletRequestContext, we extract the parent context which will * allow to get access to the PortalApplicationState object thanks to the session id used as the key for the * syncronised Map uiApplications */ if(context instanceof PortletRequestContext) { WebuiRequestContext preqContext = (WebuiRequestContext) context.getParentAppRequestContext() ; PortalApplicationState state = uiApplications.get(preqContext.getSessionId()) ; PortletRequestContext pcontext = (PortletRequestContext) context ; String key = pcontext.getApplication().getApplicationId() ; UIApplication uiApplication = state.get(key) ; if(uiApplication!images/= null) return uiApplication; synchronized(uiApplications) { ConfigurationManager cmanager = app.getConfigurationManager() ; String uirootClass = cmanager.getApplication().getUIRootComponent() ; Class type = Thread.currentThread().getContextClassLoader().loadClass(uirootClass) ; uiApplication = (UIApplication)app.createUIComponent(type, null, null, context) ; state.put(key, uiApplication) ; } return uiApplication ; } }
The render method business logic is quite similar to processAction()
.
/** * The render method business logic is quite similar to the processAction() one. * * 1) A PortletRequestContext object is created (or extracted from the cache if it already exists) * and initialized * 2) The PortletRequestContext replaces the parent one in the WebuiRequestContext ThreadLocal object * 3) If the portal has already called the portlet processAction() then the call to all onStartRequest of * the ApplicationLifecycle has already been made, otherwise we call them * 4) The ParentStateManager is also used to get the UIApplication, as we have seen it delegates the call * to the PortalStateManager which caches the UI component root associated with the current application * 5) the processRender() method of the UIPortletApplucaton is called * 6) Finally, the method onEndRequest() is called on every ApplicationLifecycle referenced in the portlet * configuration XML file and the parent WebuiRequestContext is restored * */ public void render(RenderRequest req, RenderResponse res) throws Exception { WebuiRequestContext parentAppRequestContext = WebuiRequestContext.getCurrentInstance() ; PortletRequestContext context = createRequestContext(req, res, parentAppRequestContext) ; WebuiRequestContext.setCurrentInstance(context) ; try { if(!context.hasProcessAction()) { for(ApplicationLifecycle lifecycle : getApplicationLifecycle()) { lifecycle.onStartRequest(this, context) ; } } UIApplication uiApp = getStateManager().restoreUIRootComponent(context) ; context.setUIApplication(uiApp) ; if(!context.isResponseComplete()) { UIPortletApplication uiPortletApp = (UIPortletApplication)uiApp; uiPortletApp.processRender(this, context) ; } uiApp.setLastAccessApplication(System.currentTimeMillis()) ; } finally { try { for(ApplicationLifecycle lifecycle : getApplicationLifecycle()) { lifecycle.onEndRequest(this, context) ; } } catch (Exception exception){ log.error("Error while trying to call onEndRequest of the portlet ApplicationLifecycle", exception); } WebuiRequestContext.setCurrentInstance(parentAppRequestContext) ; } }
The following is the processRender()
call made on the UIPortletApplication
:
/** * The default processRender for an UIPortletApplication handles two cases: * * A. Ajax is used * ---- * If Ajax is used and that the entire portal should not be re rendered, then an AJAX fragment is * generated with information such as the portlet id, the portlet title, the portlet modes, the window * states as well as the HTML for the block to render * * B. A full render is made * ---- * a simple call to the method super.processRender(context) which will delegate the call to all the * Lifecycle components * */ public void processRender(WebuiApplication app, WebuiRequestContext context) throws Exception { WebuiRequestContext pContext = (WebuiRequestContext)context.getParentAppRequestContext(); if(context.useAjax() &&!images/pContext.getFullRender()) { Writer w = context.getWriter() ; Set<UIComponent> list = context.getUIComponentToUpdateByAjax() ; if(list!images/= null) { if(getUIPopupMessages().hasMessage()) context.addUIComponentToUpdateByAjax(getUIPopupMessages()) ; for(UIComponent uicomponent : list) { renderBlockToUpdate(uicomponent, context, w) ; } return ; } } super.processRender(context) ; }
The Java Community Process (JCP
) uses Java Specification Requests (JSR
s) to define proposed specifications and technologies designed for the Java platform.
The Portlet Specifications aim at defining portlets that can be used by any JSR-168 (Portlet 1.0) or JSR-286 (Portlet 2.0) portlet container.
Most Java EE (Enterprise Edition) portals include at least one compliant portlet container, and is no exception. In fact, includes a container that supports both versions.
This chapter gives a brief overview of the Portlet Specifications but portlet developers are strongly encouraged to read the JSR-286 Portlet Specification .
is fully JSR-286 compliant. Any JSR-168 or JSR-286 portlet operates as it is mandated by the respective specifications inside the portal.
A portal can be considered as a series of web pages with different areas within them. Those areas contain different windows and each window contains portlet:
The diagram below visually represents this nesting:
A portlet can have different view modes. Three modes are defined by the JSR-286 specification:
Generates markup reflecting the current state of the portlet.
Allows a user to customize the behavior of the portlet.
Provides information to the user as to how to use the portlet.
Window states are an indicator of how much page space a portlet consumes on any given page. The three states defined by the JSR-168 specification are:
A portlet shares this page with other portlets.
A portlet may show very little information, or none at all.
A portlet may be the only portlet displayed on this page.
The tutorials contained in this chapter are targeted toward portlet developers. It is also recommend that developers read and understand the JSR-286 Portlet Specification .
This example is using Maven to compile and build the web archive. Maven versions can be downloaded from maven.apache.org
This section describes how to deploy a portlet in . A sample portlet called SimplestHelloWorld
is located in the examples
directory at the root of your binary package. This sample is used in the following examples.
To compile and package the application:
Navigate to the SimplestHelloWorld
directory and execute:
mvn package
If the compile is successfully packaged the result will be available in: SimplestHelloWorld/target/SimplestHelloWorld-0.0.1.war
.
Copy the package file into JBOSS_HOME/server/default/deploy
.
Start JBoss Application Server (if it is not already running).
Create a new portal page and add the portlet to it.
Like other Java EE applications, portlets are packaged in WAR files. A typical portlet WAR file can include servlets, resource bundles, images, HTML, JavaServer Pages (JSP), and other static or dynamic files.
The following is an example of the directory structure of the SimplestHelloWorld
portlet:
|-- SimplestHelloWorld-0.0.1.war | `-- WEB-INF | |-- classes | | `-- org | | `-- gatein | | `-- portal | | `-- examples | | `-- portlets | |`-- SimplestHelloWorldPortlet.class | |-- po
rtlet.xml | `-- we
b.xml
![]() | The compiled Java class implementing javax.portlet.Portlet (through javax.portlet.GenericPortlet ) |
![]() | This is the mandatory descriptor files for portlets. It is used during deployment.. |
![]() | This is the mandatory descriptor for web applications. |
Below is the SimplestHelloWorldPortlet/src/main/java/org/gatein/portal/examples/portlets/SimplestHelloWorldPortlet.java
Java source:
package org.gatein.portal.examples.portlets;
import java.io.IOException;
import java.io.PrintWriter;
import javax.portlet.GenericPortlet;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;public class SimplestHelloWorldPortlet extends GenericPortlet
{
public void doView(RenderRequest request,RenderResponse response) throws IOException
{PrintWriter writer = response.getWriter();
writer.write("Hello World !");
writer.close();
}
}
![]() |
All portlets must implement the
The
|
![]() |
If only the |
![]() | Use the RenderResponse to obtain a writer to be used to produce content. |
![]() | Write the markup to display. |
![]() | Closing the writer. |
Portlets are responsible for generating markup fragments, as they are included on a page and are surrounded by other portlets. This means that a portlet outputting HTML must not output any markup that cannot be found in a <body>
element.
requires certain descriptors to be included in a portlet WAR file. These descriptors are defined by the Jave EE (web.xml
) and Portlet Specification (portlet.xml
).
Below is an example of the SimplestHelloWorldPortlet/WEB-INF/portlet.xml
file. This file must adhere to its definition in the JSR-286 Portlet Specification. More than one portlet application may be defined in this file:
<portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd" version="2.0"> <portlet> <portlet-name>SimplestHelloWorldPortlet</portlet-name> <portlet
-class> org.gatein.portal.examples.portlets.SimplestHelloWorldPortlet </portlet-class> <support
s> <mime-type>text/html</mime-type> </supports> <portlet
-info> <title>Simplest Hello World Portlet</title> </portlet-info> </portlet> </portlet-app>
![]() | Define the portlet name. It does not have to be the class name. |
![]() |
The Fully Qualified Name ( |
![]() |
The The declared MIME types must match the capability of the portlet. It allows administrators to pair which modes and window states are supported for each markup type.
This does not have to be declared as all portlets must support the
Use the |
![]() |
When rendered, the portlet's title is displayed as the header in the portlet window, unless it is overridden programmatically. In the example above the title would be |
This section discusses:
Adding more features to the previous example.
Using a JSP page to render the markup.
Using the portlet tag library to generate links to the portlet in different ways.
Using the other standard portlet modes.
The example used in this section can be found in the JSPHelloUser
directory.
Execute mvn package
in this directory.
Copy JSPHelloUser/target/JSPHelloUser-0.0.1.war
to the deploy
directory of JBoss Application Server.
Select the new JSPHelloUser
tab in your portal.
The EDIT
button only appears with logged-in users, which is not the case in the screenshot.
The package structure in this tutorial does not differ greatly from the previous example, with the exception of adding some JSP files detailed later.
The JSPHelloUser portlet contains the mandatory portlet application descriptors. The following is an example of the directory structure of the JSPHelloUser portlet:
JSPHelloUser-0.0.1.war |-- META-INF | |-- MANIFEST.MF |-- WEB-INF | |-- classes | | `-- org | | `-- gatein | | `-- portal | | `-- examples | | `-- portlets | | `-- JSPHelloUserPortlet.class | |-- portlet.xml | `-- web.xml `-- jsp |-- edit.jsp |-- hello.jsp |-- help.jsp `-- welcome.jsp
The code below is from the JSPHelloUser/src/main/java/org/gatein/portal/examples/portlets/JSPHelloUserPortlet.java
Java source. It is split in different pieces.
package org.gatein.portal.examples.portlets;
import java.io.IOException;
import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.GenericPortlet;
import javax.portlet.PortletException;
import javax.portlet.PortletRequestDispatcher;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
import javax.portlet.UnavailableException;
public class JSPHelloUserPortlet extends GenericPortlet
{
public void doView(RenderRequest request, RenderResponse response)throws PortletException, IOException
{
String sYourName = (String) request.getParameter("yourname");if (sYourName != null)
{
request.setAttribute("yourname", sYourName);
PortletRequestDispatcher prd =getPortletContext().getRequestDispatcher("/jsp/hello.jsp");
prd.include(request, response);
}
else
{
PortletRequestDispatcher prd = getPortletContext().getRequestDispatcher("/jsp/welcome.jsp");
prd.include(request, response);
}
}
...
![]() | Override the doView method (as in the first tutorial). |
![]() |
This entry attempts to obtain the value of the render parameter named |
![]() | Get a request dispatcher on a file located within the web archive. |
![]() | Perform the inclusion of the markup obtained from the JSP. |
As well as the VIEW
portlet mode, the specification defines two other modes; EDIT
and HELP
.
These modes need to be defined in the portlet.xml
descriptor. This will enable the corresponding buttons on the portlet's window.
The generic portlet that is inherited dispatches the different views to the methods: doView
, doHelp
and doEdit
.
...
protected void doHelp(RenderRequest rRequest, RenderResponse rResponse) throws PortletException, IOException,
UnavailableException
{
rResponse.setContentType("text/html");
PortletRequestDispatcher prd = getPortletContext().getRequestDispatcher("/jsp/help.jsp");
prd.include(rRequest, rResponse);
}
protected void doEdit(RenderRequest rRequest, RenderResponse rResponse) throws PortletException, IOException,
UnavailableException
{
rResponse.setContentType("text/html");
PortletRequestDispatcher prd = getPortletContext().getRequestDispatcher("/jsp/edit.jsp");
prd.include(rRequest, rResponse);
}
...
Portlet calls happen in one or two phases. One when the portlet is rendered and two when the portlet is actioned then rendered.
An action phase is a phase where some state changes. The render phase will have access to render parameters that will be passed each time the portlet is refreshed (with the exception of caching capabilities).
The code to be executed during an action has to be implemented in the processAction method of the portlet.
...public void processAction(ActionRequest aRequest, ActionResponse aResponse) throws PortletException, IOException,
UnavailableException
{String sYourname = (String) aRequest.getParameter("yourname");
aResponse.setRenderParameter("yourname", sYourname);
}
...
![]() |
|
![]() | Here the parameter is retrieved through an action URL . |
![]() |
The value of |
The help.jsp
and edit.jsp
files are very simple. Note that CSS styles are used as defined in the portlet specification. This ensures that the portlet will render well within the theme and across portal vendors.
<div class="portlet-section-header">Help mode</div>
<div class="portlet-section-body">This is the help mode, a convenient place to give the user some help information.</div>
<div class="portlet-section-header">Edit mode</div>
<div class="portlet-section-body">This is the edit mode, a convenient place to let the user change his portlet preferences.</div>
The landing page contains the links and form to call our portlet:
<%@ taglib uri="http://java.sun.com/portlet" prefix="portlet" %> <div class="portlet-section-header">Welcome !</div> <br/> <div class="portlet-font">Welcome on the JSP Hello User portlet, my name is GateIn Portal. What's yours ?</div> <br/> <div class="portlet-font">Method 1: We simply pass the parameter to the render phase:<br/> <a href="<port
let:renderURL><portlet:param name="yourname" value="John Doe"/> </portlet:renderURL>">John Doe</a></div> <br/> <div class="portlet-font">Method 2: We pass the parameter to the render phase, using valid XML: Please check the source code to see the difference with Method 1. <portlet:rende
rURL var="myRenderURL"> <portlet:param name="yourname" value='John Doe'/> </portlet:renderURL> <br/> <a href="<%= m
yRenderURL %>">John Doe</a></div> <br/> <div class="portlet-font">Method 3: We use a form:<br/> <portlet:actio
nURL var="myActionURL"/> <form action="
<%= myActionURL %>" method="POST"> <span class="portlet-form-field-label">Name:</span> <input class="portlet-form-input-field" type="text" name="yourname"/> <input class="portlet-form-button" type="Submit"/> </form> </div>
![]() | The portlet taglib, needs to be declared. |
![]() |
The first method showed here is the simplest one. |
![]() |
In this method the |
![]() |
The variable |
![]() | The third method mixes form submission and action request. Again, a temporary variable is used to put the created URL into. |
![]() | The action URL is used in HTML form. |
In the third method the action phase is triggered first then the render phase is triggered, which outputs some content back to the web browser based on the available render parameters.
In order to write a portlet using JSF a 'bridge' is needed. This software allows developers to write a portlet application as if it was a JSF application. The bridge then negotiates the interactions between the two layers.
An example of the JBoss Portlet Bridge is available in examples/JSFHelloUser
. The configuration is slightly different from a JSP application. This example can be used as a base to configure instead of creating a new application.
As in any JSF application, the file faces-config.xml
is required. It must contain the following information:
<faces-config>
...
<application>
<view-handler>org.jboss.portletbridge.application.PortletViewHandler</view-handler>
<state-manager>org.jboss.portletbridge.application.PortletStateManager</state-manager>
</application>
...
</faces-config>
The portlet bridge libraries must be available and are usually bundled with the WEB-INF/lib
directory of the web archive.
The other difference compare to a regular portlet application, can be found in the portlet descriptor. All details about it can be found in the JSR-301 specification that the JBoss Portlet Bridge implements.
<?xml version="1.0" encoding="UTF-8"?> <portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd" version="2.0"> <portlet> <portlet-name>JSFHelloUserPortlet</portlet-name> <portlet-class>javax.portlet.faces.GenericFacesPortlet</portlet-class> <supports> <mime-type>text/html</mime-type> <portlet-mode>view</portlet-mode> <portlet-mode>edit</portlet-mode> <portlet-mode>help</portlet-mode> </supports> <portlet-info> <title>JSF Hello User Portlet</title> </portlet-info> <init-param> <name
>javax.portlet.faces.defaultViewId.view</name> <value>/jsf/welcome.jsp</value> </init-param> <init-param> <name
>javax.portlet.faces.defaultViewId.edit</name> <value>/jsf/edit.jsp</value> </init-param> <init-param> <name
>javax.portlet.faces.defaultViewId.help</name> <value>/jsf/help.jsp</value> </init-param> </portlet> </portlet-app>
![]() |
All JSF portlets define |
![]() | This is a mandatory parameter to define what's the default page to display. |
![]() | This parameter defines which page to display on the 'edit' mode. |
![]() | This parameter defines which page to display on the 'help' mode. |
Create example (This one doesn't exist anymore). Overall this chapter need to be reviewed, any taker ? :)
This example is based on the testPortlet
in portal/trunk/portlet/test
.
On Eclipse (or any IDE), create a new Java Project, and create this folder tree :
src | main | |- java | |- resources | |- webapp
Create the pom.xml, at root level of the project, like this :
<project>
<parent>
<groupId>org.exoplatform.portal</groupId>
<artifactId>config</artifactId>
<version>trunk</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>org.gatein.example.portlet.testRomain</artifactId>
<packaging>war</packaging>
<version>1.0.0</version>
<name>gatein.portlets.testRomain</name>
<description>Romain Test Portlet</description>
<dependencies>
<dependency>
<groupId>org.exoplatform.portal</groupId>
<artifactId>exo.portal.webui.portal</artifactId>
<version>${org.exoplatform.portal.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.exoplatform.portal</groupId>
<artifactId>exo.portal.webui.eXo</artifactId>
<version>${org.exoplatform.portal.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>testRomain</finalName>
</build>
</project>
In java/testRomain/portlet/component/, we will create the UITestRomainPortlet.java file for the portlet :
package org.gatein.example.portlet.testRomain;
import org.exoplatform.webui.config.annotation.ComponentConfig;
import org.exoplatform.webui.core.lifecycle.UIApplicationLifecycle;
import org.exoplatform.webui.core.UIPortletApplication;
//this part is configuration of the portlet, we set the path to the template groovy.
@ComponentConfig(
lifecycle = UIApplicationLifecycle.class,
template = "app:/groovy/testRomain/portlet/UITestRomainPortlet.gtmpl"
)
public class UITestRomainPortlet extends UIPortletApplication {
public UITestRomainPortlet() throws Exception {
}
}
In src/main/webapp, create the groovy template for the portlet. The path to this file must match the path you set in the java file, in our case : groovy/testRomain/portlet/UITestRomainPortlet.gtmpl
<div id="<%=uicomponent.getId();%>"> HelloWorld </div>
Create the folder skin in src/main/webapp. We don't fill it now, but in this folder, you can put css stylesheet and images.
Create the folder WEB-INF/classes/locale in src/main/webapp. We don't fill it now, but in this folder, you can put language properties files. See Section 5.3, “Internationalization Configuration”.
Create the file configuration.xml in WEB-INF/conf/portlet/testPortletRomain/. Content of tag <ui-component-root> must match your package organization.
<webui-configuration>
<application>
<ui-component-root>testRomain.portlet.component.UITestRomainPortlet</ui-component-root>
<state-manager>org.exoplatform.webui.application.portlet.ParentAppStateManager</state-manager>
</application>
</webui-configuration>
In WEB-INF, create file portlet.xml :
<?xml version="1.0" encoding="UTF-8"?>
<portlet-app version="1.0" xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd">
<portlet>
<description xml:lang="EN">Test Portlet Romain</description>
<portlet-name>TestRomain</portlet-name>
<display-name xml:lang="EN">Test Portlet Romain</display-name>
<portlet-class>org.exoplatform.webui.application.portlet.PortletApplicationController</portlet-class>
<init-param>
<name>webui.configuration</name>
<!-- must match the path to configuration file -->
<value>/WEB-INF/conf/portlet/testPortletRomain/configuration.xml</value>
</init-param>
<expiration-cache>0</expiration-cache>
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>help</portlet-mode>
</supports>
<supported-locale>en</supported-locale>
<resource-bundle>locale.testRomainPortlet</resource-bundle>
<portlet-info>
<title>TestPortletRomain</title>
<short-title>TestPortlet</short-title>
<keywords>test</keywords>
</portlet-info>
</portlet>
</portlet-app>
In WEB-INF, create file web.xml :
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<!-If define the Portlet Application name MUST end with .par->
<display-name>test</display-name>
<description> This application is a portlet. It can not be used outside a portal.
This web.xml file is mandatory in each .par archive file. </description>
<listener>
<listener-class>org.exoplatform.services.portletcontainer.impl.servlet.PortletApplicationListener</listener-class>
</listener>
<servlet>
<servlet-name>PortletWrapper</servlet-name>
<servlet-class>org.exoplatform.services.portletcontainer.impl.servlet.ServletWrapper</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>PortletWrapper</servlet-name>
<url-pattern>/PortletWrapper</url-pattern>
</servlet-mapping>
</web-app>
Compile your portlet, deploy it, and add it to the portal.
Now, we will add a button in the portlet. This button will open a popup with a message inside.
In the groovy template, add this code :
<div class="UIAction"> <div class="ActionContainer"> <div class="ActionButton"> <div class="LightBlueStyle"> <div class="ButtonLeft"> <div class="ButtonRight"> <div class="ButtonMiddle"> <a href="<%=uicomponent.event("OpenPopup", "")%>">Open Popup</a> </div> </div> </div> </div> </div> </div> </div>
In the java file, in @ComponentConfig, add this code :
events = {
@EventConfig(listeners = UITestRomainPortlet.OpenPopupActionListener.class)
}
Remark : XXXActionLister.class XXX must match the name you set for the event in the groovy.
static public class OpenPopupActionListener extends EventListener<UITestRomainPortlet> {
public void execute(Event<UITestRomainPortlet> event) throws Exception {
System.out.println("HelloWorld");
}
}
Now, we will add a popup which say "HelloWorld" when you click on the button.
First, create the groovy template of the popup : in webapp/groovy/testRomain/portlet, create UIHelloWorldPopupContent.gtmpl :
<div id="<%=uicomponent.getId();%>"> HelloWorld in a popup </div>
In java/testRomain/portlet/component, create the java file for the popup look like : {code} package testRomain.portlet.component;
package testRomain.portlet.component;
import org.exoplatform.webui.config.annotation.ComponentConfig;
import org.exoplatform.webui.core.UIComponent;
import org.exoplatform.webui.core.lifecycle.UIApplicationLifecycle;
@ComponentConfig(
lifecycle = UIApplicationLifecycle.class,
template = "app:/groovy/testRomain/portlet/UIHelloWorldPopupContent.gtmpl"
)
public class UIHelloWorldPopupContent extends UIComponent {
public UIHelloWorldPopupContent() throws Exception {
}
}
In UITestRomainPortlet.java, we will create the popup at the portlet creation (in the constructor) :
public UITestRomainPortlet() throws Exception {
UIPopupWindow popup = addChild(UIPopupWindow.class, null, null);
popup.setWindowSize(400, 300);
UIHelloWorldPopupContent popupContent = createUIComponent(UIHelloWorldPopupContent.class, null, null);
popup.setUIComponent(popupContent);
popup.setRendered(false);
At the beginning, we set the popup not visible. As you see, we add a children to the Portlet. So, if we want to see the content of it, we must add this in UITestPortletRomain.gtmpl :
<% uicomponent.renderChildren(); %>
This makes the portlet generate the content of all child components.
Change the treatment of the event, replace the println by :
public static class OpenPopupActionListener extends EventListener<UITestRomainPortlet> {
public void execute(Event<UITestRomainPortlet> event) throws Exception {
UITestRomainPortlet portlet = event.getSource();
UIPopupWindow popup = portlet.getChild(UIPopupWindow.class);
popup.setRendered(true);
popup.setShow(true);
}
}
When user clicks on the button, the popup is shown.
Redeploy the portlet and click on the button. You will see "HelloWorld in a popup" in a popup. If you don't change in the portlet, try to redeploy and reboot the tomcat server.
A gadget is a mini web application, embedded in a web page and running on an application server platform. These small applications help users perform various tasks.
supports gadgets such as: Todo gadget, Calendar gadget, Calculator gadget, Weather Forecasts and and RSS Reader.
Default Gadgets:
The calendar gadget allows users to switch easily between daily, monthly and yearly view and, again, is customizable to match your portal's theme.
This application helps you organize your day and work group. It is designed to keep track of your tasks in a convenient and transparent way. Tasks can be highlighted with different colors.
This mini-application lets you perform most basic arithmetic operations and can be themed to match the rest of your portal.
An RSS reader, or aggregator, collates content from various, user-specified feed sources and displays them in one location. This content can include, but isn't limited to, news headlines, blog posts or email. The RSS Reader gadget displays this content in a single window on your Portal page.
Further gadgets can be obtained from the Google Gadget site. is compatible with most of the gadgets available here.
The following sections require more textual information.
After referencing the gadget successfully, then import it into the local repository.
recommends using two virtual hosts for security. If the gadget is running on a different domain than the container (the website that 'contains' the app), it is unable to interfere with the portal by modifying code or cookies.
An example would hosting the portal from http://www.sample.com and the gadgets from http://www.samplemodules.com.
To do this, configure a parameter called gadgets.hostName. The value is the path/to/gadgetServer in GadgetRegisteryService
:
<component> <key>org.exoplatform.application.gadget.GadgetRegistryService</key> <type>org.exoplatform.application.gadget.jcr.GadgetRegistryServiceImpl</type> <init-params> <value-param> <name>gadgets.hostName</name> <description>Gadget server url</description> <value>http://localhost:8080/GateInGadgetServer/gadgets/</value> </value-param> </init-params> </component>
It is also possible to have multiple rendering servers. This helps to balance the rendering load across multiple servers.
When deploying on the same server ensure the gadget initiates before anything that calls it (for example; the webapp GateInGadgets
which uses org.exoplatform.application.gadget.GadgetRegister
).
A file called key.txt has to be generated for every installation of to be secure. This file contains a secret key used to encrypt the security token used for authenticating the user.
In Linux systems this file can be generated with:
dd if=/dev/random bs=32 count=1 | openssl base64 > /tmp/key.txt
These servers have to be on the same domain as the gadget server. You can configure the container in eXoGadgetServer:/WEB-INF/classes/containers/default/container.js
.
"gadgets.content-rewrite" : { "include-urls": ".*", "exclude-urls": "", "include-tags": ["link", "script", "embed", "img", "style"], "expires": "86400", "proxy-url": "http://localhost:8080/eXoGadgetServer/gadgets/proxy?url=", "concat-url": "http://localhost:8080/eXoGadgetServer/gadgets/concat?" },