Implemented persistence for the WorkflowFormProcessor.

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@21258 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
N Smith 2010-07-19 10:46:11 +00:00
parent 2bfa74df80
commit 18f23482b6
15 changed files with 516 additions and 107 deletions

View File

@ -1252,7 +1252,8 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
fields.add("bpm:workflowDueDate"); fields.add("bpm:workflowDueDate");
fields.add("packageItems"); fields.add("packageItems");
String workflowDefName = "jbpm$wf:adhoc"; // Use URL-friendly format.
String workflowDefName = "jbpm_wf_adhoc";
Form form = this.formService.getForm(new Item(WORKFLOW_FORM_ITEM_KIND, workflowDefName), fields); Form form = this.formService.getForm(new Item(WORKFLOW_FORM_ITEM_KIND, workflowDefName), fields);
// check a form got returned // check a form got returned
@ -1310,7 +1311,7 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
assertTrue("Expecting there to be more tasks", tasksAfter > tasksBefore); assertTrue("Expecting there to be more tasks", tasksAfter > tasksBefore);
// check workflow instance details // check workflow instance details
assertEquals(workflowDefName, workflow.definition.name); assertEquals("jbpm$wf:adhoc", workflow.definition.name);
} }
public void testNoForm() throws Exception public void testNoForm() throws Exception

View File

@ -33,9 +33,7 @@ public abstract class AbstractFieldProcessor<Data> implements FieldProcessor
public Field generateField(String fieldName, FormCreationData data) public Field generateField(String fieldName, FormCreationData data)
{ {
Data typedData = checkDataType(data.getItemData()); Data typedData = checkDataType(data.getItemData());
Field field = generateTypedField(fieldName, data, typedData); return generateTypedField(fieldName, data, typedData);
logIfFieldNotFound(field, fieldName);
return field;
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -53,18 +51,6 @@ public abstract class AbstractFieldProcessor<Data> implements FieldProcessor
} }
} }
protected void logIfFieldNotFound(Field fieldInfo, String fieldName)
{
if (fieldInfo == null)
{
Log logger = getLogger();
if (logger!=null && logger.isDebugEnabled())
{
logger.debug("Ignoring unrecognised field \"" + fieldName + "\"");
}
}
}
/** /**
* Registers this {@link FieldProcessor} with the supplied registry. * Registers this {@link FieldProcessor} with the supplied registry.
* *

View File

@ -24,4 +24,5 @@ import org.alfresco.repo.forms.Field;
public interface FieldProcessor public interface FieldProcessor
{ {
Field generateField(String fieldName, FormCreationData data); Field generateField(String fieldName, FormCreationData data);
} }

View File

@ -0,0 +1,28 @@
/*
* Copyright (C) 2005-2010 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.forms.processor;
/**
* @author Nick Smith
*/
public interface FormPersister<PersistType>
{
PersistType persist();
}

View File

