Create a Custom Product
Create a Custom Product
1 Objective
This article explains how to create a custom product. As you know eXo platform provides several products which can be integrated in one final product and used by end users. The example custom product of this tutorial is composed by eXo Portal, eXo DMS and eXo CS.2 Prerequisites
You should know javascript, maven, pom files and internationalization. You should have used the eXoBuild command. You should be familiar with the configuration of eXo products. You should have the corresponding structure for exoBuild on your computer, that means Maven, jdk1.5 etc. should be prepared. The best preparation is to build an eXo product from sources?. This article refers to your eXo path using .../eXoProjects/ .3 Find a Project Name
If you create a customer project product for eXo platform use the a name of a Greek mythological figure Take the next name in this list: http://en.wikipedia.org/wiki/List_of_Greek_mythological_figures#Other_deities Transform it in lower-case letters. This example project is called "achilles", whenever you see "achilles" in the configuration sample you must replace it by your project name. In this article both terms product and project refer to ~~achilles ~~ (that means the customized eXo product you are configuring and preparing). Create your base folder:.../eXoProjects/achilles/trunk
The "trunk" folder always contains the current development state. If you create later versions you can create branches which are called "2.1" for example, the folder would be called "eXoProjects/achilles/branches/2.1". For this configuration we use "trunk" for the folder name, and "1.0" for the version number. Later versions would use for example "2.1" in both cases.
4 Create Pom Files
The precise versions of the products are Portal 2.5.2, DMS 2.3, CS 1.2RC1. As maven depends on pom files we have to create two pom files.4.1 Root-Pom
Copy the pom.xml of the portal in the version you are going to use:*http://svn.exoplatform.org:3840/projects/portal/tags/2.5.2/pom.xml*
into
eXoProjects/achilles/trunk
We call this pom "root-pom". Adapt the root-pom
Delete all modules except "web/portal"; modify the groupId, name and version.
<groupId>org.exoplatform.achilles</groupId> <artifactId>config</artifactId> <name>eXo achilles Config</name> <version>1.0</version>
<org.exoplatform.achilles.version>1.0</org.exoplatform.achilles.version> <org.exoplatform.dms.version>2.3</org.exoplatform.dms.version> <org.exoplatform.cs.version>1.2-RC1</org.exoplatform.cs.version>
4.2 Portal-Pom
Copy the pom.xml.of your portal version :http://svn.exoplatform.org:3840/projects/portal/tags/2.5.2/web/portal/pom.xml
into
eXoProjects/achilles/trunk/web/portal/
We call this pom "portal-pom". Adapt the portal-pom
Adapt the following (modelVersion is always 4.0.0 and the packaging remains "war"):
<modelVersion>4.0.0</modelVersion> <artifactId>achilles.web.portal</artifactId> <packaging>war</packaging> <version>${org.exoplatform.achilles.version}</version> <name>achilles web module</name>
Add the dependencies of the products you want to integrate, the order is important. The dependency section uses references to the version properties of the root-pom:
<dependencies> <dependency> <groupId>org.exoplatform.portal</groupId> <artifactId>exo.portal.web.portal</artifactId> <version>${org.exoplatform.portal.version}</version> <type>war</type> <scope>runtime</scope> </dependency> <dependency> <groupId>org.exoplatform.ecm.dms.core</groupId> <artifactId>exo.ecm.dms.core.web.portal</artifactId> <version>${org.exoplatform.ecm.dms.version}</version> <type>war</type> <scope>runtime</scope> </dependency> <dependency> <groupId>org.exoplatform.cs</groupId> <artifactId>exo.cs.web.portal</artifactId> <version>${org.exoplatform.cs.version}</version> <type>war</type> <scope>runtime</scope> </dependency> </dependencies>
The files of each products are copied to your target product. In some cases Portal, DMS, and CS use the same folders and files name, which causes name conflicts. This is especially true for configuration and internationalization files. In case of name conflicts the files of the first dependency take priority over the later files. Add an empty build. By doing so the build of Maven's super-pom is not used:
<build> </build>
5 Javascript Configuration
The build process of eXo product involves configuration of javascript files. This is a little bit weird, but there is no way for you to circumvent this javascript configuration.5.1 Create a product javascript
The product javascript file for achilles is called "trunk.js" because it serves for the trunk version of achilles. The day you have several versions you will have files called "2.0.js" or "2.3.js". This file uses the method Module.GetModule(). This method is provided in a different file called "modules.js" that we modify in a later step. Create a new folder:.../eXoProjects/tools/trunk/build/src/main/javascript/eXo/products/achilles/trunk.js
This file is used by eXoBuild.bat. For example if you specify --version=2.0 the file "2.0.js" is used. If no version is specified "trunk.js" is used. You can glimpse at the final trunk file? Look for the product javascript files of the products you want to integrate and "merge" them. In our case that means we use:
/eXoProjects/tools/trunk/build/src/main/javascript/eXo/products/portal/2.5.2.js /eXoProjects/tools/trunk/build/src/main/javascript/eXo/products/dms/2.3.js /eXoProjects/tools/trunk/build/src/main/javascript/eXo/products/cs/1.2-RC1.js
Adapt the "product section" using the configuration for "achilles", there are some extra configuration for eXo DMS so we copy them from "products/dms/2.3.js" (info: serverPluginVersion is the version of the eXo Portal, we take the highest portal version of the three products)
product.name = "achilles" ; product.portalwar = "portal.war" ; product.codeRepo = "achilles/trunk" ; product.serverPluginVersion = "2.5.2" product.useContentvalidation = true; product.contentvalidationVersion = "2.3"; product.workflowVersion = "1.0";
var tool = Module.GetModule("tools/trunk") ;
var kernel = Module.GetModule("kernel/tags/2.0.6") ;
var core = Module.GetModule("core/tags/2.1.4") ;
var ws = Module.GetModule("ws/tags/1.3.2");
var eXoPortletContainer = Module.GetModule("portlet-container/tags/2.0.5", {kernel : kernel, core : core}) ;
var eXoJcr = Module.GetModule("jcr/tags/1.10.2", {kernel : kernel, core : core, ws : ws}) ;
var portal = Module.GetModule("portal/tags/2.5.2", {kernel : kernel, ws:ws, core : core, eXoPortletContainer : eXoPortletContainer, eXoJcr : eXoJcr });
var dms = Module.GetModule("ecm/dms/tags/2.3", {kernel : kernel, core : core, eXoPortletContainer : eXoPortletContainer, ws : ws, eXoJcr : eXoJcr, portal : portal});
var cs = Module.GetModule("cs/tags/1.2RC1", {kernel : kernel, ws : ws, core : core, eXoPortletContainer : eXoPortletContainer, eXoJcr : eXoJcr, portal : portal});
var achilles = Module.GetModule("achilles/trunk", {kernel : kernel, core : core, eXoPortletContainer : eXoPortletContainer, eXoJcr : eXoJcr, portal : portal});
We merge the "product.addDependencies" section. Only delete the doubled entries.
Each "addDependency" adds a webapp in your custom product.
Put in comments all portal dependencies:
/* product.addDependencies(portal.web.portal) ; */ /* product.addDependencies(dms.web.dmsportal); */ /* product.addDependencies(cs.web.csportal) ; */
product.addDependencies(achilles.web.portal) ;
product.module = achilles ;
product.dependencyModule = [tool, kernel, core, eXoPortletContainer,
ws, eXoJcr, portal, dms, cs];product.removeDependency(new Project("javax.mail", "mail", "jar", "1.4"));
...5.2 Create a module.js
.../eXoProjects/tools/trunk/build/src/main/javascript/eXo/modules/achilles/trunk/module.js
eXo.require("eXo.projects.Module") ;
eXo.require("eXo.projects.Product") ;
function getModule(params) {
return module;
}var jcr = params.eXoJcr; var portal = params.portal;
var module = new Module(); module.version = "1.0" ; module.relativeMavenRepo = "org/exoplatform/achilles" ; module.relativeSRCRepo = "achilles/trunk" ; module.name = "achilles" ;
module.web = {};
module.web.portal =
new Project("org.exoplatform.achilles", "exo.achilles.web.portal",
"exo-portal", module.version).
addDependency(portal.web.eXoResources) .
addDependency(portal.web.eXoMacSkin) .
addDependency(portal.web.eXoVistaSkin) .
addDependency(portal.webui.portal) .
addDependency(jcr.frameworks.command) .
addDependency(jcr.frameworks.web);mvn clean install
6 Adapt configuration.xml
As you learned when defining the pom files you can impose your own configuration. In the case of the configuration.xml file each product provides its own file but only the configuration of the portal is copied. You need to merge the configuration of all products. Create new a folder:.../eXoProjects/achilles/trunk/web/portal/src/main/webapp/WEB-INF/conf
The ResourceBundleService is defined two times in two different configuration files. In order to reduce confusion, let's delete the doubled configuration. Copy common-configuration.xml from portal and delete the Resource Bundle part:
<key>org.exoplatform.services.resources.ResourceBundleService</key> <type>org.exoplatform.services.resources.jcr.ResourceBundleServiceImpl</type> ...
7 Adapt web.xml
Copy the web.xml of DMS (starting with DMS because it contains also portal) to:eXoProjects/achilles/trunk/web/portal/src/main/webapp/WEB-INF/web.xml
8 First Launch
Launch exoBuild (do not forget to prepare the environment variables using "exoenv.bat" or "source exoenv.sh") :exobuild --product=achilles --exclude=all --build --deploy
.../exo-working/exo-tomcat/bin/eXo.bat run
9 Navigation
Create your own navigation: Copy all group nagivations of the DMS (because this is the most complete navigation and it already contains the portal navigation) to achilles:.../eXoProjects/achilles/trunk/web/portal/src/main/webapp/WEB-INF/conf/portal/group
.../eXoProjects/portal/tags/2.5.2/web/portal/src/main/webapp/WEB-INF/conf/portal/group/platform/users/navigation.xml Rebuild (using exobuild) the project and see if everything works fine. If you accidentally declare the same page two times you will see an error in your tomcat console.
10 Internationalization Issues
You are seeing that the menu items of the navigation are not properly shown in English There are only the keys (like "platform.users.collaboration.forum") that are used by internationalization (also called i18n). The translation is usually done in locale files. These files contain properties, that means key-value pairs. These files are also copied to your final product and nearly everything goes smoothly but some navigation node translations have got lost. The reason is that all three products define navigation nodes for the same user group. The group concerned is the "platform/users" group. The English translation of the navigation nodes of this group are defined in.../eXoProjects/portal/tags/2.5.2/web/portal/src/main/webapp/WEB-INF/classes/locale/navigation/group/platform/users_en.properties
If you look into the same folder of DMS and CS you will find similar locale files. In your custom product only the users_en.properties of portal is copied, because this is the first product in the dependency list in the portal-pom. The files of the other products are ignored (please read ahead to understand the presence of the xml files in the same folder). Merge the locale files
DMS has the most complete translations, therefore copy the whole locale folder tree of DMS to your custom product. By doing so the you define an adapted configuration for Achilles:
.../eXoProjects/achilles/trunk/web/portal/src/main/webapp/WEB-INF/classes/locale/**
CS uses a locale key structure in users.properties that is not possible to transform for the .xml file:
platform.users.collaboration=Collaboration Suite platform.users.collaboration.forum=Forum Application platform.users.collaboration.contact=Contact Application ...
.../eXoProjects/achilles/trunk/web/portal/src/main/webapp/WEB-INF/conf/portal/group/platform/users/navigation.xml
<node> <uri>collaboration</uri> <name>collaboration</name> <label>#{platform.users.collaboration}</label> ...
http://wiki.exoplatform.com/xwiki/bin/edit/Products+Interop/Create+a+Custom+Product .../eXoProjects/portal/tags/2.5.2/web/portal/src/main/webapp/WEB-INF/classes/locale/navigation/portal/classic_en.properties You remember that you copied the locale files from DMS to Achilles. Therefore you must add the property "portal.classic.webexplorer" to this file:
../eXoProjects/achilles/trunk/web/portal/src/main/webapp/WEB-INF/classes/locale/navigation/portal/classic_en.xml Rebuild your project and nearly everything should work fine.
11 Tripled Folders
There are three folders for each user, and two folders for each group.11.1 Tripled User Folders
This is due to the ecm.new.user.event.listener service. This service listens for new users and creates automatically user folders in the Collaboration Center drive.- In this drive open the users folder. You will see that 3 folders have been created for each user (distinguished by numbering [1] or [2]).
| Product | Config Path | Component/Service | Type |
|---|---|---|---|
| Portal | .../WEB-INF/conf/jcr/component-plugins-configuration.xml | org.exoplatform.services.jcr.ext. hierarchy.NodeHierarchyCreator | org.exoplatform.services.jcr.ext. hierarchy.impl.AddPathPlugin |
| DMS | .../WEB-INF/conf/dms/organization-component-plugins-configuration.xml | org.exoplatform.services. organization.OrganizationService | org.exoplatform.services. jcr.ext.hierarchy.impl.NewUserListener |
| CS | .../WEB-INF/conf/cs/jcr-component-plugins-configuration.xml | org.exoplatform.services. organization.OrganizationService | org.exoplatform.services. jcr.ext.hierarchy.impl.NewUserListener |
<component-plugin> <name>ecm.new.user.event.listener</name> <set-method>addListenerPlugin</set-method> <type>org.exoplatform.services.jcr.ext.hierarchy.impl.NewUserListener</type> ...
11.2 Doubled Group Folders
The drive.new.group.event.listener listens for new groups and creates automatically group folders in the Collaboration Center drive.- In this drive open the groups folder. You will see that 2 folders have been created for each group, (distinguished by numbering [1]).
<component-plugin> <name>drive.new.group.event.listener</name> <set-method>addListenerPlugin</set-method> <type>org.exoplatform.services.cms.drives.impl.NewGroupListener</type> ...