Fixed ALF-4751 (When starting a workflow with no form configuration duplicated fields are seen), ALF-4859 (Lifecycle review and approve form is incorrect) & ALF-4649 (Node based forms render a form containing system properties when no configuration is present)

The set of default fields for each type of form processor has been updated, default node and type forms now don't return the 'sys' properties previously seen, a default task form no longer returns the identifier field and a default workflow form no longer returns the task level description, priority and due date fields.

Also completed a coding standards and comments sweep across all forms code.

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@22734 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Gavin Cornwell
2010-09-28 09:35:45 +00:00
parent ea0b6d7602
commit 778078e26c
40 changed files with 300 additions and 215 deletions

View File

@@ -263,7 +263,7 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
// check the field definitions
Collection<FieldDefinition> fieldDefs = form.getFieldDefinitions();
assertNotNull("Expecting to find fields", fieldDefs);
assertEquals("Expecting to find 22 fields", 22, fieldDefs.size());
assertEquals("Expecting to find 18 fields", 18, fieldDefs.size());
// create a Map of the field definitions
// NOTE: we can safely do this as we know there are no duplicate field names and we're not
@@ -302,6 +302,12 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
assertNotNull("Expecting to find the cm:sentdate field", sentDateField);
assertNotNull("Expecting to find the cm:references field", referencesField);
// check the system properties are not present by default
assertFalse(fieldDefMap.containsKey("sys:node-dbid"));
assertFalse(fieldDefMap.containsKey("sys:store-identifier"));
assertFalse(fieldDefMap.containsKey("sys:node-uuid"));
assertFalse(fieldDefMap.containsKey("sys:store-protocol"));
// check the labels of all the fields
assertEquals("Expecting cm:name label to be " + LABEL_NAME,
LABEL_NAME, nameField.getLabel());
@@ -655,7 +661,7 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
// check the field definitions
Collection<FieldDefinition> fieldDefs = form.getFieldDefinitions();
assertNotNull("Expecting to find fields", fieldDefs);
assertEquals("Expecting to find 11 fields", 11, fieldDefs.size());
assertEquals("Expecting to find 7 fields", 7, fieldDefs.size());
// create a Map of the field definitions
// NOTE: we can safely do this as we know there are no duplicate field names and we're not
@@ -674,6 +680,12 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
assertNotNull("Expecting to find the cm:name field", nameField);
assertNotNull("Expecting to find the cm:contains field", containsField);
// check the system properties are not present by default
assertFalse(fieldDefMap.containsKey("sys:node-dbid"));
assertFalse(fieldDefMap.containsKey("sys:store-identifier"));
assertFalse(fieldDefMap.containsKey("sys:node-uuid"));
assertFalse(fieldDefMap.containsKey("sys:store-protocol"));
// check the labels of all the fields
assertEquals("Expecting cm:name label to be " + LABEL_NAME,
LABEL_NAME, nameField.getLabel());
@@ -828,7 +840,7 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
// check the field definitions
Collection<FieldDefinition> fieldDefs = form.getFieldDefinitions();
assertNotNull("Expecting to find fields", fieldDefs);
assertEquals("Expecting to find 11 fields", 11, fieldDefs.size());
assertEquals("Expecting to find 7 fields", 7, fieldDefs.size());
// create a Map of the field definitions
// NOTE: we can safely do this as we know there are no duplicate field names and we're not
@@ -1301,7 +1313,6 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
private List<String> getExpectedTaskFields()
{
ArrayList<String> fields = new ArrayList<String>(4);
fields.add(WorkflowModel.PROP_TASK_ID.toPrefixString(namespaceService));
fields.add(WorkflowModel.PROP_DESCRIPTION.toPrefixString(namespaceService));
fields.add(WorkflowModel.PROP_STATUS.toPrefixString(namespaceService));
fields.add(WorkflowModel.PROP_PRIORITY.toPrefixString(namespaceService));

View File

@@ -24,6 +24,7 @@ import org.alfresco.repo.forms.FormException;
import org.apache.commons.logging.Log;
/**
* Abstract base class for all field processors.
*
* @since 3.4
* @author Nick Smith

View File

@@ -22,13 +22,12 @@ package org.alfresco.repo.forms.processor;
import org.alfresco.repo.forms.Field;
/**
* Interface definition for a field processor.
*
* @since 3.4
* @author Nick Smith
*
*/
public interface FieldProcessor
{
Field generateField(String fieldName, FormCreationData data);
}

View File

@@ -25,9 +25,11 @@ import java.util.Map;
import org.alfresco.repo.forms.Field;
/**
* Holds a FieldProcessor implementation for the fields that can be processed by
* the FormProcessor.
*
* @since 3.4
* @author Nick Smith
*
*/
public class FieldProcessorRegistry
{
@@ -58,7 +60,6 @@ public class FieldProcessorRegistry
return processors.get(key);
}
/**
* Attempts to build a {@link Field}. The method first tries to derive a key from the fieldname, then uses this key to look up a {@link FieldProcessor}.
* This {@link FieldProcessor} is then used to generate a {@link Field}.
@@ -76,6 +77,12 @@ public class FieldProcessorRegistry
return fieldProcessor.generateField(fieldName, data);
}
/**
* Returns a FieldProcessor for the given field name.
*
* @param fieldName
* @return The FieldProcessor implementation for the field or null if there isn't one regsitered.
*/
protected FieldProcessor getFieldProcessor(String fieldName)
{
FieldProcessor fieldProcessor = get(getKey(fieldName));
@@ -107,6 +114,7 @@ public class FieldProcessorRegistry
}
/**
* Sets the default field processor instance.
* @param defaultProcessor the defaultProcessor to set
*/
public void setDefaultProcessor(FieldProcessor defaultProcessor)

View File

@@ -192,7 +192,10 @@ public abstract class FilteredFormProcessor<ItemType, PersistType> extends Abstr
private List<String> getIgnoredFields()
{
if (ignoredFields != null)
{
return ignoredFields;
}
return getDefaultIgnoredFields();
}

View File

@@ -28,7 +28,9 @@ package org.alfresco.repo.forms.processor;
import java.util.Map;
/**
* Simple DTO containing various objects needed to generate Forms.
* Interface definition for a simple DTO containing various objects
* needed to generate Forms.
*
* @since 3.4
* @author Nick Smith
*/

View File

@@ -67,7 +67,10 @@ public class FormCreationDataImpl implements FormCreationData
public boolean isForcedField(String fieldName)
{
if (forcedFields == null)
{
return false;
}
return forcedFields.contains(fieldName);
}

View File

@@ -20,6 +20,7 @@
package org.alfresco.repo.forms.processor;
/**
* Interface definition for a helper class that handles persisting form data.
*
* @since 3.4
* @author Nick Smith

View File

@@ -27,7 +27,7 @@ import org.apache.commons.logging.LogFactory;
/**
* Holds a FormProcessor implementation for each of the types of form that
* can be processed. By default a node, task and XML schema form processor
* can be processed. By default a node, type, task, and workflow form processor
* are available.
* <p>
* Given an item the registry selects the relevant form processor, the match

View File

@@ -12,6 +12,7 @@ import org.alfresco.repo.forms.AssociationFieldDefinition;
import org.alfresco.repo.forms.Field;
import org.alfresco.repo.forms.FieldGroup;
import org.alfresco.repo.forms.AssociationFieldDefinition.Direction;
import org.alfresco.repo.forms.processor.FieldProcessor;
import org.alfresco.service.cmr.dictionary.AssociationDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.namespace.NamespaceService;
@@ -20,10 +21,10 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* {@link FieldProcessor} implementation that handles associations.
*
* @since 3.4
* @author Nick Smith
*
*/
public class AssociationFieldProcessor extends QNameFieldProcessor<AssociationDefinition>
{

View File

@@ -29,6 +29,8 @@ import org.alfresco.service.cmr.dictionary.PropertyDefinition;
import org.alfresco.service.namespace.QName;
/**
* DTO for a content model based Field.
*
* @author Nick Smith
* @since 3.4
*/

View File

@@ -22,10 +22,10 @@ package org.alfresco.repo.forms.processor.node;
import org.alfresco.repo.forms.processor.FieldProcessorRegistry;
/**
* FieldProcessorRegistry that exclusively handles content model based field processors.
*
* @since 3.4
* @author Nick Smith
*
*/
public class ContentModelFieldProcessorRegistry extends FieldProcessorRegistry
{

View File

@@ -201,6 +201,19 @@ public abstract class ContentModelFormProcessor<ItemType, PersistType> extends
return new ContentModelItemData<ItemType>(item, propDefs, assocDefs, propValues, assocValues, transientValues);
}
protected List<String> getDefaultIgnoredFields()
{
ArrayList<String> fields = new ArrayList<String>(8);
// ignore system properties by default
fields.add("sys:node-dbid");
fields.add("sys:store-identifier");
fields.add("sys:node-uuid");
fields.add("sys:store-protocol");
return fields;
}
protected Set<QName> getAspectNames(ItemType item)
{
return getBaseType(item).getDefaultAspectNames();
@@ -374,8 +387,7 @@ public abstract class ContentModelFormProcessor<ItemType, PersistType> extends
}
else if (propDef.getDataType().getName().equals(DataTypeDefinition.BOOLEAN))
{
// check for browser representation of true, that being
// "on"
// check for browser representation of true, that being "on"
if (value instanceof String && ON.equals(value))
{
value = Boolean.TRUE;
@@ -419,8 +431,7 @@ public abstract class ContentModelFormProcessor<ItemType, PersistType> extends
}
else if (getLogger().isWarnEnabled())
{
getLogger().warn(
"Ignoring field '" + fieldData.getName() + "' as a property definition can not be found");
getLogger().warn("Ignoring field '" + fieldData.getName() + "' as a property definition can not be found");
}
}
else
@@ -442,8 +453,7 @@ public abstract class ContentModelFormProcessor<ItemType, PersistType> extends
}
else if (fieldName.equals(SizeFieldProcessor.KEY))
{
// the size property is well known but should never be
// persisted
// the size property is well known but should never be persisted
// as it is calculated so this is intentionally ignored
}
else if (getLogger().isWarnEnabled())
@@ -482,20 +492,14 @@ public abstract class ContentModelFormProcessor<ItemType, PersistType> extends
QName fullQName = QName.createQName(qNamePrefix, localName, namespaceService);
// ensure that the association being persisted is defined in the
// model
// ensure that the association being persisted is defined in the model
AssociationDefinition assocDef = assocDefs.get(fullQName);
// TODO: if the association is not defined on the node, check for
// the association
// in all models, however, the source of an association can be
// critical so we
// can't just look up the association in the model regardless. We
// need to
// either check the source class of the node and the assoc def match
// or we
// check that the association was defined as part of an aspect
// (where by it's
// TODO: if the association is not defined on the node, check for the association
// in all models, however, the source of an association can be critical so we
// can't just look up the association in the model regardless. We need to
// either check the source class of the node and the assoc def match or we
// check that the association was defined as part of an aspect (where by it's
// nature can have any source type)
if (assocDef == null)
@@ -511,8 +515,7 @@ public abstract class ContentModelFormProcessor<ItemType, PersistType> extends
String value = (String) fieldData.getValue();
String[] nodeRefs = value.split(",");
// Each element in this array will be a new target node in
// association
// Each element in this array will be a new target node in association
// with the current node.
for (String nextTargetNode : nodeRefs)
{
@@ -529,7 +532,8 @@ public abstract class ContentModelFormProcessor<ItemType, PersistType> extends
}
else
{
assocCommands.add(new AddAssocCommand(nodeRef, new NodeRef(nextTargetNode), fullQName));
assocCommands.add(new AddAssocCommand(nodeRef, new NodeRef(nextTargetNode),
fullQName));
}
}
else if (assocSuffix.equals(ASSOC_DATA_REMOVED_SUFFIX))
@@ -588,8 +592,7 @@ public abstract class ContentModelFormProcessor<ItemType, PersistType> extends
try
{
// if the name property changes the rename method of the file folder
// service should be called rather than updating the property
// directly
// service should be called rather than updating the property directly
this.fileFolderService.rename(nodeRef, (String) fieldData.getValue());
}
catch (FileExistsException fee)
@@ -859,8 +862,7 @@ class RemoveAssocCommand extends AbstractAssocCommand
}
/**
* A class representing a request to add a new child association between two
* nodes.
* A class representing a request to add a new child association between two nodes.
*
* @author Neil McErlean
*/
@@ -889,16 +891,15 @@ class AddChildAssocCommand extends AbstractAssocCommand
return;
}
}
// We are following the behaviour of the JSF client here in using the
// same
// We are following the behaviour of the JSF client here in using the same
// QName value for the 3rd and 4th parameters in the below call.
nodeService.addChild(sourceNodeRef, targetNodeRef, assocQName, assocQName);
}
}
/**
* A class representing a request to remove a child association between two
* nodes.
* A class representing a request to remove a child association between two nodes.
*
* @author Neil McErlean
*/