@ -31,14 +31,12 @@ import java.util.regex.Pattern;
import org.alfresco.model.ContentModel; import org.alfresco.model.ContentModel;
import org.alfresco.repo.forms.Field; import org.alfresco.repo.forms.Field;
import org.alfresco.repo.forms.FieldDefinition;
import org.alfresco.repo.forms.Form;
import org.alfresco.repo.forms.FormData; import org.alfresco.repo.forms.FormData;
import org.alfresco.repo.forms.FormException; import org.alfresco.repo.forms.FormException;
import org.alfresco.repo.forms.PropertyFieldDefinition;
import org.alfresco.repo.forms.FormData.FieldData; import org.alfresco.repo.forms.FormData.FieldData;
import org.alfresco.repo.forms.processor.FilteredFormProcessor; import org.alfresco.repo.forms.processor.FilteredFormProcessor;
import org.alfresco.repo.forms.processor.FormCreationData; import org.alfresco.repo.forms.processor.FormCreationData;
import org.alfresco.repo.forms.processor.workflow.DataKeyMatcher;
import org.alfresco.service.cmr.dictionary.AspectDefinition; import org.alfresco.service.cmr.dictionary.AspectDefinition;
import org.alfresco.service.cmr.dictionary.AssociationDefinition; import org.alfresco.service.cmr.dictionary.AssociationDefinition;
import org.alfresco.service.cmr.dictionary.ChildAssociationDefinition; import org.alfresco.service.cmr.dictionary.ChildAssociationDefinition;
@ -146,6 +144,8 @@ public abstract class ContentModelFormProcessor<ItemType, PersistType> extends
*/ */
protected Pattern associationNamePattern = Pattern.compile(ASSOC_DATA_PREFIX + "([a-zA-Z0-9]+)_(.*)(_[a-zA-Z]+)"); protected Pattern associationNamePattern = Pattern.compile(ASSOC_DATA_PREFIX + "([a-zA-Z0-9]+)_(.*)(_[a-zA-Z]+)");
private DataKeyMatcher keyMatcher;
/** /**
* Sets the node service * Sets the node service
* *
@ -184,6 +184,7 @@ public abstract class ContentModelFormProcessor<ItemType, PersistType> extends
public void setNamespaceService(NamespaceService namespaceService) public void setNamespaceService(NamespaceService namespaceService)
{ {
this.namespaceService = namespaceService; this.namespaceService = namespaceService;
this.keyMatcher = new DataKeyMatcher(namespaceService);
} }
/** /**
@ -807,6 +808,7 @@ public abstract class ContentModelFormProcessor<ItemType, PersistType> extends
return mimetype; return mimetype;
} }
} }
/** /**

View File

@ -21,7 +21,6 @@ package org.alfresco.repo.forms.processor.node;
import org.alfresco.repo.forms.Field; import org.alfresco.repo.forms.Field;
import org.alfresco.repo.forms.FieldDefinition; import org.alfresco.repo.forms.FieldDefinition;
import org.alfresco.repo.forms.PropertyFieldDefinition;
import org.alfresco.repo.forms.processor.AbstractFieldProcessor; import org.alfresco.repo.forms.processor.AbstractFieldProcessor;
import org.alfresco.repo.forms.processor.FormCreationData; import org.alfresco.repo.forms.processor.FormCreationData;

View File

@ -27,14 +27,14 @@ import org.alfresco.service.namespace.QName;
*/ */
public class DataKeyInfo public class DataKeyInfo
{ {
private final String dataKey; private final String fieldName;
private final QName qName; private final QName qName;
private final FieldType fieldType; private final FieldType fieldType;
private final boolean isAdd; private final boolean isAdd;
private DataKeyInfo(String dataKey, QName qName, FieldType fieldType, boolean isAdd) private DataKeyInfo(String dataKey, QName qName, FieldType fieldType, boolean isAdd)
{ {
this.dataKey = dataKey; this.fieldName = dataKey;
this.qName = qName; this.qName = qName;
this.fieldType = fieldType; this.fieldType = fieldType;
this.isAdd = isAdd; this.isAdd = isAdd;
@ -50,23 +50,28 @@ public class DataKeyInfo
return new DataKeyInfo(dataKey, qName, FieldType.PROPERTY, true); return new DataKeyInfo(dataKey, qName, FieldType.PROPERTY, true);
} }
public static DataKeyInfo makeTransientDataKeyInfo(String dataKey) public static DataKeyInfo makeTransientPropertyDataKeyInfo(String dataKey)
{ {
return new DataKeyInfo(dataKey, null, FieldType.TRANSIENT, true); return new DataKeyInfo(dataKey, null, FieldType.TRANSIENT_PROPERTY, true);
}
public static DataKeyInfo makeTransientAssociationDataKeyInfo(String dataKey, boolean isAdd)
{
return new DataKeyInfo(dataKey, null, FieldType.TRANSIENT_ASSOCIATION, isAdd);
} }
/** /**
* @return the dataKey * @return the fieldName
*/ */
public String getDataKey() public String getFieldName()
{ {
return dataKey; return fieldName;
} }
/** /**
* @return the qName * @return the qName
*/ */
public QName getqName() public QName getQName()
{ {
return qName; return qName;
} }
@ -88,4 +93,24 @@ public class DataKeyInfo
return isAdd; return isAdd;
} }
/**
* Implements the visitor pattern. Takes a DataKeyInfoVisitor and calls the
* appropriate visit method based on the fieldType.
*
* @param <T>
* @param visitor
* @return
*/
public <T> T visit(DataKeyInfoVisitor<T> visitor)
{
switch(fieldType)
{
case ASSOCIATION: return visitor.visitAssociation(this);
case PROPERTY: return visitor.visitProperty(this);
case TRANSIENT_ASSOCIATION: return visitor.visitTransientAssociation(this);
case TRANSIENT_PROPERTY: return visitor.visitTransientProperty(this);
default: return null; //Should never be reached.
}
}
} }

