Implementing Multiple Project Customisations in one Alfresco instance

Introduction

Working on an Alfresco project for a client I experienced a number conflicts when deploying the project on an existing Alfresco server where other customisation projects were already deployed.

The conflicts occurred primarily on the share instance when common configuration files were used, but also on the repository instance.

The conflicts that occurred were:

  1. A content model was not loaded because the same bean-id was used in two different projects.
  2. Only viewable aspects defined for project A was visible, not those defined for project B
  3. When selecting “change type” only types defined in project A was available, not those defined in project B

I am sure more conflicts which we haven’t discovered might occur. In this post I will describe the nature of the conflicts we did discover and how they may be avoided.

Research

In order to support and verify the recommendations in this post I have created 5 customisation projects. In these projects I have done some “standard” customisations. In the repository projects I have created customised content models. In the share projects I have added dashlets, pages and sites. These projects are all deployed on the same Alfresco server instance on which both alfreso.war and share.war are deployed. The table below shows a resume of the customisations done in each projects. The customisations are described in further details in the blog:

Project / Customisation

Custom Repo A

Custom Share A

Custom Repo B

Custom Share B

Master Share

Content Model

folderA extending cm:folder with
mandatory aspect A

 

documentB extending cm:content
with mandatory aspect B

 

 

custom-slingshot-[project]-context.xml

 

Resource
BundleBootstrap
Component=
alfresco.message.
customA

ConfigBootStrap=
share-
config-customA.xml

 

Resource
BundleBootstrap
Component=
alfresco.message.customB

ConfigBootStrap=
share-
config-customB.xml

 

share-[project]-config.xml

 

DocumentLibrary
subtype=folderA

visible
aspects=emailserver:aliasable

 

DocumentLibrary
subtype=documentB

visible
aspects=cm:generalclassifiable

 

Dashlet:

 

HelloWorld

http://code.google.com/p/share-extras/

 

 

MultipleRSS

http://code.google.com/p/share-extras/

 

 

Site:

1. presets.xml

2. create-site.get.js

3. <msg>.properties

 

 

 

 

customA.properties
title.CustomASite=…

 

 

 

 

customB.properties
title.CustomBSite=…

 

presets.xml

create-site.get.js

 

Page

 

Site-data/pages/
pageA.xml

 

site-data/template-instances/
templatePageA.xml

 

template/org/myCompany/pageA.ftl

 

site-data/components/page.content.pageA.xml

 

Site-data/pages/
pageB.xml

 

site-data/template-instances/
templatePageB.xml

 

template/org/myCompany/pageB.ftl

 

site-data/components/page.content.pageB.xml

 

Customising Alfresco Repository

Bean naming convention

