Create rules dynamically in script. You can create rule programmatically using JAVA API but there is nothing available in javascript API from Alfresco to create rule on the fly.
Take a scenario where you want to add rule on space when its created using script. I couldn’t find any way of doing this so I tried creating a custom javascript API for it.
All work was done and tested on Alfresco 4.0 CE and higher till 4.2.c
Getting Started
It leads to an idea of adding rule description in JSON format in a script then passing it to a Java class, then in java class we can parse JSON and get the rule description to create rule on the fly using JAVA API
Create JSON format for rule in this page as a starting point:
You have to modify it based on what the various actions and condition handlers expect for parameter names
Create a class which would read the rule configuration from JSON objects passed from script. Name this class as CustomRuleCreator and extends it with CreateRuleWizard mostly to use its condition and action handlers.
The constructor takes a couple of helper classes. They important thing here is the call to super.init(). This initializes the maps of condition handlers and action handlers with key on “conditionName” and “actionName”.
public CustomRuleCreator(ServiceRegistry serviceRegistry, Repository repository) {
this.serviceRegistry = serviceRegistry;
this.repository = repository;
super.init(null);
}
The createRule() method sets some of the simple rule properties such as title, description, etc in the parent class, calls setupRule() to set the more complex rule properties, then attaches the rule to the space.
public void createRule(NodeRef targetSpace, JSONObject ruleDetail)throws JSONException {
this.ruleJson = ruleJson;
this.setTitle(this.ruleJson.getString("title"));
this.setDescription(this.ruleJson.getString("description"));
this.setType(this.ruleJson.getString("ruleType"));
this.setRunInBackground(this.ruleJson.getBoolean("executeAsynchronously"));
this.setApplyToSubSpaces(this.ruleJson.getBoolean("applyToChildren"));
this.setRuleDisabled(this.ruleJson.getBoolean("disabled"));
// create the new rule
Rule rule = new Rule();
rule.setRuleType(this.getType());
// setup the rule
this.setupRule(rule);
// save the rule
this.getRuleService().saveRule(targetSpace, rule);
}
The setupRule() and createCondition() methods are overrided from CreateRuleWizard and modified to read detail from JSON objects.
Create a method say convertJsonToMap() which converts JSON Objects into map. It parses JSON and checks if scripts or destination parameters are passed in rule details then checks it values are noderef or relative paths. Also it converts any “true” or “false” values to a Boolean object and so on for Integer, Long and Double.
Now make this api available to script as javascript api. For this create a class name it as CustomRuleScript and extends org.alfresco.repo.jscript.BaseScopableProcessorExtension.
public void createRule(ScriptNode targetSpace, String ruleJson) throws JSONException {
JSONObject json = new JSONObject(ruleJson);
CustomRuleCreator customRuleCreator = new CustomRuleCreator(this.serviceRegistry, this.repository);
customRuleCreator.createRule(targetSpace.getNodeRef(), json);
}
Above method takes targetSpace as parameter, which as space on which we want to add rule on the fly.
We need to add this entry in *-context.xml file
<bean id="customRuleScript" class="com.priteshblog.repo.rule.jscript.CustomRuleScript" parent="baseJavaScriptExtension">
<property name="extensionName">
<value>customRuleScript</value>
</property>
<property name="serviceRegistry">
<ref bean="ServiceRegistry"></ref>
</property>
<property name="repository" ref="repositoryHelper">
</property>
</bean>
Below is the script to create rule on space,
function main(){
var guestHome = companyhome.childByNamePath("Guest Home");
copyDocumentRule(companyhome, guestHome.nodeRef);
}
function copyDocumentRule(ruleOnNode, destinationNode){
var rule = '';
rule += '{';
rule += ' "title": "Copy Document",';
rule += ' "description": "Copy Document to folder",';
rule += ' "ruleType": "inbound",';
rule += ' "applyToChildren": "false",';
rule += ' "executeAsynchronously": "true",';
rule += ' "disabled": "false",';
rule += ' "action" : {';
rule += ' "actionName": "composite-action",';
rule += ' "actions": [';
rule += ' {';
rule += ' "actionName": "copy",';
rule += ' "parameterValues": {';
rule += ' "destinationLocation": "'+destinationNode+'"';
rule += ' }';
rule += ' }';
rule += ' ],';
rule += ' "conditions": [';
rule += ' {';
rule += ' "conditionName": "no-condition",'
rule += ' "notcondition" : "false"';
rule += ' }';
rule += ' ]';
rule += ' }';
rule += '}';
ruleUtil.createRule(ruleOnNode, rule);
}
main();
Create rule-script.js with above script code and upload it to Data Dictionary/Scripts folder
To test above script, goto view details in company home and go to run action. Select execute script and then script above script and click on finish. You will see new rule created on company by checking “Manage Content Rule” on space.
You will have to do some debugging and check what are parameters name and action name and so on. You can check this by creating rule manually from “Manage Content Rule” and use webscripts to check rule detals. For webscripts, check this link
CustomRuleScript.java
package com.priteshblog.repo.rule.jscript;
import org.alfresco.repo.jscript.BaseScopableProcessorExtension;
import org.alfresco.repo.jscript.ScriptNode;
import org.alfresco.repo.model.Repository;
import org.alfresco.service.ServiceRegistry;
import org.json.JSONException;
import org.json.JSONObject;
public class CustomRuleScript extends BaseScopableProcessorExtension {
protected ServiceRegistry serviceRegistry = null;
protected Repository repository = null;
public CustomRuleScript() {
}
/**
* Creates a rule (described by the given JSON) on the given space.
* Assumes the current user has permission to create rules on the space.
*
* @param targetSpace the space to create the rule on
* @param ruleJson a JSON string describing the rule
* @throws JSONException if error parsing JSON
*/
public void createRule(ScriptNode targetSpace, String ruleJson) throws JSONException {
JSONObject json = new JSONObject(ruleJson);
CustomRuleCreator customRuleCreator = new CustomRuleCreator(this.serviceRegistry, this.repository);
customRuleCreator.createRule(targetSpace.getNodeRef(), json);
}
public ServiceRegistry getServiceRegistry() {
return serviceRegistry;
}
public void setServiceRegistry(ServiceRegistry serviceRegistry) {
this.serviceRegistry = serviceRegistry;
}
public Repository getRepository() {
return repository;
}
public void setRepository(Repository repository) {
this.repository = repository;
}
}
I have added condition for custom rule actions. For rule actions it will need handlers. For e.g for copy rule action, there is a CopyHandler. For custom rule action, where you have only created action-executor and not handler below code will work. Check else in setUpRule method which allow to execute further if handler for custom action not found
CustomRuleCreator.java
package com.priteshblog.repo.rule.jscript;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.model.Repository;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ActionCondition;
import org.alfresco.service.cmr.action.CompositeAction;
import org.alfresco.service.cmr.action.CompositeActionCondition;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.rule.Rule;
import org.alfresco.web.bean.actions.IHandler;
import org.alfresco.web.bean.rules.CreateRuleWizard;
import org.alfresco.web.bean.rules.handlers.BaseConditionHandler;
import org.alfresco.web.bean.rules.handlers.CompositeConditionHandler;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
public class CustomRuleCreator extends CreateRuleWizard {
private static final long serialVersionUID = 1L;
private static final Log logger = LogFactory.getLog(CustomRuleCreator.class);
/** the value of this parameter must be a node ref as a String */
protected static final String PROP_SCRIPT = "script";
/** the value of this parameter must be a node ref as a NodeRef */
protected static final String PROP_DESTINATION = "destinationLocation";
protected JSONObject ruleJson = null;
protected ServiceRegistry serviceRegistry = null;
protected Repository repository = null;
public CustomRuleCreator(ServiceRegistry serviceRegistry, Repository repository) {
this.serviceRegistry = serviceRegistry;
this.repository = repository;
super.init(null);
}
/**
* Creates a rule on the given space using rule properties provided via JSON in the constructor.
*
* @param targetSpace
* the space the rule will apply to
* @param ruleJson
* a JSON object describing the rule to create
* @throws JSONException
* if error parsing JSON string
*/
public void createRule(NodeRef targetSpace, JSONObject ruleJson)
throws JSONException {
if (logger.isDebugEnabled()) {
logger.debug("creating rule");
}
this.ruleJson = ruleJson;
this.setTitle(this.ruleJson.getString("title"));
this.setDescription(this.ruleJson.getString("description"));
this.setType(this.ruleJson.getString("ruleType"));
this.setRunInBackground(this.ruleJson.getBoolean("executeAsynchronously"));
this.setApplyToSubSpaces(this.ruleJson.getBoolean("applyToChildren"));
this.setRuleDisabled(this.ruleJson.getBoolean("disabled"));
// create the new rule
Rule rule = new Rule();
rule.setRuleType(this.getType());
// setup the rule
this.setupRule(rule);
// save the rule
this.getRuleService().saveRule(targetSpace, rule);
}
/**
* Sets up the given rule using rule properties provided via JSON in the constructor.
*
* @param rule
* the rule to setup
* @throws JSONException
* if error parsing JSON
*/
protected void setupRule(Rule rule)
throws JSONException {
// setup the rule
rule.setTitle(this.title);
rule.setDescription(this.description);
rule.applyToChildren(this.applyToSubSpaces);
rule.setExecuteAsynchronously(this.runInBackground);
rule.setRuleDisabled(this.ruleDisabled);
JSONObject ruleAction = this.ruleJson.getJSONObject("action");
// assume composite action
CompositeAction compositeAction = this.getActionService().createCompositeAction();
rule.setAction(compositeAction);
JSONArray conditions = ruleAction.getJSONArray("conditions");
for (int cIdx = 0; cIdx < conditions.length(); cIdx++) {
JSONObject jsonCond = conditions.getJSONObject(cIdx);
ActionCondition condition = createCondition(jsonCond);
if (condition instanceof CompositeActionCondition) {
CompositeActionCondition compositeCondition = (CompositeActionCondition) condition;
compositeCondition.setORCondition(jsonCond.getBoolean(CompositeConditionHandler.PROP_CONDITION_OR));
compositeCondition.setInvertCondition(jsonCond.getBoolean(CompositeConditionHandler.PROP_CONDITION_NOT));
JSONArray subconditions = jsonCond.getJSONArray("conditions");
for (int sIdx = 0; sIdx < subconditions.length(); sIdx++) {
JSONObject jsonSubCond = subconditions.getJSONObject(sIdx);
compositeCondition.addActionCondition(createCondition(jsonSubCond));
}
}
compositeAction.addActionCondition(condition);
}
// add all the actions to the rule
JSONArray actions = ruleAction.getJSONArray("actions");
for (int aIdx = 0; aIdx < actions.length(); aIdx++) {
JSONObject jsonAction = actions.getJSONObject(aIdx);
String actionName = jsonAction.getString(PROP_ACTION_NAME);
this.action = actionName;
JSONObject jsonParams = jsonAction.getJSONObject("parameterValues");
Map actionProps = this.convertJsonToMap(jsonParams);
// get the action handler to prepare for the save
Map repoActionParams = new HashMap();
IHandler handler = this.actionHandlers.get(this.action);
if (handler != null) {
handler.prepareForSave(actionProps, repoActionParams);
}else{
repoActionParams =actionProps;
}
// add the action to the rule
Action action = this.getActionService().createAction(actionName);
action.setParameterValues(repoActionParams);
compositeAction.addAction(action);
}
}
/**
* Creates an action condition based on a JSON object
*
* @param jsonCond
* JSON object containing condition properties
* @return an action condition
* @throws JSONException
* if error parsing JSON object
*/
private ActionCondition createCondition(JSONObject jsonCond)
throws JSONException {
String conditionName = jsonCond.getString(PROP_CONDITION_NAME);
Map repoCondParams = new HashMap();
Map conditionProps = this.convertJsonToMap(jsonCond);
// get the condition handler to prepare for the save
IHandler handler = this.conditionHandlers.get(conditionName);
if (handler != null) {
handler.prepareForSave(conditionProps, repoCondParams);
}
// add the condition to the rule
ActionCondition condition = this.getActionService().createActionCondition(conditionName);
condition.setParameterValues(repoCondParams);
// specify whether the condition result should be inverted
Boolean not = (Boolean) conditionProps.get(BaseConditionHandler.PROP_CONDITION_NOT);
if (not == null) {
not = Boolean.TRUE;
}
condition.setInvertCondition(((Boolean) not).booleanValue());
return condition;
}
/**
* Converts a JSON object to a Map
*
* @param jsonObj
* JSON object to convert
* @return a Map where key is a String and value is a Serializable object
* @throws JSONException
* if error parsing JSON object
*/
@SuppressWarnings("rawtypes")
protected Map convertJsonToMap(JSONObject jsonObj)
throws JSONException {
// convert JSON to Map
Map propMap = new HashMap();
Iterator keyIt = jsonObj.keys();
while (keyIt.hasNext()) {
String key = (String) keyIt.next();
String value = jsonObj.getString(key);
Serializable propValue = null;
// convert repository paths to node references
if (PROP_SCRIPT.equals(key) || PROP_DESTINATION.equals(key)) {
NodeService nodeService = this.serviceRegistry.getNodeService();
NodeRef nodeRef = null;
String storeProtocol = null;
String storeIdentifier = null;
String nodeUUID = null;
// is value a node reference?
if (NodeRef.isNodeRef(value)) {
nodeRef = new NodeRef(value);
storeProtocol = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_STORE_PROTOCOL);
storeIdentifier = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_STORE_IDENTIFIER);
nodeUUID = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_NODE_UUID);
}
else {
// assume we have a repository path relative to Company Home
// search for the path within Alfresco content repository
String nodePath = "workspace/SpacesStore/" + value;
nodeRef = this.repository.findNodeRef("path", nodePath.split("/"));
if (nodeRef == null) {
throw new JSONException("Path " + nodePath + " not found.");
}
else {
storeProtocol = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_STORE_PROTOCOL);
storeIdentifier = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_STORE_IDENTIFIER);
nodeUUID = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_NODE_UUID);
value = storeProtocol.concat("://").concat(storeIdentifier).concat("/").concat(nodeUUID);
}
}
if (PROP_SCRIPT.equals(key)) {
propValue = value;
}
else if (PROP_DESTINATION.equals(key)) {
propValue = nodeRef;
}
}
if (propValue == null) {
if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false")) {
propValue = jsonObj.getBoolean(key);
}
}
if (propValue == null) {
try {
propValue = jsonObj.getInt(key);
}
catch (JSONException jsone) {
logger.info(jsone.getMessage());
}
}
if (propValue == null) {
try {
propValue = jsonObj.getDouble(key);
}
catch (JSONException jsone) {
logger.info(jsone.getMessage());
}
}
if (propValue == null) {
try {
propValue = jsonObj.getLong(key);
}
catch (JSONException jsone) {
logger.info(jsone.getMessage());
}
}
if (propValue == null) {
// default to String
propValue = value;
}
propMap.put(key, propValue);
}
return propMap;
}
}
Reference: site