View File

@ -0,0 +1,60 @@
/*
* Copyright (C) 2005-2010 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.forms.processor.workflow;
/**
* Visitor interface used to enable the visitor pattern on {@link DataKeyInfo}
* instances. Implementations of this interface can call
* <code>DataKeyInfo.visit(DataKeyInfoVisitor)</code> to have the appropriate
* visit method called on the visitor, based on the fieldType of the
* {@link DataKeyInfo} instance.
*
* @author Nick Smith
*/
public interface DataKeyInfoVisitor<T>
{
/**
* Called for {@link DataKeyInfo} instances with a field type of ASSOCIATION.
* @param info
* @return
*/
T visitAssociation(DataKeyInfo info);
/**
* Called for {@link DataKeyInfo} instances with a field type of PROPERTY.
* @param info
* @return
*/
T visitProperty(DataKeyInfo info);
/**
* Called for {@link DataKeyInfo} instances with a field type of TRANSIENT_ASSOCIATION.
* @param info
* @return
*/
T visitTransientAssociation(DataKeyInfo info);
/**
* Called for {@link DataKeyInfo} instances with a field type of TRANSIENT_PROPERTY.
* @param info
* @return
*/
T visitTransientProperty(DataKeyInfo info);
}

View File

@ -40,11 +40,6 @@ public class DataKeyMatcher
*/ */
private final static Pattern propertyNamePattern = Pattern.compile("(^[a-zA-Z0-9]+)_([a-zA-Z0-9_]+$)"); private final static Pattern propertyNamePattern = Pattern.compile("(^[a-zA-Z0-9]+)_([a-zA-Z0-9_]+$)");
public DataKeyMatcher(NamespaceService namespaceService)
{
this.namespaceService = namespaceService;
}
/** /**
* A regular expression which can be used to match association names. These * A regular expression which can be used to match association names. These
* names will look like <code>"assoc_cm_references_added"</code>. The * names will look like <code>"assoc_cm_references_added"</code>. The
@ -53,12 +48,20 @@ public class DataKeyMatcher
*/ */
private final static Pattern associationNamePattern = Pattern.compile("(^[a-zA-Z0-9]+)_([a-zA-Z0-9_]+)(_[a-zA-Z]+$)"); private final static Pattern associationNamePattern = Pattern.compile("(^[a-zA-Z0-9]+)_([a-zA-Z0-9_]+)(_[a-zA-Z]+$)");
private final static Pattern transientAssociationPattern = Pattern.compile("(^[a-zA-Z0-9]+)(_[a-zA-Z]+$)");
private final NamespaceService namespaceService; private final NamespaceService namespaceService;
public DataKeyMatcher(NamespaceService namespaceService)
{
this.namespaceService = namespaceService;
}
/** /**
* * Attempts to match the <code>dataKey</code> to either a property or association pattern.
* @param dataKey * If no match can be found then returns <code>null</code>.
* @return * @param dataKey the dataKey to be matched.
* @return a {@link DataKeyInfo} representation or <code>null</code>.
*/ */
public DataKeyInfo match(String dataKey) public DataKeyInfo match(String dataKey)
{ {
@ -70,7 +73,6 @@ public class DataKeyMatcher
{ {
return matchAssociation(dataKey); return matchAssociation(dataKey);
} }
// No match found. // No match found.
return null; return null;
} }
@ -79,14 +81,33 @@ public class DataKeyMatcher
{ {
String keyName = dataKey.substring(ASSOC_DATA_PREFIX.length()); String keyName = dataKey.substring(ASSOC_DATA_PREFIX.length());
Matcher matcher = associationNamePattern.matcher(keyName); Matcher matcher = associationNamePattern.matcher(keyName);
if (!matcher.matches()) if (matcher.matches())
{ {
return null; QName qName = getQName(matcher);
boolean isAdd = isAdd(matcher, 3);
String name = qName.toPrefixString(namespaceService);
return DataKeyInfo.makeAssociationDataKeyInfo(name, qName, isAdd);
} }
QName qName = getQName(matcher); return matchTransientAssociation(keyName);
String suffix = matcher.group(3); }
private DataKeyInfo matchTransientAssociation(String keyName)
{
Matcher matcher = transientAssociationPattern.matcher(keyName);
if(matcher.matches())
{
boolean isAdd = isAdd(matcher, 2);
String name = matcher.group(1);
return DataKeyInfo.makeTransientAssociationDataKeyInfo(name, isAdd);
}
return null;
}
private boolean isAdd(Matcher matcher, int suffixPos)
{
String suffix = matcher.group(suffixPos);
boolean isAdd = !(ASSOC_DATA_REMOVED_SUFFIX.equals(suffix)); boolean isAdd = !(ASSOC_DATA_REMOVED_SUFFIX.equals(suffix));
return DataKeyInfo.makeAssociationDataKeyInfo(keyName, qName, isAdd); return isAdd;
} }
private DataKeyInfo matchProperty(String dataKey) private DataKeyInfo matchProperty(String dataKey)
@ -96,9 +117,10 @@ public class DataKeyMatcher
if (matcher.matches()) if (matcher.matches())
{ {
QName qName = getQName(matcher); QName qName = getQName(matcher);
return DataKeyInfo.makePropertyDataKeyInfo(keyName, qName); String name = qName.toPrefixString(namespaceService);
return DataKeyInfo.makePropertyDataKeyInfo(name, qName);
} }
return DataKeyInfo.makeTransientDataKeyInfo(keyName); return DataKeyInfo.makeTransientPropertyDataKeyInfo(keyName);
} }
private QName getQName(Matcher matcher) private QName getQName(Matcher matcher)

