You are looking at documentation for an older release. Not what you want? See the current release documentation.
A script action, also known as ECMS action, is basically a Groovy script that can be triggered by user click or events like content addition.
The action can do lots of things like sending an email or notification, transforming a document into PDF, watermarking an image. It is the way to perform some tasks in Content like restoring a node from Trash. You can find such built-in scripts here.
In this introductory example, you write a script that sends emails telling a user about content update in a specific folder.
An action involves:
A Groovy script that implements CmsScript
and runs your business logic.
A node type definition, that is the data model for action. The word "action" might be used for both meanings, but strictly speaking, an "action" is a node type, and an "action instance" is a node of that type.
Templates (view and dialog) that are used when users add/edit/view an action instance via UI.
All the stuffs can be done via UI or by an extension. This tutorial covers both but is more focused in an extension. Source code can be found here.
You need to create a portal extension by following portal extension tutorial. Hereunder is its structure:
The file names are self explanatory. You can change the structure except that
scripts/ecm-explorer/action
path is not variable.
First, the script must implement CmsScript
, and two methods:
import org.exoplatform.services.cms.scripts.CmsScript;
public class SampleScript implements CmsScript {
public void execute(Object context) throws Exception {
...
}
public void setParams(String[] arg0) {} //in this example, this method does nothing.
}
Constructor
This does not need a constructor, however if you need some services from the portal container, you should let the CmsScript framework pass them to your script on its creation:
import org.exoplatform.services.jcr.RepositoryService;
import org.exoplatform.services.jcr.ext.app.SessionProviderService;
...
private RepositoryService repositoryService_ ;
private SessionProviderService seProviderService_;
//Constructor
public SendMailAction(RepositoryService repositoryService, SessionProviderService sessionProviderService) {
repositoryService_ = repositoryService ;
seProviderService_ = sessionProviderService;
}
By this way, the framework is responsible for assuring the services are available before you get them, and you do not need to worry about some circumstances - like the script is triggered during the startup when a service is not created yet.
Object context
The object context
provides you all the information of the action launching, in summary:
The source, typically a folder where the action instance is created.
The node, on which the event happens. In this example, it is a node created/removed under the source.
The event type, or the lifecycle phase, so you know what happened.
Other variables. For example, you need a "To" address to send mails to, so you let the users input it when they create an action instance.
Complete the execute(Object context)
method, by extracting information you need and sending a message:
public void execute(Object context) throws Exception {
Map values = (Map) context;
String to = (String) values.get("exo:to");
String subject = (String) values.get("exo:subject");
String srcWorkspace = (String) values.get("srcWorkspace");
String srcPath = (String) values.get("srcPath");
if (to == null) {
LOG.warn("A SendMailAction at " + srcWorkspace + ":" + srcPath + " is canceled because the TO address is not determined");
return;
}
Message message = new Message();
message.setTo(to);
message.setSubject(subject);
message.setBody("There is content update in " + srcWorkspace + ":" + srcPath);
try {
((MailService) CommonsUtils.getService(MailService.class)).sendMessage(message);
} catch (Exception e) {
e.printStackTrace();
}
}
To quickly explore which keys the context
has, you can cast it to a String and print it out.
Script registration
In configuration.xml
file, register your script, with attention to the two path parameters.
<external-component-plugins>
<target-component>org.exoplatform.services.cms.scripts.ScriptService</target-component>
<component-plugin>
<name>manage.script.plugin</name>
<set-method>addScriptPlugin</set-method>
<type>org.exoplatform.services.cms.scripts.impl.ScriptPlugin</type>
<description></description>
<init-params>
<value-param>
<name>autoCreateInNewRepository</name>
<value>true</value>
</value-param>
<value-param>
<name>predefinedScriptsLocation</name>
<value>war:/conf/ecms-action</value>
</value-param>
<object-param>
<name>predefined.scripts</name>
<description></description>
<object type="org.exoplatform.services.cms.impl.ResourceConfig">
<field name="resources">
<collection type="java.util.ArrayList">
<value>
<object type="org.exoplatform.services.cms.impl.ResourceConfig$Resource">
<field name="description"><string>Send Mail Script</string></field>
<field name="name"><string>ecm-explorer/action/SendMailScript.groovy</string></field>
</object>
</value>
</collection>
</field>
</object>
</object-param>
</init-params>
</component-plugin>
</external-component-plugins>
To create a script in UI (Content Administration), go to
→ .
Define your action node type exo:sendMailAction
in nodetypes-configuration.xml
file.
Basically it extends the built-in type exo:scriptAction
, and adds some other properties.
The two first properties are the script's path and label.
<nodeTypes xmlns:nt="http://www.jcp.org/jcr/nt/1.0" xmlns:mix="http://www.jcp.org/jcr/mix/1.0"
xmlns:jcr="http://www.jcp.org/jcr/1.0">
<nodeType name="exo:sendMailAction" isMixin="false" hasOrderableChildNodes="false" primaryItemName="">
<supertypes>
<supertype>exo:scriptAction</supertype>
</supertypes>
<propertyDefinitions>
<propertyDefinition name="exo:script"
requiredType="String" autoCreated="true" mandatory="true" onParentVersion="COPY" protected="false" multiple="false">
<valueConstraints />
<defaultValues>
<defaultValue>ecm-explorer/action/SendMailScript.groovy</defaultValue>
</defaultValues>
</propertyDefinition>
<propertyDefinition name="exo:scriptLabel"
requiredType="String" autoCreated="true" mandatory="true" onParentVersion="COPY" protected="false" multiple="false">
<valueConstraints />
<defaultValues>
<defaultValue>Send Mail Action</defaultValue>
</defaultValues>
</propertyDefinition>
...
</propertyDefinitions>
</nodeType>
</nodeTypes>
You want "To" address and the mail subject passed to your script in context
parameter,
so here you define them as action properties:
<propertyDefinition name="exo:to"
requiredType="String" autoCreated="false" mandatory="true" onParentVersion="COPY" protected="false" multiple="false">
<valueConstraints />
</propertyDefinition>
<propertyDefinition name="exo:subject"
requiredType="String" autoCreated="true" mandatory="true"
onParentVersion="COPY" protected="false" multiple="false">
<valueConstraints />
<defaultValues>
<defaultValue>Content update</defaultValue>
</defaultValues>
</propertyDefinition>
Then register the node type in configuration.xml
file:
<external-component-plugins>
<target-component>org.exoplatform.services.jcr.RepositoryService</target-component>
<component-plugin>
<name>add.nodeType</name>
<set-method>addPlugin</set-method>
<type>org.exoplatform.services.jcr.impl.AddNodeTypePlugin</type>
<init-params>
<values-param>
<name>autoCreatedInNewRepository</name>
<description>Node types configuration file</description>
<value>war:/conf/nodetypes-configuration.xml</value>
</values-param>
</init-params>
</component-plugin>
</external-component-plugins>
In UI (Content Administration), to create an action node type, go to exo:scriptAction with some string variables, go to → .
→ . The UI will give you all the choices as equivalent as an xml definition does. Alternatively, if you want a simple type that extendsThere are properties that you fix the values in the node type definition, and there are properties that you let the users input when they create an action instance. In this example, you let the users choose lifecycle phases (events to trigger the action), "To" and "Subject".
For that purpose, you write two Groovy templates: a dialog to edit the action, and a view to display it.
The dialog, lifecyle selectbox, isDeep checkbox and text fields
See the full code of the dialog SendMailActionDialog.gtmpl
in the project source.
In almost cases, the dialog allows users to input an action name, so let's start with this structure:
<div class="uiAddActionForm resizable"> <h6 class="titleBar"><%=_ctx.appRes(uicomponent.getId() + ".title")%></h6> <% uiform.begin() %> <div class="form-horizontal" style="min-width:550px;"> <div class="control-group" style="display:none"> <label class="control-label" for="id"><%=_ctx.appRes("ScriptAction.dialog.label.id")%>:</label> <div class="controls"> <% String[] fieldId = ["jcrPath=/node", "mixintype=mix:affectedNodeTypes", "editable=false", "visible=if-not-null"]; uicomponent.addMixinField("id", fieldId) ; %> </div> </div> <div class="control-group"> <label class="control-label" for="actionName"><%=_ctx.appRes("ScriptAction.dialog.label.name")%>:</label> <div class="controls"> <% String[] fieldName = ["jcrPath=/node/exo:name", "validate=empty,XSSValidator"]; uicomponent.addTextField("actionName", _ctx.appRes("ScriptAction.dialog.label.name"), fieldName); %> </div> </div> </div> <%uiform.end()%> </div> <%/* start render action*/%> <%uiform.processRenderAction()%> <%/* end render action*/%>
Then for each variable you add a control-group that consists of a label and an input. The input can vary in many kinds. Though it might not always be the right logic to let users input these fields in this action, the project aims at showing some typical controls, then you see in the code: lifecycle phase selectbox, "isDeep" selectbox, "To" and "Subject" text.
Here is the snippet of lifecycle selectbox:
<div class="control-group"> <label class="control-label" for="lifecycle"><%=_ctx.appRes("ScriptAction.dialog.label.lifecycle")%>:</label> <div class="controls"> <% String[] fieldLifecycle = ["jcrPath=/node/exo:lifecyclePhase", "options=read,node_added,node_removed,property_added,property_removed,property_changed", "multiValues=true", "onchange=true","size=5","validate=empty"] ; uicomponent.addSelectBoxField("lifecycle", fieldLifecycle) ; %> </div> </div>
Add read if you want the ability to run the action from the context menu.
The "isDeep" option, if true, sets the listeners on all over the subtree, so events that happen in child nodes trigger the action.
The view
Much more simpler than the dialog, in a view you just need to get the properties, convert to string if necessary and display them in a table.
<% def node = uicomponent.getNode(); StringBuilder builder; %> <table class="uiGrid table table-hover table-striped"> <tr> <td>Action Name</td> <td> <%if(node.hasProperty("exo:name")) {%> <%=node.getProperty("exo:name").getString()%> <%}%> </td> </tr> <tr> <td>Lifecycle Phases</td> <td> <% if (node.hasProperty("exo:lifecyclePhase")) { builder = new StringBuilder(); def values = node.getProperty("exo:lifecyclePhase").getValues(); for (value in values) { builder.append(value.getString()).append(","); } if (builder.length() > 0) {%><%= builder.deleteCharAt(builder.length() -1) %><%} } %> </td> </tr> <tr> <td>Is Deep?</td> <td> <%if(node.hasProperty("exo:isDeep")){%> <%=node.getProperty("exo:isDeep").getString()%> <%}%> </td> </tr> <tr> <td>To Address</td> <td> <%if(node.hasProperty("exo:to")){%> <%=node.getProperty("exo:to").getString()%> <%}%> </td> </tr> <tr> <td>Signature</td> <td> <%if(node.hasProperty("exo:subject")){%> <%=node.getProperty("exo:subject").getString()%> <%}%> </td> </tr> </table>
Templates registration
To register the view and dialog, add the following to configuration.xml
file:
<external-component-plugins>
<target-component>org.exoplatform.services.cms.templates.TemplateService</target-component>
<component-plugin>
<name>addTemplates</name>
<set-method>addTemplates</set-method>
<type>org.exoplatform.services.cms.templates.impl.TemplatePlugin</type>
<init-params>
<value-param>
<name>autoCreateInNewRepository</name>
<value>true</value>
</value-param>
<value-param>
<name>storedLocation</name>
<value>war:/conf/ecms-action/templates</value>
</value-param>
<object-param>
<name>template.configuration</name>
<object type="org.exoplatform.services.cms.templates.impl.TemplateConfig">
<field name="nodeTypes">
<collection type="java.util.ArrayList">
<value>
<object type="org.exoplatform.services.cms.templates.impl.TemplateConfig$NodeType">
<field name="nodetypeName"><string>exo:sendMailAction</string></field>
<field name="documentTemplate"><boolean>false</boolean></field>
<field name="label"><string>Send Mail Action</string></field>
<field name="referencedView">
<collection type="java.util.ArrayList">
<value>
<object type="org.exoplatform.services.cms.templates.impl.TemplateConfig$Template">
<field name="templateFile"><string>/views/SendMailActionView.gtmpl</string></field>
<field name="roles"><string>*</string></field>
</object>
</value>
</collection>
</field>
<field name="referencedDialog">
<collection type="java.util.ArrayList">
<value>
<object type="org.exoplatform.services.cms.templates.impl.TemplateConfig$Template">
<field name="templateFile"><string>/dialogs/SendMailActionDialog.gtmpl</string></field>
<field name="roles"><string>*</string></field>
</object>
</value>
</collection>
</field>
</object>
</value>
</collection>
</field>
</object>
</object-param>
</init-params>
</component-plugin>
</external-component-plugins>
Notice the parameter documentTemplate
is set to false to mark this as action template.
In UI (Content Administration), to create action templates, go to Actions tab.
→ and select theIn UI, to create an action instance for a folder, the user will choose the folder, then select Actions from the Actions bar. Select exo:sendMailAction, then complete the dialog.
If you know a certain location where an instance should be created, you can do it by the extension.
Add the following to configuration.xml
:
<external-component-plugins>
<target-component>org.exoplatform.services.cms.actions.ActionServiceContainer</target-component>
<component-plugin>
<name>exo:scriptAction</name>
<set-method>addPlugin</set-method>
<type>org.exoplatform.services.cms.actions.impl.ScriptActionPlugin</type>
<init-params>
<object-param>
<name>predefined.actions</name>
<description></description>
<object type="org.exoplatform.services.cms.actions.impl.ActionConfig">
<field name="workspace"><string>collaboration</string></field>
<field name="actions">
<collection type="java.util.ArrayList">
<value>
<object type="org.exoplatform.services.cms.actions.impl.ActionConfig$Action">
<field name="type"><string>exo:sendMailAction</string></field>
<field name="name"><string>sendMailAction</string></field>
<field name="description"><string>A sample ECMS action</string></field>
<field name="srcWorkspace"><string>collaboration</string></field>
<field name="srcPath"><string>/sites/shared</string></field>
<field name="isDeep"><boolean>true</boolean></field>
<field name="lifecyclePhase">
<collection type="java.util.ArrayList">
<value><string>node_added</string></value>
<value><string>node_removed</string></value>
</collection>
</field>
<field name="variables">
<string>exo:subject=Content update;exo:to=test@example.com</string>
</field>
</object>
</value>
</collection>
</field>
</object>
</object-param>
</init-params>
</component-plugin>
</external-component-plugins>
Note that:
The source location (where to add the action instance) is determined by its workspace and its path.
The variables ("To" and "Subject") are separated by comma.
Sites Explorer or Crash addon sets your sight inside JCR repository. If you are using Sites Explorer, you should change the preferences to enable DMS structure and show hidden nodes. To use Crash, follow Crash addon guide.
Scripts and templates are stored in dms-system
workspace, at the paths:
/exo:ecm/scripts/ecm-explorer/action/SendMailScript.groovy
/exo:ecm/templates/exo:sendMailAction/dialogs
/exo:ecm/templates/exo:sendMailAction/views
Action instances are stored under the source node.
For example, if you have created an action in collaboration:/sites/test-send-mail-action
,
and name it mailMe, then check the path:
/sites/test-send-mail-action/exo:actions/mailMe
/sites/test-send-mail-action/exo:actions/mailMe +-properties | +-jcr:primaryType: exo:sendMailAction | +-jcr:mixinTypes: [exo:sortable,exo:modify,exo:datetime,exo:owneable,mix:affectedNodeTypes,mix:referenceable,exo:rss- | | enable,mix:lockable] | +-jcr:uuid: 'fd0a7e497f00010114a120a432b05e7d' | +-exo:dateCreated: 2015-08-05T15:46:40.202+07:00 | +-exo:dateModified: 2015-08-05T15:46:40.202+07:00 | +-exo:index: 1000 | +-exo:isDeep: true | +-exo:lastModifiedDate: 2015-08-05T15:46:40.204+07:00 | +-exo:lastModifier: 'root' | +-exo:lifecyclePhase: ['node_added','node_removed','read'] | +-exo:name: 'mailMe' | +-exo:owner: 'root' | +-exo:roles: ['*:/platform/users'] | +-exo:script: 'ecm-explorer/action/SendMailScript.groovy' | +-exo:scriptLabel: 'Send Mail Action' | +-exo:subject: 'ContEnt updAte' | +-exo:title: 'mailMe' | +-exo:to: 'chautn@exoplatform.com' +-children