RM-591: A records manager can define a rule that creates folders based on a creation strategy

* RM actions can be configured to allow parmater level substitution
 * Paramater processor component added .. resposibile for processing parameters of an RM action and selecting the correct processor
 * Parameter processor framework added .. new processors can be sprung in using the usual pattern
 * 'node' processor added .. allows simple property value substitution based on the actioned upon node
 * 'date' processor added .. allows simple date value substituation
 * 'message' processor added .. allows message bundle value substitution
 * an example substitution parameter could be .. "/${message.my-company.name}/invoices/${date.month.short}"
 * fileTo action is configured to allow parameter substitution .. with the relative path and auto create features as well it is possible to define a rule that creates records folders based on a simple creation strategy .. for example always file into a record folder for this month



git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/HEAD@47262 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Roy Wetherall
2013-02-28 07:21:26 +00:00
parent c626585c1a
commit 81806ae2f9
16 changed files with 770 additions and 126 deletions

View File

@@ -0,0 +1,58 @@
/*
* Copyright (C) 2005-2013 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.module.org_alfresco_module_rm.action;
import org.alfresco.repo.action.executer.ActionExecuterAbstractBase;
import org.alfresco.repo.action.parameter.ParameterProcessorComponent;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* Extension to action implementation hierarchy to insert parameter substitution processing.
*
* @author Roy Wetherall
* @since 2.1
*/
public abstract class PropertySubActionExecuterAbstractBase extends ActionExecuterAbstractBase
{
private ParameterProcessorComponent parameterProcessorComponent;
protected boolean allowParameterSubstitutions = false;
public void setParameterProcessorComponent(ParameterProcessorComponent parameterProcessorComponent)
{
this.parameterProcessorComponent = parameterProcessorComponent;
}
public void setAllowParameterSubstitutions(boolean allowParameterSubstitutions)
{
this.allowParameterSubstitutions = allowParameterSubstitutions;
}
@Override
public void execute(Action action, NodeRef actionedUponNodeRef)
{
if (allowParameterSubstitutions == true)
{
parameterProcessorComponent.process(action, getActionDefinition(), actionedUponNodeRef);
}
super.execute(action, actionedUponNodeRef);
}
}

View File

@@ -69,7 +69,7 @@ import org.springframework.util.StringUtils;
*
* @author Roy Wetherall
*/
public abstract class RMActionExecuterAbstractBase extends ActionExecuterAbstractBase
public abstract class RMActionExecuterAbstractBase extends PropertySubActionExecuterAbstractBase
implements RecordsManagementAction,
RecordsManagementModel,
BeanNameAware
@@ -392,8 +392,6 @@ public abstract class RMActionExecuterAbstractBase extends ActionExecuterAbstra
*/
public RecordsManagementActionResult execute(NodeRef filePlanComponent, Map<String, Serializable> parameters)
{
//isExecutableImpl(filePlanComponent, parameters, true);
// Create the action
Action action = this.actionService.createAction(name);
action.setParameterValues(parameters);
@@ -440,22 +438,6 @@ public abstract class RMActionExecuterAbstractBase extends ActionExecuterAbstra
// No parameters
}
// /**
// * @see org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction#isExecutable(org.alfresco.service.cmr.repository.NodeRef, java.util.Map)
// */
// public boolean isExecutable(NodeRef filePlanComponent, Map<String, Serializable> parameters)
// {
// return isExecutableImpl(filePlanComponent, parameters, false);
// }
//
// /**
// * @param filePlanComponent
// * @param parameters
// * @param throwException
// * @return
// */
// protected abstract boolean isExecutableImpl(NodeRef filePlanComponent, Map<String, Serializable> parameters, boolean throwException);
/**
* By default, rmActions do not provide an implicit target nodeRef.
*/

View File