View File

@ -27,5 +27,6 @@ public enum FieldType
{ {
ASSOCIATION, ASSOCIATION,
PROPERTY, PROPERTY,
TRANSIENT; TRANSIENT_ASSOCIATION,
TRANSIENT_PROPERTY;
} }

View File

@ -51,11 +51,11 @@ import org.apache.commons.logging.LogFactory;
public class TaskFormProcessor extends ContentModelFormProcessor<WorkflowTask, WorkflowTask> public class TaskFormProcessor extends ContentModelFormProcessor<WorkflowTask, WorkflowTask>
{ {
/** Logger */ /** Logger */
private static final Log LOGGER = LogFactory.getLog(TaskFormProcessor.class); private static final Log LOGGER = LogFactory.getLog(TaskFormProcessor.class);
private static final TypedPropertyValueGetter valueGetter = new TypedPropertyValueGetter();
private DataKeyMatcher keyMatcher; private TypedPropertyValueGetter valueGetter;
private WorkflowService workflowService; private DataKeyMatcher keyMatcher;
private WorkflowService workflowService;
// Constructor for Spring // Constructor for Spring
public TaskFormProcessor() public TaskFormProcessor()
@ -72,6 +72,7 @@ public class TaskFormProcessor extends ContentModelFormProcessor<WorkflowTask, W
this.dictionaryService = dictionaryService; this.dictionaryService = dictionaryService;
this.fieldProcessorRegistry = fieldProcessorRegistry; this.fieldProcessorRegistry = fieldProcessorRegistry;
this.keyMatcher = new DataKeyMatcher(namespaceService); this.keyMatcher = new DataKeyMatcher(namespaceService);
this.valueGetter = new TypedPropertyValueGetter(dictionaryService);
} }
@Override @Override
@ -101,31 +102,30 @@ public class TaskFormProcessor extends ContentModelFormProcessor<WorkflowTask, W
{ {
String name = fieldData.getName(); String name = fieldData.getName();
DataKeyInfo keyInfo = keyMatcher.match(name); DataKeyInfo keyInfo = keyMatcher.match(name);
if ((keyInfo == null || FieldType.TRANSIENT == keyInfo.getFieldType()) && if (keyInfo == null ||
LOGGER.isWarnEnabled()) FieldType.TRANSIENT_PROPERTY == keyInfo.getFieldType() )
{ {
LOGGER.warn("Ignoring unrecognized field: " + name); if(LOGGER.isDebugEnabled())
LOGGER.debug("Ignoring unrecognized field: " + name);
return;
} }
if (keyInfo != null) QName fullName = keyInfo.getQName();
Object rawValue = fieldData.getValue();
if (FieldType.PROPERTY == keyInfo.getFieldType())
{ {
QName fullName = keyInfo.getqName(); Serializable propValue = getPropertyValueToPersist(fullName, rawValue, itemData);
Object rawValue = fieldData.getValue(); // TODO What if the user wants to set prop to null?
if (FieldType.PROPERTY == keyInfo.getFieldType()) if (propValue != null)
{ {
Serializable propValue = getPropertyValueToPersist(fullName, rawValue, itemData); updater.addProperty(fullName, propValue);
// TODO What if the user wants to set prop to null?
if (propValue != null)
{
updater.addProperty(fullName, propValue);
}
} }
else if (FieldType.ASSOCIATION == keyInfo.getFieldType()) }
else if (FieldType.ASSOCIATION == keyInfo.getFieldType())
{
if (rawValue instanceof String)
{ {
if (rawValue instanceof String) updater.changeAssociation(fullName, (String) rawValue, keyInfo.isAdd());
{
updater.changeAssociation(fullName, (String) rawValue, keyInfo.isAdd());
}
} }
} }
} }
@ -143,7 +143,7 @@ public class TaskFormProcessor extends ContentModelFormProcessor<WorkflowTask, W
{ {
return valueGetter.getValue(value, propDef); return valueGetter.getValue(value, propDef);
} }
return null; return (Serializable) value;
} }
/* /*
@ -232,4 +232,13 @@ public class TaskFormProcessor extends ContentModelFormProcessor<WorkflowTask, W
this.keyMatcher = new DataKeyMatcher(namespaceService); this.keyMatcher = new DataKeyMatcher(namespaceService);
} }
/* (non-Javadoc)
* @see org.alfresco.repo.forms.processor.node.ContentModelFormProcessor#setDictionaryService(org.alfresco.service.cmr.dictionary.DictionaryService)
*/
@Override
public void setDictionaryService(DictionaryService dictionaryService)
{
super.setDictionaryService(dictionaryService);
this.valueGetter = new TypedPropertyValueGetter(dictionaryService);
}
} }