View File

@@ -34,10 +34,11 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Helper class for building the default fields for a form where an explicit
* set of fields was not provided.
*
* @since 3.4
* @author Nick Smith
*
*/
public class DefaultFieldBuilder
{
@@ -76,10 +77,6 @@ public class DefaultFieldBuilder
this.ItemData = (ContentModelItemData<?>) formData.getItemData();
}
/**
* @param ignoredTransientFields2
* @return
*/
private <T> List<T> getNonNullList(List<T> list)
{
return list == null ? Collections.<T>emptyList() : list;

View File

@@ -21,6 +21,7 @@ package org.alfresco.repo.forms.processor.node;
import org.alfresco.repo.forms.Field;
import org.alfresco.repo.forms.FieldGroup;
import org.alfresco.repo.forms.processor.FieldProcessor;
import org.alfresco.service.cmr.dictionary.ClassAttributeDefinition;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.ParameterCheck;
@@ -29,10 +30,10 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
/**
* Default {@link FieldProcessor} implementation, used when an explicit FieldProcessor can not be located.
*
* @since 3.4
* @author Nick Smith
*
*/
public class DefaultFieldProcessor extends QNameFieldProcessor<ClassAttributeDefinition> implements InitializingBean
{

View File

@@ -23,16 +23,17 @@ import static org.alfresco.repo.forms.processor.node.FormFieldConstants.PROP_DAT
import org.alfresco.repo.forms.FieldDefinition;
import org.alfresco.repo.forms.PropertyFieldDefinition;
import org.alfresco.repo.forms.processor.FieldProcessor;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.extensions.surf.util.I18NUtil;
/**
* {@link FieldProcessor} implementation representing the <code>encoding</code> transient field.
*
* @since 3.4
* @author Nick Smith
*
*/
public class EncodingFieldProcessor extends TransientFieldProcessor
{

View File

@@ -41,17 +41,33 @@ import org.alfresco.service.namespace.NamespaceService;
*/
public class FieldUtils
{
/**
* Generates a property field.
*
* @param propDef The definition of the property to generate
* @param value The value of the field
* @param group The group the field belongs to
* @param namespaceService NamespaceService instance
* @return The generated Field object
*/
public static Field makePropertyField(
PropertyDefinition property,
PropertyDefinition propDef,
Object value,
FieldGroup group,
NamespaceService namespaceService)
{
PropertyFieldProcessor processor = new PropertyFieldProcessor(namespaceService, null);
return processor.makeField(property, value, group);
return processor.makeField(propDef, value, group);
}
/**
* Generates a list of property fields without values.
*
* @param propDefs List of property defintions to create
* @param group The group the field belongs to
* @param namespaceService NamespaceService instance
* @return List of generated Field objects
*/
public static List<Field> makePropertyFields(
Collection<PropertyDefinition> propDefs,
FieldGroup group,
@@ -60,6 +76,14 @@ public class FieldUtils
return makePropertyFields(propDefs, null, group, namespaceService);
}
/**
* Generates a list of property fields with values.
*
* @param propDefAndValue Map of property definitions and corresponding values
* @param group The group the field belongs to
* @param namespaceService NamespaceService instance
* @return List of generated Field objects
*/
public static List<Field> makePropertyFields(
Map<PropertyDefinition, Object> propDefAndValue,
FieldGroup group,
@@ -68,6 +92,15 @@ public class FieldUtils
return makePropertyFields(propDefAndValue.keySet(), propDefAndValue, group, namespaceService);
}
/**
* Generates a list of property fields with values.
*
* @param propDefs List of property definitions to generate
* @param values Map containing the values to use for each property
* @param group The group the field belongs to
* @param namespaceService NamespaceService instance
* @return List of generated Field objects
*/
public static List<Field> makePropertyFields(
Collection<PropertyDefinition> propDefs,
Map<PropertyDefinition, Object> values,
@@ -85,6 +118,15 @@ public class FieldUtils
return fields;
}
/**
* Generates an asssociation field.
*
* @param assocDef The definition of the association to generate
* @param value The value of the field
* @param group The group the field belongs to
* @param namespaceService NamespaceService instance
* @return The generated Field object
*/
public static Field makeAssociationField(
AssociationDefinition assocDef,
Object value,
@@ -95,7 +137,14 @@ public class FieldUtils
return processor.makeField(assocDef, value, group);
}
/**
* Generates a list of association fields without values.
*
* @param assocDefs List of association defintions to create
* @param group The group the field belongs to
* @param namespaceService NamespaceService instance
* @return List of generated Field objects
*/
public static List<Field> makeAssociationFields(
Collection<AssociationDefinition> assocDefs,
FieldGroup group,
@@ -104,6 +153,14 @@ public class FieldUtils
return makeAssociationFields(assocDefs, null, group, namespaceService);
}
/**
* Generates a list of association fields with values.
*
* @param assocDefAndValue Map of association definitions and corresponding values
* @param group The group the field belongs to
* @param namespaceService NamespaceService instance
* @return List of generated Field objects
*/
public static List<Field> makeAssociationFields(
Map<AssociationDefinition, Object> assocDefAndValue,
FieldGroup group,
@@ -112,6 +169,15 @@ public class FieldUtils
return makeAssociationFields(assocDefAndValue.keySet(), assocDefAndValue, group, namespaceService);
}
/**
* Generates a list of association fields with values.
*
* @param assocDefs List of association definitions to generate
* @param values Map containing the values to use for each property
* @param group The group the field belongs to
* @param namespaceService NamespaceService instance
* @return List of generated Field objects
*/
public static List<Field> makeAssociationFields(
Collection<AssociationDefinition> assocDefs,
Map<AssociationDefinition, Object> values,

View File

@@ -23,16 +23,17 @@ import static org.alfresco.repo.forms.processor.node.FormFieldConstants.PROP_DAT
import org.alfresco.repo.forms.FieldDefinition;
import org.alfresco.repo.forms.PropertyFieldDefinition;
import org.alfresco.repo.forms.processor.FieldProcessor;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.extensions.surf.util.I18NUtil;
/**
* {@link FieldProcessor} implementation representing the <code>mimetype</code> transient field.
*
* @since 3.4
* @author Nick Smith
*
*/
public class MimetypeFieldProcessor extends TransientFieldProcessor
{
@@ -44,12 +45,14 @@ public class MimetypeFieldProcessor extends TransientFieldProcessor
private static final String MSG_MIMETYPE_DESC = "form_service.mimetype.description";
@Override
protected Log getLogger() {
protected Log getLogger()
{
return logger;
}
@Override
protected FieldDefinition makeTransientFieldDefinition() {
protected FieldDefinition makeTransientFieldDefinition()
{
String dataKeyName = PROP_DATA_PREFIX + KEY;
PropertyFieldDefinition mimetypeField = new PropertyFieldDefinition(KEY, DataTypeDefinition.TEXT
.getLocalName());

View File

@@ -40,10 +40,10 @@ import org.alfresco.service.cmr.dictionary.PropertyDefinition;
import org.alfresco.service.namespace.QName;
/**
* Mock implementation of the repository ClassDefinition.
*
* @since 3.4
* @author Nick Smith
*
*/
public class MockClassAttributeDefinition implements PropertyDefinition, AssociationDefinition
{
@@ -139,8 +139,7 @@ public class MockClassAttributeDefinition implements PropertyDefinition, Associa
}
/*
* @see
* org.alfresco.service.cmr.dictionary.PropertyDefinition#getConstraints()
* @see org.alfresco.service.cmr.dictionary.PropertyDefinition#getConstraints()
*/
public List<ConstraintDefinition> getConstraints()
{
@@ -148,8 +147,7 @@ public class MockClassAttributeDefinition implements PropertyDefinition, Associa
}
/*
* @see
* org.alfresco.service.cmr.dictionary.PropertyDefinition#getContainerClass
* @see org.alfresco.service.cmr.dictionary.PropertyDefinition#getContainerClass
* ()
*/
public ClassDefinition getContainerClass()
@@ -166,8 +164,7 @@ public class MockClassAttributeDefinition implements PropertyDefinition, Associa
}
/*
* @see
* org.alfresco.service.cmr.dictionary.PropertyDefinition#getDefaultValue()
* @see org.alfresco.service.cmr.dictionary.PropertyDefinition#getDefaultValue()
*/
public String getDefaultValue()
{
@@ -175,8 +172,7 @@ public class MockClassAttributeDefinition implements PropertyDefinition, Associa
}
/*
* @see
* org.alfresco.service.cmr.dictionary.PropertyDefinition#getDescription()
* @see org.alfresco.service.cmr.dictionary.PropertyDefinition#getDescription()
*/
public String getDescription()
{
@@ -184,8 +180,7 @@ public class MockClassAttributeDefinition implements PropertyDefinition, Associa
}
/*
* @seeorg.alfresco.service.cmr.dictionary.PropertyDefinition#
* getIndexTokenisationMode()
* @see org.alfresco.service.cmr.dictionary.PropertyDefinition#getIndexTokenisationMode()
*/
public IndexTokenisationMode getIndexTokenisationMode()
{
@@ -225,9 +220,7 @@ public class MockClassAttributeDefinition implements PropertyDefinition, Associa
}
/*
* @see
* org.alfresco.service.cmr.dictionary.PropertyDefinition#isIndexedAtomically
* ()
* @see org.alfresco.service.cmr.dictionary.PropertyDefinition#isIndexedAtomically()
*/
public boolean isIndexedAtomically()
{
@@ -243,9 +236,7 @@ public class MockClassAttributeDefinition implements PropertyDefinition, Associa
}
/*
* @see
* org.alfresco.service.cmr.dictionary.PropertyDefinition#isMandatoryEnforced
* ()
* @see org.alfresco.service.cmr.dictionary.PropertyDefinition#isMandatoryEnforced()
*/
public boolean isMandatoryEnforced()
{
@@ -253,8 +244,7 @@ public class MockClassAttributeDefinition implements PropertyDefinition, Associa
}
/*
* @see
* org.alfresco.service.cmr.dictionary.PropertyDefinition#isMultiValued()
* @see org.alfresco.service.cmr.dictionary.PropertyDefinition#isMultiValued()
*/
public boolean isMultiValued()
{
@@ -278,8 +268,7 @@ public class MockClassAttributeDefinition implements PropertyDefinition, Associa
}
/*
* @see
* org.alfresco.service.cmr.dictionary.PropertyDefinition#isStoredInIndex()
* @see org.alfresco.service.cmr.dictionary.PropertyDefinition#isStoredInIndex()
*/
public boolean isStoredInIndex()
{
@@ -287,9 +276,7 @@ public class MockClassAttributeDefinition implements PropertyDefinition, Associa
}
/*
* @see
* org.alfresco.service.cmr.dictionary.AssociationDefinition#getSourceClass
* ()
* @see org.alfresco.service.cmr.dictionary.AssociationDefinition#getSourceClass()
*/
public ClassDefinition getSourceClass()
{
@@ -297,9 +284,7 @@ public class MockClassAttributeDefinition implements PropertyDefinition, Associa
}
/*
* @see
* org.alfresco.service.cmr.dictionary.AssociationDefinition#getSourceRoleName
* ()
* @see org.alfresco.service.cmr.dictionary.AssociationDefinition#getSourceRoleName()
*/
public QName getSourceRoleName()
{
@@ -307,9 +292,7 @@ public class MockClassAttributeDefinition implements PropertyDefinition, Associa
}
/*
* @see
* org.alfresco.service.cmr.dictionary.AssociationDefinition#getTargetClass
* ()
* @see org.alfresco.service.cmr.dictionary.AssociationDefinition#getTargetClass()
*/
public ClassDefinition getTargetClass()
{
@@ -317,9 +300,7 @@ public class MockClassAttributeDefinition implements PropertyDefinition, Associa
}
/*
* @see
* org.alfresco.service.cmr.dictionary.AssociationDefinition#getTargetRoleName
* ()
* @see org.alfresco.service.cmr.dictionary.AssociationDefinition#getTargetRoleName()
*/
public QName getTargetRoleName()
{
@@ -335,9 +316,7 @@ public class MockClassAttributeDefinition implements PropertyDefinition, Associa
}
/*
* @see
* org.alfresco.service.cmr.dictionary.AssociationDefinition#isSourceMandatory
* ()
* @see org.alfresco.service.cmr.dictionary.AssociationDefinition#isSourceMandatory()
*/
public boolean isSourceMandatory()
{
@@ -345,8 +324,7 @@ public class MockClassAttributeDefinition implements PropertyDefinition, Associa
}
/*
* @see
* org.alfresco.service.cmr.dictionary.AssociationDefinition#isSourceMany()
* @see org.alfresco.service.cmr.dictionary.AssociationDefinition#isSourceMany()
*/
public boolean isSourceMany()
{
@@ -354,9 +332,7 @@ public class MockClassAttributeDefinition implements PropertyDefinition, Associa
}
/*
* @see
* org.alfresco.service.cmr.dictionary.AssociationDefinition#isTargetMandatory
* ()
* @see org.alfresco.service.cmr.dictionary.AssociationDefinition#isTargetMandatory()
*/
public boolean isTargetMandatory()
{
@@ -364,8 +340,7 @@ public class MockClassAttributeDefinition implements PropertyDefinition, Associa
}
/*
* @seeorg.alfresco.service.cmr.dictionary.AssociationDefinition#
* isTargetMandatoryEnforced()
* @see org.alfresco.service.cmr.dictionary.AssociationDefinition#isTargetMandatoryEnforced()
*/
public boolean isTargetMandatoryEnforced()
{
@@ -373,8 +348,7 @@ public class MockClassAttributeDefinition implements PropertyDefinition, Associa
}
/*
* @see
* org.alfresco.service.cmr.dictionary.AssociationDefinition#isTargetMany()
* @see org.alfresco.service.cmr.dictionary.AssociationDefinition#isTargetMany()
*/
public boolean isTargetMany()
{

View File

@@ -37,10 +37,10 @@ import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.namespace.NamespaceService;
/**
* Mock implementation of a FieldProcessorRegistry.
*
* @since 3.4
* @author Nick Smith
*
*/
public class MockFieldProcessorRegistry extends ContentModelFieldProcessorRegistry
{

View File

@@ -56,9 +56,7 @@ public class NodeFormProcessor extends ContentModelFormProcessor<NodeRef, NodeRe
private static Log logger = LogFactory.getLog(NodeFormProcessor.class);
/*
* @see
* org.alfresco.repo.forms.processor.node.ContentModelFormProcessor#getLogger
* ()
* @see org.alfresco.repo.forms.processor.node.ContentModelFormProcessor#getLogger()
*/
@Override
protected Log getLogger()
@@ -67,9 +65,7 @@ public class NodeFormProcessor extends ContentModelFormProcessor<NodeRef, NodeRe
}
/*
* @see
* org.alfresco.repo.forms.processor.FilteredFormProcessor#getTypedItem(
* org.alfresco.repo.forms.Item)
* @see org.alfresco.repo.forms.processor.FilteredFormProcessor#getTypedItem(org.alfresco.repo.forms.Item)
*/
@Override
protected NodeRef getTypedItem(Item item)
@@ -102,13 +98,16 @@ public class NodeFormProcessor extends ContentModelFormProcessor<NodeRef, NodeRe
}
// check we have a valid node ref
if (nodeRef == null) { throw new FormNotFoundException(item, new IllegalArgumentException(item.getId())); }
if (nodeRef == null)
{
throw new FormNotFoundException(item, new IllegalArgumentException(item.getId()));
}
// check the node itself exists
if (this.nodeService.exists(nodeRef) == false)
{
throw new FormNotFoundException(item, new InvalidNodeRefException("Node does not exist: " + nodeRef,
nodeRef));
throw new FormNotFoundException(item,
new InvalidNodeRefException("Node does not exist: " + nodeRef, nodeRef));
}
else
{
@@ -273,9 +272,7 @@ public class NodeFormProcessor extends ContentModelFormProcessor<NodeRef, NodeRe
}
/*
* @see
* org.alfresco.repo.forms.processor.FilteredFormProcessor#internalPersist
* (java.lang.Object, org.alfresco.repo.forms.FormData)
* @see org.alfresco.repo.forms.processor.FilteredFormProcessor#internalPersist(java.lang.Object, org.alfresco.repo.forms.FormData)
*/
@Override
protected NodeRef internalPersist(NodeRef item, FormData data)
@@ -288,13 +285,4 @@ public class NodeFormProcessor extends ContentModelFormProcessor<NodeRef, NodeRe
return item;
}
/* (non-Javadoc)
* @see org.alfresco.repo.forms.processor.FilteredFormProcessor#getDefaultIgnoredFields()
*/
@Override
protected List<String> getDefaultIgnoredFields()
{
return null;
}
}

View File

@@ -32,6 +32,7 @@ import org.alfresco.repo.forms.Field;
import org.alfresco.repo.forms.FieldGroup;
import org.alfresco.repo.forms.PropertyFieldDefinition;
import org.alfresco.repo.forms.PropertyFieldDefinition.FieldConstraint;
import org.alfresco.repo.forms.processor.FieldProcessor;
import org.alfresco.service.cmr.dictionary.Constraint;
import org.alfresco.service.cmr.dictionary.ConstraintDefinition;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
@@ -46,10 +47,10 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.util.StringUtils;
/**
* {@link FieldProcessor} implementation that handles properties.
*
* @since 3.4
* @author Nick Smith
*
*/
public class PropertyFieldProcessor extends QNameFieldProcessor<PropertyDefinition>
{

View File

@@ -24,6 +24,7 @@ import org.alfresco.repo.forms.Field;
import org.alfresco.repo.forms.FieldDefinition;
import org.alfresco.repo.forms.FieldGroup;
import org.alfresco.repo.forms.processor.AbstractFieldProcessor;
import org.alfresco.repo.forms.processor.FieldProcessor;
import org.alfresco.repo.forms.processor.FormCreationData;
import org.alfresco.service.cmr.dictionary.ClassAttributeDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryService;
@@ -31,6 +32,7 @@ import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
/**
* {@link FieldProcessor} implementation that handles QName fields.
*
* @since 3.4
* @author Nick Smith

View File

@@ -23,22 +23,22 @@ import static org.alfresco.repo.forms.processor.node.FormFieldConstants.PROP_DAT
import org.alfresco.repo.forms.FieldDefinition;
import org.alfresco.repo.forms.PropertyFieldDefinition;
import org.alfresco.repo.forms.processor.FieldProcessor;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.extensions.surf.util.I18NUtil;
/**
* {@link FieldProcessor} implementation representing the <code>size</code> transient field.
*
* @since 3.4
* @author Nick Smith
*
*/
public class SizeFieldProcessor extends TransientFieldProcessor
{
private static final Log logger = LogFactory.getLog(SizeFieldProcessor.class);
public static final String KEY = "size";
private static final String MSG_SIZE_LABEL = "form_service.size.label";

View File

@@ -22,13 +22,14 @@ package org.alfresco.repo.forms.processor.node;
import org.alfresco.repo.forms.Field;
import org.alfresco.repo.forms.FieldDefinition;
import org.alfresco.repo.forms.processor.AbstractFieldProcessor;
import org.alfresco.repo.forms.processor.FieldProcessor;
import org.alfresco.repo.forms.processor.FormCreationData;
/**
* Abstract base class for all transient {@link FieldProcessor}s.
*
* @since 3.4
* @author Nick Smith
*
*/
public abstract class TransientFieldProcessor extends AbstractFieldProcessor<TransientValueGetter>
{

View File

@@ -20,13 +20,12 @@
package org.alfresco.repo.forms.processor.node;
/**
* Interface definition for an object that retrieves a transient filed value.
*
* @since 3.4
* @author Nick Smith
*
*/
public interface TransientValueGetter
{
Object getTransientValue(String name);
}

View File

@@ -19,9 +19,11 @@
package org.alfresco.repo.forms.processor.node;
import static org.alfresco.repo.forms.processor.node.FormFieldConstants.DATA_KEY_SEPARATOR;
import static org.alfresco.repo.forms.processor.node.FormFieldConstants.PROP;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.alfresco.model.ContentModel;
@@ -40,8 +42,6 @@ import org.alfresco.util.GUID;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import static org.alfresco.repo.forms.processor.node.FormFieldConstants.*;
/**
* FormProcessor implementation that can generate and persist Form objects for
* types in the Alfresco content model.
@@ -57,15 +57,12 @@ public class TypeFormProcessor extends ContentModelFormProcessor<TypeDefinition,
private static QName ASPECT_FILE_PLAN_COMPONENT = QName.createQName("http://www.alfresco.org/model/recordsmanagement/1.0", "filePlanComponent");
protected static final String NAME_PROP_DATA = PROP + DATA_KEY_SEPARATOR + "cm"
+ DATA_KEY_SEPARATOR + "name";
protected static final String NAME_PROP_DATA = PROP + DATA_KEY_SEPARATOR + "cm" + DATA_KEY_SEPARATOR + "name";
public static final String DESTINATION = "alf_destination";
/*
* @see
* org.alfresco.repo.forms.processor.node.ContentModelFormProcessor#getLogger
* ()
* @see org.alfresco.repo.forms.processor.node.ContentModelFormProcessor#getLogger()
*/
@Override
protected Log getLogger()
@@ -74,9 +71,7 @@ public class TypeFormProcessor extends ContentModelFormProcessor<TypeDefinition,
}
/*
* @see
* org.alfresco.repo.forms.processor.node.NodeFormProcessor#getTypedItem
* (org.alfresco.repo.forms.Item)
* @see org.alfresco.repo.forms.processor.node.NodeFormProcessor#getTypedItem(org.alfresco.repo.forms.Item)
*/
@Override
protected TypeDefinition getTypedItem(Item item)
@@ -129,9 +124,7 @@ public class TypeFormProcessor extends ContentModelFormProcessor<TypeDefinition,
}
/*
* @see
* org.alfresco.repo.forms.processor.node.NodeFormProcessor#internalPersist
* (java.lang.Object, org.alfresco.repo.forms.FormData)
* @see org.alfresco.repo.forms.processor.node.NodeFormProcessor#internalPersist(java.lang.Object, org.alfresco.repo.forms.FormData)
*/
@Override
protected NodeRef internalPersist(TypeDefinition item, final FormData data)
@@ -233,9 +226,8 @@ public class TypeFormProcessor extends ContentModelFormProcessor<TypeDefinition,
nodeRef = this.nodeService.createNode(
parentRef,
ContentModel.ASSOC_CONTAINS,
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, QName
.createValidLocalName(nodeName)), typeDef.getName(), nodeProps)
.getChildRef();
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI,
QName.createValidLocalName(nodeName)), typeDef.getName(), nodeProps).getChildRef();
}
return nodeRef;
@@ -282,13 +274,4 @@ public class TypeFormProcessor extends ContentModelFormProcessor<TypeDefinition,
{
return null;
}
/* (non-Javadoc)
* @see org.alfresco.repo.forms.processor.FilteredFormProcessor#getDefaultIgnoredFields()
*/
@Override
protected List<String> getDefaultIgnoredFields()
{
return null;
}
}

View File

@@ -36,6 +36,7 @@ import org.alfresco.service.cmr.workflow.WorkflowService;
import org.alfresco.util.ParameterCheck;
/**
* Abstract base class for workflow based form processors.
*
* @since 3.4
* @author Nick Smith
@@ -81,11 +82,8 @@ public abstract class AbstractWorkflowFormProcessor<ItemType, PersistType> exten
this.workflowService = workflowService;
}
/*
* @see
* org.alfresco.repo.forms.processor.node.NodeFormProcessor#getTypedItem
* (org.alfresco.repo.forms.Item)
* @see org.alfresco.repo.forms.processor.node.NodeFormProcessor#getTypedItem(org.alfresco.repo.forms.Item)
*/
@Override
protected ItemType getTypedItem(Item item)
@@ -124,32 +122,40 @@ public abstract class AbstractWorkflowFormProcessor<ItemType, PersistType> exten
@Override
protected List<String> getDefaultIgnoredFields()
{
ArrayList<String> fields = new ArrayList<String>(5);
List<String> fields = super.getDefaultIgnoredFields();
if (fields == null)
{
fields = new ArrayList<String>(20);
}
// ignore document related properties
fields.add("cm:name");
fields.add("cm:owner");
fields.add("cm:creator");
fields.add("cm:modifier");
fields.add("cm:content");
fields.add("cm:accessed");
fields.add("cm:modified");
fields.add("cm:created");
// ignore task properties that shouldn't be directly edited
fields.add("bpm:package");
fields.add("bpm:pooledActors");
fields.add("bpm:completedItems");
fields.add("sys:node-dbid");
fields.add("sys:store-identifier");
fields.add("bpm:completionDate");
fields.add("bpm:context");
fields.add("sys:node-uuid");
fields.add("bpm:hiddenTransitions");
fields.add("bpm:reassignable");
fields.add("cm:creator");
fields.add("cm:modifier");
fields.add("bpm:startDate");
fields.add("bpm:packageActionGroup");
fields.add("bpm:packageItemActionGroup");
fields.add("bpm:outcome");
fields.add("cm:content");
fields.add("cm:accessed");
fields.add("bpm:startDate");
fields.add("cm:modified");
fields.add("cm:created");
fields.add("sys:store-protocol");
fields.add("bpm:taskId");
return fields;
}
/**
* Returns an implementation of {@link ContentModelFormPersister} which is
* used to accumulate all the changes specified in the {@link Form} and then persist them.

View File

@@ -34,6 +34,7 @@ import org.apache.commons.logging.LogFactory;
/**
* Utility class that assists in persisting content model related form data.
*
* @since 3.4
* @author Nick Smith

View File

@@ -22,10 +22,10 @@ package org.alfresco.repo.forms.processor.workflow;
import org.alfresco.service.namespace.QName;
/**
* Data transfer object that represents a data key.
*
* @since 3.4
* @author Nick Smith
*
*/
public class DataKeyInfo
{

View File

@@ -28,10 +28,10 @@ import org.alfresco.service.namespace.QName;
import static org.alfresco.repo.forms.processor.node.FormFieldConstants.*;
/**
* Utility class used for matching data keys.
*
* @since 3.4
* @author Nick Smith
*
*/
public class DataKeyMatcher
{

View File

@@ -20,10 +20,10 @@
package org.alfresco.repo.forms.processor.workflow;
/**
* Enum representing the various types of fields.
*
* @since 3.4
* @author Nick Smith
*
*/
public enum FieldType
{

View File

@@ -22,6 +22,7 @@ package org.alfresco.repo.forms.processor.workflow;
import org.alfresco.repo.forms.FormData.FieldData;
/**
* Interface definition for a helper class that handles persisting form data.
*
* @since 3.4
* @author Nick Smith
@@ -30,8 +31,6 @@ import org.alfresco.repo.forms.FormData.FieldData;
*/
public interface FormPersister<T>
{
void addField(FieldData fieldData);
T persist();

View File

@@ -23,16 +23,17 @@ import static org.alfresco.repo.forms.processor.node.FormFieldConstants.PROP_DAT
import org.alfresco.repo.forms.FieldDefinition;
import org.alfresco.repo.forms.PropertyFieldDefinition;
import org.alfresco.repo.forms.processor.FieldProcessor;
import org.alfresco.repo.forms.processor.node.TransientFieldProcessor;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.extensions.surf.util.I18NUtil;
/**
* {@link FieldProcessor} for handling the transitions of a workflow task.
*
* @since 3.4
* @author Nick Smith
*
*/
public class TransitionFieldProcessor extends TransientFieldProcessor
{

View File

@@ -38,10 +38,10 @@ import org.json.JSONException;
import org.springframework.extensions.surf.util.I18NUtil;
/**
* Utility class that retrieves the appropriately typed value for a property.
*
* @since 3.4
* @author Nick Smith
*
*/
public class TypedPropertyValueGetter
{

View File

@@ -35,10 +35,10 @@ import org.alfresco.service.namespace.QName;
import org.apache.commons.logging.Log;
/**
* Utility class that assists in persisting workflow related form data.
*
* @since 3.4
* @author Nick Smith
*
*/
public class WorkflowFormPersister extends ContentModelFormPersister<WorkflowInstance>
{

View File

@@ -19,7 +19,9 @@
package org.alfresco.repo.forms.processor.workflow;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.alfresco.repo.forms.processor.node.ContentModelItemData;
@@ -58,7 +60,7 @@ public class WorkflowFormProcessor extends AbstractWorkflowFormProcessor<Workflo
@Override
protected TypeDefinition getBaseType(WorkflowDefinition item)
{
//TODO I'm not sure this is safe as getStartTaskDefinition() is 'optional'.
// TODO: I'm not sure this is safe as getStartTaskDefinition() is 'optional'.
WorkflowTaskDefinition startTask = item.getStartTaskDefinition();
return startTask.getMetadata();
}
@@ -153,4 +155,26 @@ public class WorkflowFormProcessor extends AbstractWorkflowFormProcessor<Workflo
ContentModelItemData<WorkflowDefinition> itemData = makeItemData(item);
return new WorkflowFormPersister(itemData, namespaceService, dictionaryService, workflowService, nodeService, logger);
}
/*
* @see org.alfresco.repo.forms.processor.workflow.AbstractWorkflowFormProcessor#getDefaultIgnoredFields()
*/
@Override
protected List<String> getDefaultIgnoredFields()
{
List<String> fields = super.getDefaultIgnoredFields();
if (fields == null)
{
fields = new ArrayList<String>(3);
}
// for the workflow form processor also hide the task specific
// description, due date and priority fields
fields.add("bpm:description");
fields.add("bpm:dueDate");
fields.add("bpm:priority");
return fields;
}
}

View File

@@ -31,7 +31,7 @@ import static org.alfresco.repo.workflow.WorkflowModel.ASSOC_POOLED_ACTORS;
import static org.alfresco.repo.workflow.WorkflowModel.PROP_DESCRIPTION;
import static org.alfresco.repo.workflow.WorkflowModel.PROP_PACKAGE_ACTION_GROUP;
import static org.alfresco.repo.workflow.WorkflowModel.PROP_PACKAGE_ITEM_ACTION_GROUP;
import static org.alfresco.repo.workflow.WorkflowModel.PROP_PRIORITY;
import static org.alfresco.repo.workflow.WorkflowModel.PROP_WORKFLOW_PRIORITY;
import static org.alfresco.repo.workflow.WorkflowModel.PROP_STATUS;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyMap;
@@ -61,6 +61,7 @@ import org.alfresco.repo.forms.FormData.FieldData;
import org.alfresco.repo.forms.processor.node.DefaultFieldProcessor;
import org.alfresco.repo.forms.processor.node.MockClassAttributeDefinition;
import org.alfresco.repo.forms.processor.node.MockFieldProcessorRegistry;
import org.alfresco.repo.workflow.WorkflowModel;
import org.alfresco.service.cmr.dictionary.AssociationDefinition;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryService;
@@ -93,7 +94,7 @@ public class WorkflowFormProcessorTest extends TestCase
{
private static final String TASK_DEF_NAME = "TaskDef";
private static final String WF_DEF_NAME = "foo$wf:bar";
private static final QName PRIORITY_NAME = PROP_PRIORITY;
private static final QName PRIORITY_NAME = PROP_WORKFLOW_PRIORITY;
private static final QName DESC_NAME = PROP_DESCRIPTION;
private static final QName STATUS_NAME = PROP_STATUS;
private static final QName PROP_WITH_ = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "some_prop");
@@ -207,7 +208,6 @@ public class WorkflowFormProcessorTest extends TestCase
Form form = processForm();
List<String> fieldDefs = form.getFieldDefinitionNames();
assertTrue(fieldDefs.contains(ASSIGNEE_NAME.toPrefixString(namespaceService)));
assertTrue(fieldDefs.contains(DESC_NAME.toPrefixString(namespaceService)));
assertTrue(fieldDefs.contains(PRIORITY_NAME.toPrefixString(namespaceService)));
assertTrue(fieldDefs.contains(PackageItemsFieldProcessor.KEY));
@@ -215,12 +215,16 @@ public class WorkflowFormProcessorTest extends TestCase
assertFalse(fieldDefs.contains(ACTORS_NAME.toPrefixString(namespaceService)));
assertFalse(fieldDefs.contains(PROP_PACKAGE_ACTION_GROUP.toPrefixString(namespaceService)));
assertFalse(fieldDefs.contains(PROP_PACKAGE_ITEM_ACTION_GROUP.toPrefixString(namespaceService)));
assertFalse(fieldDefs.contains(WorkflowModel.PROP_DESCRIPTION.toPrefixString(namespaceService)));
assertFalse(fieldDefs.contains(WorkflowModel.PROP_DUE_DATE.toPrefixString(namespaceService)));
assertFalse(fieldDefs.contains(WorkflowModel.PROP_PRIORITY.toPrefixString(namespaceService)));
assertFalse(fieldDefs.contains(WorkflowModel.PROP_TASK_ID.toPrefixString(namespaceService)));
Serializable fieldData = (Serializable) Collections.emptyList();
FormData formData = form.getFormData();
assertEquals(fieldData, formData.getFieldData("assoc_bpm_assignee").getValue());
checkPackageActionGroups(formData);
assertEquals("2", formData.getFieldData("prop_bpm_priority").getValue());
assertEquals("2", formData.getFieldData("prop_bpm_workflowPriority").getValue());
}
public void testGeneratePackageItems() throws Exception

View File

@@ -32,7 +32,7 @@ function testGetFormForContentNode()
var fieldDefs = form.fieldDefinitions;
test.assertNotNull(fieldDefs, "field definitions should not be null.");
test.assertEquals(22, fieldDefs.length);
test.assertEquals(18, fieldDefs.length);
// as we know there are no duplicates we can safely create a map of the
// field definitions for the purposes of this test
@@ -154,7 +154,7 @@ function testGetFormForFolderNode()
var fieldDefs = form.fieldDefinitions;
test.assertNotNull(fieldDefs, "field definitions should not be null.");
test.assertEquals(11, fieldDefs.length);
test.assertEquals(7, fieldDefs.length);
// as we know there are no duplicates we can safely create a map of the
// field definitions for the purposes of this test

View File

@@ -102,7 +102,9 @@ public class WorkflowBuilder
workflowService.endTask(startTask.id, null);
}
else
{
throw new WorkflowException("Start task not found! Expected 1 task but found: " + tasks.size());
}
}
}