@@ -6,9 +6,7 @@ import java.util.List;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase;
import org.alfresco.repo.action.ActionImpl;
import org.alfresco.repo.action.ParameterDefinitionImpl;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ParameterDefinition;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
@@ -83,25 +81,15 @@ public class FileToAction extends RMActionExecuterAbstractBase
{
// TODO .. what if a record of the same name already exists in the destination record folder??
final NodeRef finalRecordFolder = recordFolder;
// AuthenticationUtil.runAsSystem(new RunAsWork<Void>()
// {
// @Override
// public Void doWork() throws Exception
// {
try
{
// TODO .. why do I have to execute this as system .. I should have permission to do this!!!
fileFolderService.move(actionedUponNodeRef, finalRecordFolder, null);
}
catch (FileNotFoundException fileNotFound)
{
throw new AlfrescoRuntimeException("Unable to execute file to action, because the move operation failed.", fileNotFound);
}
//
// return null;
// }
// });
final NodeRef finalRecordFolder = recordFolder;
try
{
fileFolderService.move(actionedUponNodeRef, finalRecordFolder, null);
}
catch (FileNotFoundException fileNotFound)
{
throw new AlfrescoRuntimeException("Unable to execute file to action, because the move operation failed.", fileNotFound);
}
}
else
{
@@ -138,6 +126,7 @@ public class FileToAction extends RMActionExecuterAbstractBase
// look for the path parameter
String path = (String)action.getParameterValue(PARAM_PATH);
String[] pathValues = ArrayUtils.EMPTY_STRING_ARRAY;
if (path != null && path.isEmpty() == false)
{
pathValues = StringUtils.tokenizeToStringArray(path, "/", false, true);
@@ -193,25 +182,15 @@ public class FileToAction extends RMActionExecuterAbstractBase
private NodeRef resolvePath(final NodeRef context, final String[] pathValues)
{
NodeRef result = null;
//FileInfo fileInfo = AuthenticationUtil.runAsSystem(new RunAsWork<FileInfo>()
//{
// @Override
// public FileInfo doWork() throws Exception
// {
FileInfo fileInfo = null;
try
{
fileInfo = fileFolderService.resolveNamePath(context, new ArrayList<String>(Arrays.asList(pathValues)), false);
}
catch (FileNotFoundException e)
{
// ignore, checking for null
}
// return fileInfo;
// }
//});
FileInfo fileInfo = null;
try
{
fileInfo = fileFolderService.resolveNamePath(context, new ArrayList<String>(Arrays.asList(pathValues)), false);
}
catch (FileNotFoundException e)
{
// ignore, checking for null
}
if (fileInfo != null)
{
result = fileInfo.getNodeRef();

View File

@@ -0,0 +1,113 @@
/*
* Copyright (C) 2005-2013 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.action.parameter;
import java.util.Calendar;
import java.util.Locale;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* Date parameter processor.
*
* @author Roy Wetherall
* @since 2.1
*/
public class DateParameterProcessor extends ParameterProcessor
{
private static final String MONTH = "month";
private static final String YEAR = "year";
private static final String SHORT = "short";
private static final String LONG = "long";
/**
* @see org.alfresco.repo.action.parameter.ParameterProcessor#process(java.lang.String, org.alfresco.service.cmr.repository.NodeRef)
*/
@Override
public String process(String value, NodeRef actionedUponNodeRef)
{
// the default position is to return the value un-changed
String result = value;
// strip the processor name from the value
value = stripName(value);
if (value.isEmpty() == false)
{
String[] values = value.split("\\.", 2);
Calendar calendar = Calendar.getInstance();
int field = getField(values);
if (Calendar.YEAR == field)
{
result = Integer.toString(calendar.get(field));
}
else
{
result = calendar.getDisplayName(field, getStyle(values), Locale.getDefault());
}
}
return result;
}
private int getField(String[] values)
{
int result = 0;
String field = values[0];
if (MONTH.equals(field) == true)
{
result = Calendar.MONTH;
}
else if (YEAR.equals(field) == true)
{
result = Calendar.YEAR;
}
else
{
throw new AlfrescoRuntimeException("Date component " + field + " is not supported by parameter substitution.");
}
return result;
}
private int getStyle(String[] values)
{
int result = Calendar.SHORT;
if (values.length == 2)
{
String style = values[1];
if (LONG.equals(style) == true)
{
result = Calendar.LONG;
}
else if (SHORT.equals(style) == true)
{
result = Calendar.SHORT;
}
else
{
throw new AlfrescoRuntimeException("Style component " + style + " is not supported by parameter substitution.");
}
}
return result;
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright (C) 2005-2013 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.action.parameter;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.springframework.extensions.surf.util.I18NUtil;
/**
* Message parameter processor.
*
* @author Roy Wetherall
* @since 2.1
*/
public class MessageParameterProcessor extends ParameterProcessor
{
/**
* @see org.alfresco.repo.action.parameter.ParameterProcessor#process(java.lang.String, org.alfresco.service.cmr.repository.NodeRef)
*/
@Override
public String process(String value, NodeRef actionedUponNodeRef)
{
// the default position is to return the value un-changed
String result = value;
// strip the processor name from the value
value = stripName(value);
if (value.isEmpty() == false)
{
result = I18NUtil.getMessage(value);
if (result == null)
{
throw new AlfrescoRuntimeException("The message parameter processor could not resolve the message for the id " + value);
}
}
return result;
}
}

View File

@@ -0,0 +1,121 @@
/*
* Copyright (C) 2005-2013 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.action.parameter;
import java.io.Serializable;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.apache.commons.lang.ArrayUtils;
/**
* Node parameter processor.
*
* @author Roy Wetherall
* @since 2.1
*/
public class NodeParameterProcessor extends ParameterProcessor
{
/** Supported data types */
private QName[] supportedDataTypes =
{
DataTypeDefinition.TEXT,
DataTypeDefinition.BOOLEAN,
DataTypeDefinition.DATE,
DataTypeDefinition.DATETIME,
DataTypeDefinition.DOUBLE,
DataTypeDefinition.FLOAT,
DataTypeDefinition.INT
};
/** Node service */
private NodeService nodeService;
/** Namespace service */
private NamespaceService namespaceService;
/** Dictionary service */
private DictionaryService dictionaryService;
/**
* @param nodeService node service
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* @param namespaceService namespace service
*/
public void setNamespaceService(NamespaceService namespaceService)
{
this.namespaceService = namespaceService;
}
/**
* @param dictionaryService dictionary service
*/
public void setDictionaryService(DictionaryService dictionaryService)
{
this.dictionaryService = dictionaryService;
}
/**
* @see org.alfresco.repo.action.parameter.ParameterProcessor#process(java.lang.String, org.alfresco.service.cmr.repository.NodeRef)
*/
@Override
public String process(String value, NodeRef actionedUponNodeRef)
{
// the default position is to return the value un-changed
String result = value;
// strip the processor name from the value
value = stripName(value);
if (value.isEmpty() == false)
{
QName qname = QName.createQName(value, namespaceService);
PropertyDefinition propertyDefinition = dictionaryService.getProperty(qname);
if (propertyDefinition == null)
{
throw new AlfrescoRuntimeException("The property " + value + " does not have a property definition.");
}
QName type = propertyDefinition.getDataType().getName();
if (ArrayUtils.contains(supportedDataTypes, type) == true)
{
Serializable propertyValue = nodeService.getProperty(actionedUponNodeRef, qname);
result = propertyValue.toString();
}
else
{
throw new AlfrescoRuntimeException("The property " + value + " is of type " + type.toString() + " which is not supported by parameter substitution.");
}
}
return result;
}
}

View File

@@ -0,0 +1,94 @@
/*
* Copyright (C) 2005-2013 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.action.parameter;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* Abstract parameter processor implementation.
*
* @author Roy Wetherall
* @since 2.1
*/
public abstract class ParameterProcessor
{
/** Processor name */
private String name;
/** Parameter processor component */
private ParameterProcessorComponent parameterProcessorComponent;
/**
* @return parameter processor name
*/
public String getName()
{
return name;
}
/**
* @param name parameter processor name
*/
public void setName(String name)
{
this.name = name;
}
/**
* @param parameterProcessorComponent parameter processor component
*/
public void setParameterProcessorComponent(ParameterProcessorComponent parameterProcessorComponent)
{
this.parameterProcessorComponent = parameterProcessorComponent;
}
/**
* Init method
*/
public void init()
{
parameterProcessorComponent.register(this);
}
/**
* Process the parameter value.
*
* @param value substitution value
* @param actionedUponNodeRef actioned upon node reference
* @return String processed string, original string if subs string invalid
*/
public abstract String process(String value, NodeRef actionedUponNodeRef);
/**
* Strips the name of the processor from the subs value.
*
* @param value subs value
* @return String subs value with the name and '.' delimiter removed
*/
protected String stripName(String value)
{
String result = "";
String[] values = value.split("\\.", 2);
if (values.length == 2)
{
result = values[1];
}
return result;
}
}

View File

@@ -0,0 +1,124 @@
/*
* Copyright (C) 2005-2013 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.action.parameter;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.service.cmr.action.ParameterDefinition;
import org.alfresco.service.cmr.action.ParameterizedItem;
import org.alfresco.service.cmr.action.ParameterizedItemDefinition;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.repository.NodeRef;
/**
*
*
* @author Roy Wetherall
* @since 2.1
*/
public class ParameterProcessorComponent
{
private static final String REG_EX = "\\$\\{([^\\$\\{]+)\\}";
private Map<String, ParameterProcessor> processors = new HashMap<String, ParameterProcessor>(5);
/**
*
* @param processor
*/
public void register(ParameterProcessor processor)
{
this.processors.put(processor.getName(), processor);
}
/**
*
* @param ruleItem
* @param ruleItemDefinition
* @param actionedUponNodeRef
*/
public void process(ParameterizedItem ruleItem, ParameterizedItemDefinition ruleItemDefinition, NodeRef actionedUponNodeRef)
{
for (Map.Entry<String, Serializable> entry : ruleItem.getParameterValues().entrySet())
{
String parameterName = entry.getKey();
// get the parameter definition
ParameterDefinition def = ruleItemDefinition.getParameterDefintion(parameterName);
if (def != null)
{
if (DataTypeDefinition.TEXT.equals(def.getType()) == true)
{
String parameterValue = (String)entry.getValue();
// match the substitution pattern
Pattern patt = Pattern.compile(REG_EX);
Matcher m = patt.matcher(parameterValue);
StringBuffer sb = new StringBuffer(parameterValue.length());
while (m.find())
{
String text = m.group(1);
// lookup parameter processor to use
ParameterProcessor processor = lookupProcessor(text);
if (processor == null)
{
throw new AlfrescoRuntimeException("A parameter processor has not been found for the substitution string " + text);
}
else
{
// process each substitution value
text = processor.process(text, actionedUponNodeRef);
}
// append new value
m.appendReplacement(sb, Matcher.quoteReplacement(text));
}
m.appendTail(sb);
// set the updated parameter value
ruleItem.setParameterValue(parameterName, sb.toString());
}
}
}
}
private ParameterProcessor lookupProcessor(String value)
{
ParameterProcessor result = null;
if (value != null && value.isEmpty() == false)
{
String[] values = value.split("\\.", 2);
if (values.length != 0)
{
// get the processor from the registered map
result = processors.get(values[0]);
}
}
return result;
}
}