View File

@ -26,7 +26,6 @@
package org.alfresco.repo.forms.processor.workflow; package org.alfresco.repo.forms.processor.workflow;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -88,7 +87,7 @@ public class TaskUpdater
public boolean changeAssociation(QName name, String nodeRefs, boolean isAdd) public boolean changeAssociation(QName name, String nodeRefs, boolean isAdd)
{ {
List<NodeRef> value = getNodeRefs(nodeRefs); List<NodeRef> value = NodeRef.getNodeRefs(nodeRefs, LOGGER);
if (value == null) if (value == null)
{ {
return false; return false;
@ -120,34 +119,4 @@ public class TaskUpdater
return map; return map;
} }
private List<NodeRef> getNodeRefs(Object value)
{
String[] nodeRefIds = ((String) value).split(",");
List<NodeRef> nodeRefs = new ArrayList<NodeRef>(nodeRefIds.length);
for (String nodeRefString : nodeRefIds)
{
String nodeRefId = nodeRefString.trim();
if (NodeRef.isNodeRef(nodeRefId))
{
NodeRef nodeRef = new NodeRef(nodeRefId);
nodeRefs.add(nodeRef);
}
else
{
logNodeRefError(nodeRefId);
}
}
return nodeRefs;
}
private void logNodeRefError(String nodeRefId)
{
if (LOGGER.isWarnEnabled())
{
StringBuilder msg = new StringBuilder();
msg.append("Target Node: ").append(nodeRefId);
msg.append(" is not a valid NodeRef and has been ignored.");
LOGGER.warn(msg.toString());
}
}
} }