The conflict that occurred when deploying multiple repository customisations (conflict #1 described above) was due to improper naming convention.

In order to avoid these errors in the future we agreed to prefix bean id’s with myCompany.myProject.

It would be natural to name the content model myCompany.myProject:model. This is unfortunately not possible. Alfresco will show an error in the log when you try to submit changes via the edit metadata form (WARN [processor.node.NodeFormProcessor] Ignoring unrecognised field ‘prop_myCompany#dot#CustomB_myName’) and the metadata will not be persisted.

For our models we have instead adopted the following naming convention: myCompanyCustom:model

Customising Share

In this section I will list some best practices for customising share still with focus on avoiding conflicts between different customisation projects

custom-slingshot-application.xml

Instead of overriding the standard custom-slingshot-application.xml you should create a project specific custom-slingshot-[project].xml configuration file for defining your beans.

share-config-custom.xml

Customising Share is for a great part done in the file share-config-custom.xml but rather than using this file I would recommend to create a project specific version (share-[project]-config.xml) and load this file from within custom-slingshot-[project].xml. This is illustrated below:

<!– Override the config source to include Web Framework Commons and Share config –><bean id=“customA.custom.config” class=“org.springframework.extensions.config.ConfigBootstrap” init-method=“register”>
<
property name=“configService” ref=“web.config”/>
<
property name=“configs”>
<
list>
<
value>classpath:alfresco/web-extension/share-config-customA.xml</value>
</
list>
</
property>

</
bean>

The most import thing to remember when defining your share-[project]-config.xml is not to specify the replace=”true” attribute in the <config> element. This attribute will cause your configuration to override similar configuration specified in other customisation projects. This was the cause of both conflict #2 and conflict #3 described above.

Dashlets

A dashlet typically consist of several parts: web-tier web scripts and repository-tier web scripts. The recommendation here is to follow the guidelines from the Sample Project http://code.google.com/p/share-extras/wiki/SampleProject

custom developed web-tier web scripts are placed in:

  • alfresco/site-webscripts[/org/myCompany/myProject ]

existing web-tier scripts that you want to override are placed here:

  • alfresco/web-extension/site-webscripts[/org/myCompany/myProject ]

For repository-tier web scripts, place scripts under

custom developed repository web scripts are placed in:

  • alfresco/templates/webscripts[/org/myCompany/myProject]

existing repository scripts that you want to override are placed in:

  • alfresco/extension/templates/webscripts [/org/myCompany/myProject]

Furthermore a dashlet component may consist of additional resources (js, css, images, etc.). These can be located in:

  • share[/org/myCompany/myProject ]

In this case you should link to the resources from you freemarker template using the syntax:

<img src="${url.context}/res/myCompany/myProject/components/dashlets/image.png" alt="Image description here" />

Sites

Defining a site in Alfresco involves customising three files. These are:

presets.xml ../tomcat/shared/classes/alfresco/web-extension/site- data/presets

create-site.get.js ../tomcat/shared/classes/alfresco/web-extension/site- webscripts/org/alfresco/modules

<msg>.properties (configurable location)

You will notice that presets.xml and create-site.get.js are located in the shared directory and it is unfortunately not possible to use individual names for each project. The consequence is that if a new project is deployed it will overwrite previously deployed projects customisations!

There are a couple of methods to circumvent this problem:

  1. Create the site using bootstrap. This is method is best suited when there must exist exactly one site
  2. Create a master project which contains customisations which are common across projects. This is the approach I will describe in this blog.
  3. Create a customised dashlet with the purpose of creating the project specific site
    This approach is used in the Alfresco's Record Management Module (see
    http://forums.alfresco.com/en/viewtopic.php?f=51&t=22461)

Master share and repo projects:

A Master share and repo project should contain the configuration files of which only one can exist.

There are several benefits using a master share and project. The master share project can contain configuration which is common for all projects e.g. single-sign on configuration.

The sites I have created for the custom share A and custom Share B projects are referred in presets.xml like this:

<presets>
<!-- CustomA Site
dashboard -->
<
preset id="customA-site-dashboard">
<
components>

............
<
sitePages>[{"pageId":"pageA"}, {"pageId":"documentlibrary"}]</sitePages>
</
properties>
</
page>
</
pages>
</
preset>
<
preset id="customB-site-dashboard">
<
components>
<!-- title -->
<
component>

............
<
properties>

<sitePages>[{"pageId":"pageB"}]</sitePages>

</properties>
</
page>
</
pages>
</
preset>

Also the create-site.get.js is defined in the common master share project like this:

var sitePresets = [{id: "customA-site-dashboard", name: msg.get("title.customASite")},
{id:
"customB-site-dashboard", name: msg.get("title.customBSite")},
{id:
"site-dashboard", name: msg.get("title.collaborationSite")}];

model.sitePresets = sitePresets;

I also created a master repo project. However I did not use it for this post. I envision that a master repo project could be used to define a common content model which the individual customisation projects could inherit from. I haven't tried it though.

Conclusion:

After developing and deploying the 5 projects using the guidelines described in this post I did not discover any conflicts. I am able to create sites of both type “Custom Site A” and “Custom Site B”. However the developed components are not project specific. This means that if I create a Site of type “Custom Site A” I am still able to add a page B (defined in Custom Share B project) to this site. I am also able to able to add dashlet MultipleRSS to site A (MultipleRSS is defined in Custom Share B). Visible aspects defined both project A and project B are visible for all documents.

I don't see this is a problem. It is the responsibility of the site administrator to add relevant components to a site.

An alternative method to avoid conflicts in the share instance is of course to have a dedicated war file for each customisation project. This might be a good solution in some cases even though there a some overhead involved with this solution. However following the guidelines in this post you a free to decide if your project should be deployed in the common share instance with other projects or if you decide to have an individual [project].war file. [project].war is a copy of share.war with your customisations applied.

Posted in Uncategorized | 11 Comments