Extending the Multi Site Manager extending-the-multi-site-manager
This document helps you understand how to extend the functionality of the Multi Site Manager and covers the following topics.
- Learn about the main members of the MSM Java API
- Create a new synchronization action that can be used in a rollout configuration
- Modify the default language and country codes
Overview of the Java API overview-of-the-java-api
Multi Site Management consists of the following packages:
The main MSM API objects interact as follows (see also the section Terms Used):
-
Blueprint
- ABlueprint
(as in blueprint configuration) specifies the pages from which a live copy can inherit content.-
The use of a blueprint configuration (
Blueprint
) is optional, but:- It allows the author to use the Rollout option on the source to explicitly push modifications to live copies that inherit from this source.
- It allows the author to use Create Site, which allows the user to easily select languages and configure the structure of the live copy.
- It defines the default rollout configuration for any resultant live copies.
-
-
LiveRelationship
- TheLiveRelationship
specifies the connection (relationship) between a resource in the live copy branch and its equivalent source/blueprint resource.-
The relationships are used when realizing inheritance and rollout.
-
LiveRelationship
objects provide access (references) to the rollout configurations (RolloutConfig
),LiveCopy
, andLiveStatus
objects related to the relationship. -
For example, a live copy is created in
/content/copy/us
from the source/blueprint at/content/wknd/language-masters
. The resources/content/wknd/language-masters/en/jcr:content
and/content/copy/us/en/jcr:content
form a relationship.
-
-
LiveCopy
- ALiveCopy
holds the configuration details for the relationships (LiveRelationship
) between the live copy resources and their source/blueprint resources.-
Use the
LiveCopy
class to access to the path of the page, the path of the source/blueprint page, the rollout configurations and whether child pages are also included in theLiveCopy
. -
A
LiveCopy
node is created each time Create Site or Create Live Copy is used.
-
-
LiveStatus
-LiveStatus
objects provide access to the runtime status of aLiveRelationship
. Use to query the synchronization status of a live copy. -
LiveAction
- ALiveAction
is an action that is executed on each resource that is involved in the rollout.LiveAction
s are only generated byRolloutConfig
s.
-
LiveActionFactory
- ALiveActionFactory
createsLiveAction
objects given aLiveAction
configuration. Configurations are stored as resources in the repository. -
RolloutConfig
- TheRolloutConfig
holds a list ofLiveActions
, to be used when triggered. TheLiveCopy
inherits theRolloutConfig
and the result is present in theLiveRelationship
.- Setting up a live copy for the very first time also uses a
RolloutConfig
(which triggers theLiveAction
s).
- Setting up a live copy for the very first time also uses a
Creating a New Synchronization Action creating-a-new-synchronization-action
You can create custom synchronization actions to use with your rollout configurations. This can be useful when the installed actions do not meet your specific application requirements.
To do so, create two classes:
- An implementation of the
com.day.cq.wcm.msm.api.LiveAction
interface that performs the action. - An OSGi component that implements the
com.day.cq.wcm.msm.api.LiveActionFactory
interface and creates instances of yourLiveAction
class
The LiveActionFactory
creates instances of the LiveAction
class for a given configuration:
-
LiveAction
classes include the following methods:-
getName
- Returns the name of the action- The name is used to refer to the action, for example, in rollout configurations.
-
execute
- Performs the tasks of the action
-
-
LiveActionFactory
classes include the following members:-
LIVE_ACTION_NAME
- A field that contains the name of the associatedLiveAction
- This name must coincide with the value that is returned by the
getName
method of theLiveAction
class.
- This name must coincide with the value that is returned by the
-
createAction
- Creates an instance of theLiveAction
- The optional
Resource
parameter can be used to provide configuration information.
- The optional
-
createsAction
- Returns the name of the associatedLiveAction
-
Accessing the LiveAction Configuration Node accessing-the-liveaction-configuration-node
Use the LiveAction
configuration node in the repository to store information that affects the runtime behavior of the LiveAction
instance. The node in the repository that stores the LiveAction
configuration is available to the LiveActionFactory
object at runtime. Therefore, you can add properties to the configuration node to and use them in your LiveActionFactory
implementation as needed.
For example, a LiveAction
must store the name of the blueprint author. A property of the configuration node includes the property name of the blueprint page that stores the information. At runtime, the LiveAction
retrieves the property name from the configuration, then obtains the property value.
The parameter of the LiveActionFactory.createAction
method is a Resource
object. This Resource
object represents the cq:LiveSyncAction
node for this live action in the rollout configuration.
See Creating a Rollout Configuration for more information.
As usual when using a configuration node, you should adapt it to a ValueMap
object:
public LiveAction createAction(Resource resource) throws WCMException {
ValueMap config;
if (resource == null || resource.adaptTo(ValueMap.class) == null) {
config = new ValueMapDecorator(Collections.<String, Object>emptyMap());
} else {
config = resource.adaptTo(ValueMap.class);
}
return new MyLiveAction(config, this);
}
Accessing Target Nodes, Source Nodes, and the LiveRelationship accessing-target-nodes-source-nodes-and-the-liverelationship
The following objects are provided as parameters of the execute
method of the LiveAction
object:
-
A
Resource
object that represents the source of the live copy -
A
Resource
object that represents the target of the live copy. -
The
LiveRelationship
object for the live copy- The
autoSave
value indicates whether yourLiveAction
should save changes that are made to the repository - The
reset
value indicates the rollout reset mode.
- The
From these objects, you can obtain information about the LiveCopy
. You can also use the Resource
objects to obtain ResourceResolver
, Session
, and Node
objects. These objects are useful for manipulating repository content:
In the first line of the following code, source is the Resource
object of the source page:
ResourceResolver resolver = source.getResourceResolver();
Session session = resolver.adaptTo(javax.jcr.Session.class);
Node sourcenode = source.adaptTo(javax.jcr.Node.class);
Resource
arguments may be null
or Resources
objects that do not adapt to Node
objects, such as NonExistingResource
objects.Creating a New Rollout Configuration creating-a-new-rollout-configuration
You can create a rollout configuration when the installed rollout configurations do not meet your application requirements be following two steps:
The new rollout configuration is then available to you when setting rollout configurations on a blueprint or live copy page.
Create the Rollout Configuration create-the-rollout-configuration
To create a rollout configuration:
-
Open CRXDE Lite at
https://<host>:<port>/crx/de
. -
Navigate to
/apps/msm/<your-project>/rolloutconfigs
, your project’s customized version of/libs/msm/wcm/rolloutconfigs
.- If this is your first configuration, this
/libs
branch must be used as a template to create the new branch under/apps
.
- If this is your first configuration, this
-
Under this location, create a node with the following properties:
- Name: The node name of the rollout configuration, for example,
contentCopy
orworkflow
- Type:
cq:RolloutConfig
- Name: The node name of the rollout configuration, for example,
-
Add the following properties to this node:
-
Name:
jcr:title
Type:String
Value: An identifying title that will appear in the UI -
Name:
jcr:description
Type:String
Value: An optional description. -
Name:
cq:trigger
Type:String
Value: The Rollout Trigger to be usedrollout
modification
publish
deactivate
-
-
Click Save All.
Add Synchronization Actions to the Rollout Configuration add-synchronization-actions-to-the-rollout-configuration
Rollout configurations are stored below the rollout configuration node that you have created under the /apps/msm/<your-project>/rolloutconfigs
node.
Add child nodes of type cq:LiveSyncAction
to add synchronization actions to the rollout configuration. The order of the synchronization action nodes determines the order in which the actions occur.
-
In CRXDE Lite, select your Rollout Configuration node, for example,
/apps/msm/myproject/rolloutconfigs/myrolloutconfig
. -
Create a node with the following node properties:
- Name: The node name of the synchronization action
- The name must be the same as the Action Name in the table under Synchronization Actions such as
contentCopy
orworkflow
.
- The name must be the same as the Action Name in the table under Synchronization Actions such as
- Type:
cq:LiveSyncAction
- Name: The node name of the synchronization action
-
Add and configure as many synchronization action nodes as you require.
-
Rearrange the action nodes so that their order matches the order in which you want them to occur.
- The topmost action node occurs first.
Creating and Using a Simple LiveActionFactory Class creating-and-using-a-simple-liveactionfactory-class
Follow the procedures in this section to develop a LiveActionFactory
and use it in a rollout configuration. The procedures use Maven and Eclipse to develop and deploy the LiveActionFactory
:
- Create the maven project and import it into Eclipse.
- Add dependencies to the POM file.
- Implement the
LiveActionFactory
interface and deploy the OSGi bundle. - Create the rollout configuration.
- Create the live copy.
The Maven project and the source code of the Java class is available in the public Git repository.
Create the Maven Project create-the-maven-project
The following procedure requires that you have added the adobe-public
profile to your Maven settings file.
- For information about the adobe-public profile, see Obtaining the Content Package Maven Plugin
- For information about the Maven settings file, see the Maven Settings Reference.
-
Open a terminal or command-line session and change the directory to point to the location of where to create the project.
-
Enter the following command:
code language-text mvn archetype:generate -DarchetypeGroupId=com.day.jcr.vault -DarchetypeArtifactId=multimodule-content-package-archetype -DarchetypeVersion=1.0.0 -DarchetypeRepository=adobe-public-releases
-
Specify the following values at interactive prompt:
groupId
:com.adobe.example.msm
artifactId
:MyLiveActionFactory
version
:1.0-SNAPSHOT
package
:MyPackage
appsFolderName
:myapp
artifactName
:MyLiveActionFactory package
packageGroup
:myPackages
-
Start Eclipse and import the Maven project.
Add Dependencies to the POM File add-dependencies-to-the-pom-file
Add dependencies so that the Eclipse compiler can reference the classes that are used in the LiveActionFactory
code.
-
From the Eclipse Project Explorer, open the file
MyLiveActionFactory/pom.xml
. -
In the editor, click the
pom.xml
tab and locate theproject/dependencyManagement/dependencies
section. -
Add the following XML inside the
dependencyManagement
element and then save the file.code language-xml <dependency> <groupId>com.day.cq.wcm</groupId> <artifactId>cq-msm-api</artifactId> <version>5.6.2</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.apache.sling</groupId> <artifactId>org.apache.sling.api</artifactId> <version>2.4.3-R1488084</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.day.cq.wcm</groupId> <artifactId>cq-wcm-api</artifactId> <version>5.6.6</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.apache.sling</groupId> <artifactId>org.apache.sling.commons.json</artifactId> <version>2.0.6</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.day.cq</groupId> <artifactId>cq-commons</artifactId> <version>5.6.4</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.apache.sling</groupId> <artifactId>org.apache.sling.jcr.jcr-wrapper</artifactId> <version>2.0.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.day.cq</groupId> <artifactId>cq-commons</artifactId> <version>5.6.4</version> <scope>provided</scope> </dependency>
-
Open the POM file for the bundle from Project Explorer at
MyLiveActionFactory-bundle/pom.xml
. -
In the editor, click the
pom.xml
tab and locate the project/dependencies section. Add the following XML inside the dependencies element and then save the file:code language-xml <dependency> <groupId>com.day.cq.wcm</groupId> <artifactId>cq-msm-api</artifactId> </dependency> <dependency> <groupId>org.apache.sling</groupId> <artifactId>org.apache.sling.api</artifactId> </dependency> <dependency> <groupId>com.day.cq.wcm</groupId> <artifactId>cq-wcm-api</artifactId> </dependency> <dependency> <groupId>org.apache.sling</groupId> <artifactId>org.apache.sling.commons.json</artifactId> </dependency> <dependency> <groupId>com.day.cq</groupId> <artifactId>cq-commons</artifactId> </dependency> <dependency> <groupId>org.apache.sling</groupId> <artifactId>org.apache.sling.jcr.jcr-wrapper</artifactId> </dependency> <dependency> <groupId>com.day.cq</groupId> <artifactId>cq-commons</artifactId> </dependency>
Implement LiveActionFactory implement-liveactionfactory
The following LiveActionFactory
class implements a LiveAction
that logs messages about the source and target pages, and copies the cq:lastModifiedBy
property from the source node to the target node. The name of the live action is exampleLiveAction
.
-
In the Eclipse Project Explorer, right-click the
MyLiveActionFactory-bundle/src/main/java/com.adobe.example.msm
package and click New > Class. -
For the Name, enter
ExampleLiveActionFactory
and then click Finish. -
Open the
ExampleLiveActionFactory.java
file, replace the content with the following code, and save the file.code language-java package com.adobe.example.msm; import java.util.Collections; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Property; import org.apache.felix.scr.annotations.Service; import org.apache.sling.api.resource.Resource; import org.apache.sling.api.resource.ResourceResolver; import org.apache.sling.api.resource.ValueMap; import org.apache.sling.api.wrappers.ValueMapDecorator; import org.apache.sling.commons.json.io.JSONWriter; import org.apache.sling.commons.json.JSONException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.jcr.Node; import javax.jcr.RepositoryException; import javax.jcr.Session; import com.day.cq.wcm.msm.api.ActionConfig; import com.day.cq.wcm.msm.api.LiveAction; import com.day.cq.wcm.msm.api.LiveActionFactory; import com.day.cq.wcm.msm.api.LiveRelationship; import com.day.cq.wcm.api.WCMException; @Component(metatype = false) @Service public class ExampleLiveActionFactory implements LiveActionFactory<LiveAction> { @Property(value="exampleLiveAction") static final String actionname = LiveActionFactory.LIVE_ACTION_NAME; public LiveAction createAction(Resource config) { ValueMap configs; /* Adapt the config resource to a ValueMap */ if (config == null || config.adaptTo(ValueMap.class) == null) { configs = new ValueMapDecorator(Collections.<String, Object>emptyMap()); } else { configs = config.adaptTo(ValueMap.class); } return new ExampleLiveAction(actionname, configs); } public String createsAction() { return actionname; } /************* LiveAction ****************/ private static class ExampleLiveAction implements LiveAction { private String name; private ValueMap configs; private static final Logger log = LoggerFactory.getLogger(ExampleLiveAction.class); public ExampleLiveAction(String nm, ValueMap config){ name = nm; configs = config; } public void execute(Resource source, Resource target, LiveRelationship liverel, boolean autoSave, boolean isResetRollout) throws WCMException { String lastMod = null; log.info(" *** Executing ExampleLiveAction *** "); /* Determine if the LiveAction is configured to copy the cq:lastModifiedBy property */ if ((Boolean) configs.get("repLastModBy")){ /* get the source's cq:lastModifiedBy property */ if (source != null && source.adaptTo(Node.class) != null){ ValueMap sourcevm = source.adaptTo(ValueMap.class); lastMod = sourcevm.get(com.day.cq.wcm.msm.api.MSMNameConstants.PN_PAGE_LAST_MOD_BY, String.class); } /* set the target node's la-lastModifiedBy property */ Session session = null; if (target != null && target.adaptTo(Node.class) != null){ ResourceResolver resolver = target.getResourceResolver(); session = resolver.adaptTo(javax.jcr.Session.class); Node targetNode; try{ targetNode=target.adaptTo(javax.jcr.Node.class); targetNode.setProperty("la-lastModifiedBy", lastMod); log.info(" *** Target node lastModifiedBy property updated: {} ***",lastMod); }catch(Exception e){ log.error(e.getMessage()); } } if(autoSave){ try { session.save(); } catch (Exception e) { try { session.refresh(true); } catch (RepositoryException e1) { e1.printStackTrace(); } e.printStackTrace(); } } } } public String getName() { return name; } /************* Deprecated *************/ @Deprecated public void execute(ResourceResolver arg0, LiveRelationship arg1, ActionConfig arg2, boolean arg3) throws WCMException { } @Deprecated public void execute(ResourceResolver arg0, LiveRelationship arg1, ActionConfig arg2, boolean arg3, boolean arg4) throws WCMException { } @Deprecated public String getParameterName() { return null; } @Deprecated public String[] getPropertiesNames() { return null; } @Deprecated public int getRank() { return 0; } @Deprecated public String getTitle() { return null; } @Deprecated public void write(JSONWriter arg0) throws JSONException { } } }
-
Using the terminal or command session, change the directory to the
MyLiveActionFactory
directory (the Maven project directory). Then, enter the following command:code language-shell mvn -PautoInstallPackage clean install
-
The AEM
error.log
file should indicate that the bundle is started, visible in the logs athttps://<host>:<port>/system/console/status-slinglogs
.code language-text 13.08.2013 14:34:55.450 *INFO* [OsgiInstallerImpl] com.adobe.example.msm.MyLiveActionFactory-bundle BundleEvent RESOLVED 13.08.2013 14:34:55.451 *INFO* [OsgiInstallerImpl] com.adobe.example.msm.MyLiveActionFactory-bundle BundleEvent STARTING 13.08.2013 14:34:55.451 *INFO* [OsgiInstallerImpl] com.adobe.example.msm.MyLiveActionFactory-bundle BundleEvent STARTED 13.08.2013 14:34:55.453 *INFO* [OsgiInstallerImpl] com.adobe.example.msm.MyLiveActionFactory-bundle Service [com.adobe.example.msm.ExampleLiveActionFactory,2188] ServiceEvent REGISTERED 13.08.2013 14:34:55.454 *INFO* [OsgiInstallerImpl] org.apache.sling.audit.osgi.installer Started bundle com.adobe.example.msm.MyLiveActionFactory-bundle [316]
Create the Example Rollout Configuration create-the-example-rollout-configuration
Create the MSM rollout configuration that uses the LiveActionFactory
that you created:
-
Create and configure a Rollout Configuration with the standard procedure using the properties:
- Title: Example Rollout Configuration
- Name: examplerolloutconfig
- cq:trigger:
publish
Add the Live Action to the Example Rollout Configuration add-the-live-action-to-the-example-rollout-configuration
Configure the rollout configuration that you created in the previous procedure so that it uses the ExampleLiveActionFactory
class.
-
Open CRXDE Lite.
-
Create the following node under
/apps/msm/rolloutconfigs/examplerolloutconfig/jcr:content
:- Name:
exampleLiveAction
- Type:
cq:LiveSyncAction
- Name:
-
Click Save All.
-
Select the
exampleLiveAction
node and add a property to indicate to theExampleLiveAction
class that thecq:LastModifiedBy
property should be replicated from the source to the target node.- Name:
repLastModBy
- Type:
Boolean
- Value:
true
- Name:
-
Click Save All.
Create the Live Copy create-the-live-copy
Create a live copy of the English/Products branch of the WKND reference site using your rollout configuration:
-
Source:
/content/wknd/language-masters/en/products
-
Rollout Configuration: Example Rollout Configuration
Activate the Products (english) page of the source branch and observe the log messages that the LiveAction
class generates:
16.08.2013 10:53:33.055 *INFO* [Thread-444535] com.adobe.example.msm.ExampleLiveActionFactory$ExampleLiveAction ***ExampleLiveAction has been executed.***
16.08.2013 10:53:33.055 *INFO* [Thread-444535] com.adobe.example.msm.ExampleLiveActionFactory$ExampleLiveAction ***Target node lastModifiedBy property updated: admin ***
Changing Language Names and Default Countries changing-language-names-and-default-countries
AEM uses a default set of language and country codes.
- The default language code is the lower-case, two-letter code as defined by ISO-639-1.
- The default country code is the lower-case or upper-case, two-letter code as defined by ISO 3166.
MSM uses a stored list of language and country codes to determine the name of the country that is associated with the name of the language version of your page. You can change the following aspects of the list if necessary:
- Language titles
- Country names
- Default countries for languages (for codes such as
en
,de
, amongst others)
The language list is stored below the /libs/wcm/core/resources/languages
node. Each child node represents a language or a language-country:
-
The name of the node is the language code (such as
en
orde
), or the language_country code (such asen_us
orde_ch
). -
The
language
property of the node stores the full name of the language for the code. -
The
country
property of the node stores the full name of the country for the code. -
When the node name consists only of a language code (such as
en
), the country property is*
, and an additionaldefaultCountry
property stores the code of the language-country to indicate the country to use.
To modify the languages:
-
Open CRXDE Lite.
-
Select the
/apps
folder and click Create, then Create Folder. -
Name the new folder
wcm
. -
Repeat the previous step to create the
/apps/wcm/core
folder tree. Create a node of typesling:Folder
incore
calledresources
. -
Right-click the
/libs/wcm/core/resources/languages
node and click Copy. -
Right-click the
/apps/wcm/core/resources
folder and click Paste. Modify the child nodes as required. -
Click Save All.
-
Click Tools, Operations then Web Console. From this console click OSGi, then Configuration.
-
Locate and click Day CQ WCM Language Manager, and change the value of Language List to
/apps/wcm/core/resources/languages
, then click Save.
Configuring MSM Locks on Page Properties configuring-msm-locks-on-page-properties
When creating a custom page property you may need to consider whether the new property should be eligible for roll out to any live copies.
For example, if two new page properties are being added:
-
Contact Email:
- This property is not required to be rolled out, as it will be different in each country (or brand, and so on).
-
Key Visual Style:
- The project requirement is that this property is to be rolled out as it is (usually) common to all countries (or brands, and so on).
Then you need to ensure that:
-
Contact Email:
- Is excluded from the rolled out properties.
- See Configuring Live Copy Synchronization for more information.
-
Key Visual Style:
- Make sure you are not allowed to edit this property unless inheritance is cancelled.
- Also ensure that you can then reinstate inheritance. This is controlled by clicking the chain/broken-chain links that toggle to indicate the status of the connection.
Whether a page property is subject to roll out and therefore, subject to cancelling/reinstating inheritance when editing, is controlled by the dialog property:
-
cq-msm-lockable
-
This will create the chain-link symbol in the dialog.
-
This only allows editing if inheritance is cancelled (the chain-link is broken).
-
This only applies to the first child level of the resource
- Type:
String
- Value: Holds the name of the property under consideration and is comparable to the value of the property
name
- For example, see
/libs/foundation/components/page/cq:dialog/content/items/tabs/items/basic/items/column/items/title/items/title
- For example, see
- Type:
-
When cq-msm-lockable
has been defined, breaking/closing the chain will interact with MSM in the following way:
-
If the value of
cq-msm-lockable
is:-
Relative (for example,
myProperty
or./myProperty
)- Breaking the chain will add and remove the property from
cq:propertyInheritanceCancelled
.
- Breaking the chain will add and remove the property from
-
Absolute (for example,
/image
)-
Breaking the chain will cancel inheritance by adding the
cq:LiveSyncCancelled
mixin to./image
and settingcq:isCancelledForChildren
totrue
. -
Closing the chain will revert inheritance.
-
-
cq-msm-lockable
applies to the first child level of the resource to be edited and it is not functional on any deeper level ancestor regardless if the value is defined as absolute or relative.