View File

@ -31,8 +31,11 @@ import java.util.Arrays;
import java.util.List; import java.util.List;
import org.alfresco.repo.forms.FormException; import org.alfresco.repo.forms.FormException;
import org.alfresco.repo.forms.processor.node.ItemData;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition; 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.dictionary.PropertyDefinition;
import org.alfresco.service.namespace.QName;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
import org.springframework.extensions.surf.util.I18NUtil; import org.springframework.extensions.surf.util.I18NUtil;
@ -44,6 +47,27 @@ public class TypedPropertyValueGetter
{ {
public static final String ON = "on"; public static final String ON = "on";
private final DictionaryService dictionaryService;
public TypedPropertyValueGetter(DictionaryService dictionaryService)
{
this.dictionaryService = dictionaryService;
}
public Serializable getPropertyValueToPersist(QName fullName, Object value, ItemData<?> itemData)
{
PropertyDefinition propDef = itemData.getPropertyDefinition(fullName);
if (propDef == null)
{
propDef = dictionaryService.getProperty(fullName);
}
if (propDef != null)
{
return getValue(value, propDef);
}
return (Serializable) value;
}
public Serializable getValue(Object value, PropertyDefinition propDef) public Serializable getValue(Object value, PropertyDefinition propDef)
{ {
if (value == null) if (value == null)

View File

@ -0,0 +1,165 @@
/*
* Copyright (C) 2005-2010 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.forms.processor.workflow;
import java.io.Serializable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.workflow.WorkflowModel;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.workflow.WorkflowDefinition;
import org.alfresco.service.cmr.workflow.WorkflowException;
import org.alfresco.service.cmr.workflow.WorkflowInstance;
import org.alfresco.service.cmr.workflow.WorkflowPath;
import org.alfresco.service.cmr.workflow.WorkflowService;
import org.alfresco.service.cmr.workflow.WorkflowTask;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
/**
* A helper class used to start workflows. The builder accumuates all the
* changes to be made to the list of parameters and package items, then starts a
* new workflow once the build() method is called.
*
* @author Nick Smith
*/
public class WorkflowBuilder
{
private final WorkflowService workflowService;
private final WorkflowDefinition definition;
private final NodeService nodeService;
private final Map<QName, Serializable> params = new HashMap<QName, Serializable>();
private final Set<NodeRef> packageItems = new HashSet<NodeRef>();
private NodeRef packageNode = null;
public WorkflowBuilder(WorkflowDefinition definition, WorkflowService workflowService, NodeService nodeService)
{
this.workflowService = workflowService;
this.nodeService = nodeService;
this.definition = definition;
}
public void addParameter(QName name, Serializable value)
{
params.put(name, value);
}
public void addAssociationParameter(QName name, Serializable value)
{
params.put(name, value);
}
public void addPackageItems(List<NodeRef> items)
{
packageItems.addAll(items);
}
/**
* Takes a comma-separated list of {@link NodeRef} ids and adds the
* specified NodeRefs to the package.
*
* @param items
*/
public void addPackageItems(String items)
{
List<NodeRef> nodes = NodeRef.getNodeRefs(items);
addPackageItems(nodes);
}
public void addPackageItemsAsStrings(List<String> itemStrs)
{
for (String itemStr : itemStrs)
{
addPackageItem(itemStr);
}
}
public void addPackageItem(NodeRef item)
{
packageItems.add(item);
}
public void addPackageItem(String itemStr)
{
packageItems.add(new NodeRef(itemStr));
}
/**
* @param packageNode the packageNode to set
*/
public void setPackageNode(NodeRef packageNode)
{
this.packageNode = packageNode;
}
public WorkflowInstance build()
{
buildPackage();
WorkflowPath path = workflowService.startWorkflow(definition.id, params);
signalStartTask(path);
return path.instance;
}
private void signalStartTask(WorkflowPath path)
{
List<WorkflowTask> tasks = workflowService.getTasksForWorkflowPath(path.id);
if(tasks.size() == 1)
{
WorkflowTask startTask = tasks.get(0);
workflowService.endTask(startTask.id, null);
}
else
throw new WorkflowException("Start task not found! Expected 1 task but found: " + tasks.size());
}
private void buildPackage()
{
final NodeRef packageRef = workflowService.createPackage(packageNode);
final String url = NamespaceService.CONTENT_MODEL_1_0_URI;
final QName packageContains = WorkflowModel.ASSOC_PACKAGE_CONTAINS;
AuthenticationUtil.runAs(new RunAsWork<Void>()
{
public Void doWork() throws Exception
{
for (NodeRef item : packageItems)
{
String name =
(String) nodeService.getProperty(item, ContentModel.PROP_NAME);
String localName = QName.createValidLocalName(name);
QName qName = QName.createQName(url, localName);
nodeService.addChild(packageRef, item, packageContains, qName);
}
return null;
}
}, AuthenticationUtil.getSystemUserName());
params.put(WorkflowModel.ASSOC_PACKAGE, packageRef);
}
}

View File

@ -28,11 +28,14 @@ import java.util.regex.Matcher;
import org.alfresco.model.ContentModel; import org.alfresco.model.ContentModel;
import org.alfresco.repo.forms.FormData; import org.alfresco.repo.forms.FormData;
import org.alfresco.repo.forms.FormException;
import org.alfresco.repo.forms.FormNotFoundException; import org.alfresco.repo.forms.FormNotFoundException;
import org.alfresco.repo.forms.Item; import org.alfresco.repo.forms.Item;
import org.alfresco.repo.forms.FormData.FieldData; import org.alfresco.repo.forms.FormData.FieldData;
import org.alfresco.repo.forms.processor.node.ContentModelFormProcessor; import org.alfresco.repo.forms.processor.node.ContentModelFormProcessor;
import org.alfresco.repo.forms.processor.node.ItemData;
import org.alfresco.repo.workflow.WorkflowModel; import org.alfresco.repo.workflow.WorkflowModel;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.dictionary.TypeDefinition; import org.alfresco.service.cmr.dictionary.TypeDefinition;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.NodeService;
@ -66,6 +69,10 @@ public class WorkflowFormProcessor extends ContentModelFormProcessor<WorkflowDef
/** Unprotected Node Service */ /** Unprotected Node Service */
private NodeService unprotectedNodeService; private NodeService unprotectedNodeService;
/** TyepdPropertyValueGetter */
private TypedPropertyValueGetter valueGetter;
private DataKeyMatcher keyMatcher;
/* (non-Javadoc) /* (non-Javadoc)
* @see org.alfresco.repo.forms.processor.node.ContentModelFormProcessor#getAssociationValues(java.lang.Object) * @see org.alfresco.repo.forms.processor.node.ContentModelFormProcessor#getAssociationValues(java.lang.Object)
*/ */
@ -188,13 +195,39 @@ public class WorkflowFormProcessor extends ContentModelFormProcessor<WorkflowDef
return defName; return defName;
} }
/*
* @see /* (non-Javadoc)
* org.alfresco.repo.forms.processor.node.NodeFormProcessor#internalPersist * @see org.alfresco.repo.forms.processor.FilteredFormProcessor#internalPersist(java.lang.Object, org.alfresco.repo.forms.FormData)
* (java.lang.Object, org.alfresco.repo.forms.FormData)
*/ */
@Override @Override
protected WorkflowInstance internalPersist(WorkflowDefinition workflowDef, final FormData data) protected WorkflowInstance internalPersist(WorkflowDefinition definition, FormData data)
{
WorkflowBuilder builder = new WorkflowBuilder(definition, workflowService, nodeService);
ItemData<WorkflowDefinition> itemData = makeItemData(definition);
for (FieldData fieldData : data)
{
addFieldToSerialize(builder, itemData, fieldData);
}
return builder.build();
}
private void addFieldToSerialize(WorkflowBuilder builder, ItemData<WorkflowDefinition> itemData, FieldData fieldData)
{
String dataKeyName = fieldData.getName();
DataKeyInfo keyInfo = keyMatcher.match(dataKeyName);
if (keyInfo == null ||
FieldType.TRANSIENT_PROPERTY == keyInfo.getFieldType() )
{
if(logger.isDebugEnabled())
logger.debug("Ignoring unrecognized field: " + dataKeyName);
return;
}
WorkflowDataKeyInfoVisitor visitor = new WorkflowDataKeyInfoVisitor(fieldData.getValue(), builder, itemData);
keyInfo.visit(visitor);
}
protected WorkflowInstance oldInternalPersist(WorkflowDefinition workflowDef, final FormData data)
{ {
if (logger.isDebugEnabled()) logger.debug("Persisting form for: " + workflowDef); if (logger.isDebugEnabled()) logger.debug("Persisting form for: " + workflowDef);
@ -285,4 +318,88 @@ public class WorkflowFormProcessor extends ContentModelFormProcessor<WorkflowDef
{ {
this.workflowService = workflowService; this.workflowService = workflowService;
} }
/* (non-Javadoc)
* @see org.alfresco.repo.forms.processor.node.ContentModelFormProcessor#setNamespaceService(org.alfresco.service.namespace.NamespaceService)
*/
@Override
public void setNamespaceService(NamespaceService namespaceService)
{
super.setNamespaceService(namespaceService);
this.keyMatcher = new DataKeyMatcher(namespaceService);
}
/* (non-Javadoc)
* @see org.alfresco.repo.forms.processor.node.ContentModelFormProcessor#setDictionaryService(org.alfresco.service.cmr.dictionary.DictionaryService)
*/
@Override
public void setDictionaryService(DictionaryService dictionaryService)
{
super.setDictionaryService(dictionaryService);
this.valueGetter = new TypedPropertyValueGetter(dictionaryService);
}
private class WorkflowDataKeyInfoVisitor implements DataKeyInfoVisitor<Void>
{
private final Object rawValue;
private final WorkflowBuilder builder;
private final ItemData<WorkflowDefinition> itemData;
public WorkflowDataKeyInfoVisitor(Object rawValue, WorkflowBuilder builder,
ItemData<WorkflowDefinition> itemData)
{
this.rawValue = rawValue;
this.builder = builder;
this.itemData = itemData;
}
/* (non-Javadoc)
* @see org.alfresco.repo.forms.processor.workflow.DataKeyInfoVisitor#visitAssociation(org.alfresco.repo.forms.processor.workflow.DataKeyInfo)
*/
public Void visitAssociation(DataKeyInfo info)
{
QName qName = info.getQName();
if (rawValue instanceof String)
{
Serializable nodes = (Serializable) NodeRef.getNodeRefs((String) rawValue);
builder.addParameter(qName, nodes);
}
return null;
}
/* (non-Javadoc)
* @see org.alfresco.repo.forms.processor.workflow.DataKeyInfoVisitor#visitProperty(org.alfresco.repo.forms.processor.workflow.DataKeyInfo)
*/
public Void visitProperty(DataKeyInfo info)
{
QName qName = info.getQName();
Serializable propValue = valueGetter.getPropertyValueToPersist(qName, rawValue, itemData);
builder.addParameter(qName, propValue);
return null;
}
/* (non-Javadoc)
* @see org.alfresco.repo.forms.processor.workflow.DataKeyInfoVisitor#visitTransientAssociation(org.alfresco.repo.forms.processor.workflow.DataKeyInfo)
*/
public Void visitTransientAssociation(DataKeyInfo info)
{
if(PackageItemsFieldProcessor.KEY.equals(info.getFieldName()))
{
if(rawValue instanceof String)
{
builder.addPackageItems((String)rawValue);
}
}
return null;
}
/* (non-Javadoc)
* @see org.alfresco.repo.forms.processor.workflow.DataKeyInfoVisitor#visitTransientProperty(org.alfresco.repo.forms.processor.workflow.DataKeyInfo)
*/
public Void visitTransientProperty(DataKeyInfo info)
{
throw new FormException("This methdo should never be called!");
}
}
} }