diff --git a/config/alfresco/form-services-context.xml b/config/alfresco/form-services-context.xml index ec7677b313..df65cb8130 100644 --- a/config/alfresco/form-services-context.xml +++ b/config/alfresco/form-services-context.xml @@ -69,7 +69,10 @@ + parent="baseFormProcessor" > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/source/java/org/alfresco/repo/forms/Field.java b/source/java/org/alfresco/repo/forms/Field.java new file mode 100644 index 0000000000..8fba2dc124 --- /dev/null +++ b/source/java/org/alfresco/repo/forms/Field.java @@ -0,0 +1,43 @@ +/* + * 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 . + */ + +package org.alfresco.repo.forms; + +/** + * Interface definition of a field + * + * @author Nick Smith + */ +public interface Field +{ + /** + * @return the field definition + */ + FieldDefinition getFieldDefinition(); + + /** + * @return the fieldName + */ + String getFieldName(); + + /** + * @return the value for this field or null. + */ + Object getValue(); +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/forms/Form.java b/source/java/org/alfresco/repo/forms/Form.java index 3636975fb0..f8d3e51b96 100644 --- a/source/java/org/alfresco/repo/forms/Form.java +++ b/source/java/org/alfresco/repo/forms/Form.java @@ -185,6 +185,41 @@ public class Form this.data.addFieldData(fieldName, fieldData); } + /** + * Adds a {@link Field} to the form by adding the {@link FieldDefinition} + * and the value if any. + * + * @param field + */ + public void addField(Field field) + { + if (field == null) + { + return; + } + + FieldDefinition fieldDefinition = field.getFieldDefinition(); + addFieldDefinition(fieldDefinition); + Object value = field.getValue(); + + if (value != null) + { + addData(fieldDefinition.getDataKeyName(), value); + } + } + + /** + * Adds a {@link Collection} of {@link Field Fields} to the form by adding the {@link FieldDefinition FieldDefinitions} + * and the values if any. + */ + public void addFields(Collection fields) + { + for (Field field : fields) + { + addField(field); + } + } + /* * @see java.lang.Object#toString() */ diff --git a/source/java/org/alfresco/repo/forms/FormServiceImplTest.java b/source/java/org/alfresco/repo/forms/FormServiceImplTest.java index 9a11ea101e..8c38471fa0 100644 --- a/source/java/org/alfresco/repo/forms/FormServiceImplTest.java +++ b/source/java/org/alfresco/repo/forms/FormServiceImplTest.java @@ -30,10 +30,10 @@ import java.util.Map; import org.alfresco.model.ContentModel; import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.forms.AssociationFieldDefinition.Direction; +import org.alfresco.repo.forms.FormData.FieldData; import org.alfresco.repo.forms.PropertyFieldDefinition.FieldConstraint; import org.alfresco.repo.forms.processor.node.TypeFormProcessor; import org.alfresco.repo.jscript.ClasspathScriptLocation; -import org.alfresco.repo.security.authentication.AuthenticationComponent; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.ContentReader; @@ -130,9 +130,6 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest this.contentService = (ContentService)this.applicationContext.getBean("ContentService"); this.workflowService = (WorkflowService)this.applicationContext.getBean("WorkflowService"); - AuthenticationComponent authenticationComponent = (AuthenticationComponent) this.applicationContext - .getBean("authenticationComponent"); - // Do the tests as userOne createUser(USER_ONE); authenticationComponent.setCurrentUser(USER_ONE); @@ -1091,6 +1088,7 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest QName underscoreProperty = QName.createQName(fdkUri, "with_underscore"); QName dashProperty = QName.createQName(fdkUri, "with-dash"); QName duplicateProperty = QName.createQName(fdkUri, "duplicate"); + QName periodProperty = QName.createQName(fdkUri, "period"); String guid = GUID.generate(); String name = "everything" + guid + ".txt"; @@ -1098,6 +1096,7 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest String underscoreValue = "Property with an underscore in the name."; String dashValue = "Property with a dash in the name."; String duplicateValue = "Property with the same name as an association."; + String periodValue = "day|1"; Map docProps = new HashMap(4); docProps.put(ContentModel.PROP_NAME, name); @@ -1105,6 +1104,7 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest docProps.put(underscoreProperty, underscoreValue); docProps.put(dashProperty, dashValue); docProps.put(duplicateProperty, duplicateValue); + docProps.put(periodProperty, periodValue); NodeRef everythingNode = this.nodeService.createNode(this.folder, ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, name), everythingType, docProps).getChildRef(); @@ -1117,6 +1117,7 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest fields.add("fdk:with-dash"); fields.add("prop:fdk:duplicate"); fields.add("assoc:fdk:duplicate"); + fields.add("fdk:period"); Form form = this.formService.getForm(new Item(NODE_FORM_ITEM_KIND, everythingNode.toString()), fields); @@ -1136,6 +1137,7 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest PropertyFieldDefinition textField = null; PropertyFieldDefinition underscoreField = null; PropertyFieldDefinition dashField = null; + PropertyFieldDefinition periodField = null; PropertyFieldDefinition duplicatePropField = null; AssociationFieldDefinition duplicateAssocField = null; @@ -1168,12 +1170,17 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest duplicateAssocField = (AssociationFieldDefinition)field; } } + else if (field.getName().equals("fdk:period")) + { + periodField = (PropertyFieldDefinition)field; + } } assertNotNull("Expected to find nameField", nameField); assertNotNull("Expected to find textField", textField); assertNotNull("Expected to find underscoreField", underscoreField); assertNotNull("Expected to find dashField", dashField); + assertNotNull("Expected to find periodField", periodField); assertNotNull("Expected to find duplicatePropField", duplicatePropField); assertNotNull("Expected to find duplicateAssocField", duplicateAssocField); @@ -1183,11 +1190,18 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest assertEquals(textValue, values.getFieldData(textField.getDataKeyName()).getValue()); assertEquals(underscoreValue, values.getFieldData(underscoreField.getDataKeyName()).getValue()); assertEquals(dashValue, values.getFieldData(dashField.getDataKeyName()).getValue()); + assertEquals(periodValue, values.getFieldData(periodField.getDataKeyName()).getValue().toString()); assertEquals(duplicateValue, values.getFieldData(duplicatePropField.getDataKeyName()).getValue()); - List assocs = (List)values.getFieldData(duplicateAssocField.getDataKeyName()).getValue(); + FieldData fieldData = values.getFieldData(duplicateAssocField.getDataKeyName()); + assertNotNull(fieldData); + List assocs = (List)fieldData.getValue(); assertNotNull(assocs); assertEquals(0, assocs.size()); + // check the period property data type parameters were returned + DataTypeParameters dtp = periodField.getDataTypeParameters(); + assertNotNull("Expected to find data type parameters for the fdk:period field", dtp); + // update the properties via FormService FormData data = new FormData(); diff --git a/source/java/org/alfresco/repo/forms/processor/AbstractFieldProcessor.java b/source/java/org/alfresco/repo/forms/processor/AbstractFieldProcessor.java new file mode 100644 index 0000000000..34c57a9a40 --- /dev/null +++ b/source/java/org/alfresco/repo/forms/processor/AbstractFieldProcessor.java @@ -0,0 +1,101 @@ +/* + * 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 . + */ + +package org.alfresco.repo.forms.processor; + +import org.alfresco.repo.forms.Field; +import org.alfresco.repo.forms.FormException; +import org.apache.commons.logging.Log; + +import com.sun.star.lang.IllegalArgumentException; + +public abstract class AbstractFieldProcessor implements FieldProcessor +{ + /* (non-Javadoc) + * @see org.alfresco.repo.forms.field.processor.FieldProcessor#generateField(java.lang.String, java.lang.Object) + */ + public Field generateField(String fieldName, FormCreationData data) + { + Data typedData = checkDataType(data.getItemData()); + Field field = generateTypedField(fieldName, data, typedData); + logIfFieldNotFound(field, fieldName); + return field; + } + + @SuppressWarnings("unchecked") + private Data checkDataType(Object itemData) + { + Class expectedType = getExpectedDataType(); + if (expectedType.isAssignableFrom(itemData.getClass())) + { + return (Data)itemData; + } + else + { + String msg = "Data object: "+itemData+" is not of expected type: "+expectedType; + throw new FormException(msg, new IllegalArgumentException()); + } + } + + 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. + * + * @param registry The registry to which this {@link FieldProcessor} is added. + */ + public void setFieldProcessorRegistry(FieldProcessorRegistry registry) + { + registry.register(getRegistryKey(), this); + } + + /** + * @return a {@link Log} used to log events that occur while processing fields. + */ + protected abstract Log getLogger(); + + /** + * @return a {@link String} key used to identify this {@link FieldProcessor} in a {@link FieldProcessorRegistry}. + */ + protected abstract String getRegistryKey(); + + /** + * @return the expected {@link Class} of the data parameter for the method generateField. + */ + protected abstract Class getExpectedDataType(); + + /** + * The method which actually creates the {@link Field}. + * @param fieldName the name of the {@link Field} to be genereated. + * @param formData TODO + * @param typedData the data used to create the field. + * @return a {@link Field} or null. + */ + protected abstract Field generateTypedField(String fieldName, FormCreationData formData, Data typedData); +} diff --git a/source/java/org/alfresco/repo/forms/processor/FieldProcessor.java b/source/java/org/alfresco/repo/forms/processor/FieldProcessor.java new file mode 100644 index 0000000000..e24af2448f --- /dev/null +++ b/source/java/org/alfresco/repo/forms/processor/FieldProcessor.java @@ -0,0 +1,27 @@ +/* + * 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 . + */ + +package org.alfresco.repo.forms.processor; + +import org.alfresco.repo.forms.Field; + +public interface FieldProcessor +{ + Field generateField(String fieldName, FormCreationData data); +} diff --git a/source/java/org/alfresco/repo/forms/processor/FieldProcessorRegistry.java b/source/java/org/alfresco/repo/forms/processor/FieldProcessorRegistry.java new file mode 100644 index 0000000000..c5116c073b --- /dev/null +++ b/source/java/org/alfresco/repo/forms/processor/FieldProcessorRegistry.java @@ -0,0 +1,111 @@ +/* + * 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 . + */ + +package org.alfresco.repo.forms.processor; + +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.repo.forms.Field; + +public class FieldProcessorRegistry +{ + private final Map processors = new HashMap(); + + /** + * The {@link FieldProcessor} to be used if no specific processor can be found. + */ + private FieldProcessor defaultProcessor; + + /** + * Registers a {@link FieldProcessor} with this registry using the specified key. + * @param key + * @param processor + */ + public void register(String key, FieldProcessor processor) + { + processors.put(key, processor); + } + + /** + * Returns the {@link FieldProcessor} that was registered witht he specified key. + * @param key + * @return + */ + public FieldProcessor get(String key) + { + 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}. + * @param fieldName the name of the field to be created. + * @param data A data object used by the {@link FieldProcessor} to build the {@link Field}. + * @return a {@link Field} or null. + */ + public Field buildField(String fieldName, FormCreationData data) + { + FieldProcessor fieldProcessor = getFieldProcessor(fieldName); + if (fieldProcessor == null) + { + return null; + } + return fieldProcessor.generateField(fieldName, data); + } + + protected FieldProcessor getFieldProcessor(String fieldName) + { + FieldProcessor fieldProcessor = get(getKey(fieldName)); + if (fieldProcessor == null && useDefaultProcessor(fieldName)) + { + fieldProcessor = defaultProcessor; + } + return fieldProcessor; + } + + /** + * Determines if the defaultProcessor should be used. + * @param fieldName + * @return true if the defaultProcessor should be used, otherwise false. + */ + protected boolean useDefaultProcessor(String fieldName) + { + return true; + } + + /** + * Derives the key used to look up the {@link FieldProcessor} from the fieldName. + * @param fieldName + * @return the key used to look up the {@link FieldProcessor}. + */ + protected String getKey(String fieldName) + { + return fieldName; + } + + /** + * @param defaultProcessor the defaultProcessor to set + */ + public void setDefaultProcessor(FieldProcessor defaultProcessor) + { + this.defaultProcessor = defaultProcessor; + } +} diff --git a/source/java/org/alfresco/repo/forms/processor/FilteredFormProcessor.java b/source/java/org/alfresco/repo/forms/processor/FilteredFormProcessor.java index b8dec235fc..6a05f29ebb 100644 --- a/source/java/org/alfresco/repo/forms/processor/FilteredFormProcessor.java +++ b/source/java/org/alfresco/repo/forms/processor/FilteredFormProcessor.java @@ -19,9 +19,12 @@ package org.alfresco.repo.forms.processor; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; +import org.alfresco.repo.forms.Field; import org.alfresco.repo.forms.Form; import org.alfresco.repo.forms.FormData; import org.alfresco.repo.forms.Item; @@ -40,6 +43,8 @@ public abstract class FilteredFormProcessor extends Abstr protected FilterRegistry filterRegistry; + protected FieldProcessorRegistry fieldProcessorRegistry; + /** * Sets the filter registry * @@ -129,6 +134,79 @@ public abstract class FilteredFormProcessor extends Abstr return persistedObject; } + /** + * Generates the form. + * + * @param item The object to generate a form for + * @param fields Restricted list of fields to include + * @param forcedFields List of fields to forcibly include + * @param form The form object being generated + * @param context Map representing optional context that can be used during + * retrieval of the form + */ + protected void internalGenerate(ItemType item, List fields, List forcedFields, Form form, Map context) + { + Log log = getLogger(); + if (log.isDebugEnabled()) log.debug("Generating form for: " + item); + + // generate the form type and URI for the item. + Item formItem = form.getItem(); + formItem.setType(getItemType(item)); + formItem.setUrl(getItemURI(item)); + + Object itemData = makeItemData(item); + FormCreationData data = new FormCreationData(itemData, forcedFields, context); + List fieldsToAdd; + if (fields != null && fields.size() > 0) + { + fieldsToAdd = generateSelectedFields(fields, data); + } + else + { + fieldsToAdd = generateDefaultFields(data); + } + form.addFields(fieldsToAdd); + if (log.isDebugEnabled()) // + log.debug("Generated form: " + form); + } + + /** + * Generates a list of default fields to add if no field names are specified. + * @param data Used for field creation. + * @return a {@link List} of {@link Field Fields} which may be empty. + */ + protected List generateDefaultFields(FormCreationData data) + { + return Collections.emptyList(); + } + + protected List generateSelectedFields(List fields, FormCreationData data) + { + List fieldData = new ArrayList(fields.size()); + for (String fieldName : fields) + { + Field field = fieldProcessorRegistry.buildField(fieldName, data); + if (field == null) + { + if (getLogger().isWarnEnabled()) { + String msg = "Ignoring unrecognised field \"" + fieldName + "\""; + getLogger().warn(msg); + } + } + else + { + fieldData.add(field); + } + } + return fieldData; + } + + /** + * Creates a data object used by the {@link FormProcessor} and {@link FieldProcessor FieldProcessors} to create {@link Field Fields} + * @return + */ + protected abstract Object makeItemData(ItemType item); + /** * Returns a typed Object representing the given item. *

@@ -142,18 +220,37 @@ public abstract class FilteredFormProcessor extends Abstr protected abstract ItemType getTypedItem(Item item); /** - * Generates the form. + * Retrieves a logger instance to log to. * - * @param item The object to generate a form for - * @param fields Restricted list of fields to include - * @param forcedFields List of fields to forcibly include - * @param form The form object being generated - * @param context Map representing optional context that can be used during - * retrieval of the form + * @return Log instance to log to. */ - protected abstract void internalGenerate(ItemType item, List fields, List forcedFields, Form form, - Map context); + protected abstract Log getLogger(); + /** + * Returns a {@link String} describing the type fo the specified item. + * @param item + * @return + */ + protected abstract String getItemType(ItemType item); + + /** + * Returns the URI location of the specified item. + * @param item + * @return + */ + protected abstract String getItemURI(ItemType item); + + /** + * Sets the field processor registry. + * + * @param fieldProcessorRegistry + * The {@link FieldProcessorRegistry} to use. + */ + public void setFieldProcessorRegistry(FieldProcessorRegistry fieldProcessorRegistry) + { + this.fieldProcessorRegistry = fieldProcessorRegistry; + } + /** * Persists the form data. * diff --git a/source/java/org/alfresco/repo/forms/processor/FormCreationData.java b/source/java/org/alfresco/repo/forms/processor/FormCreationData.java new file mode 100644 index 0000000000..79f884c11e --- /dev/null +++ b/source/java/org/alfresco/repo/forms/processor/FormCreationData.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2005-2009 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ + +package org.alfresco.repo.forms.processor; + +import java.util.List; +import java.util.Map; + +/** + * Simple DTO containing various objects needed to generate Forms. + * @author Nick Smith + */ +public class FormCreationData +{ + private final Object itemData; + private final List forcedFields; + private final Map context; + + public FormCreationData(Object itemData, + List forcedFields, + Map context) + { + this.itemData = itemData; + this.forcedFields = forcedFields; + this.context = context; + } + + + /** + * @return the itemData + */ + public Object getItemData() + { + return itemData; + } + + /** + * @return If the fieldName given is a forced field then + * returns true, otherwise returns false. + */ + public boolean isForcedField(String fieldName) + { + if (forcedFields == null) + return false; + return forcedFields.contains(fieldName); + } + + /** + * @return the context + */ + public Map getContext() + { + return context; + } +} diff --git a/source/java/org/alfresco/repo/forms/processor/node/AssociationFieldProcessor.java b/source/java/org/alfresco/repo/forms/processor/node/AssociationFieldProcessor.java new file mode 100644 index 0000000000..4daa908111 --- /dev/null +++ b/source/java/org/alfresco/repo/forms/processor/node/AssociationFieldProcessor.java @@ -0,0 +1,125 @@ +package org.alfresco.repo.forms.processor.node; + +import static org.alfresco.repo.forms.processor.node.FormFieldConstants.ASSOC_DATA_PREFIX; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +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.service.cmr.dictionary.AssociationDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +public class AssociationFieldProcessor extends QNameFieldProcessor +{ + private static final Log logger = LogFactory.getLog(AssociationFieldProcessor.class); + + public AssociationFieldProcessor() + { + // Constructor for Spring. + } + + public AssociationFieldProcessor(NamespaceService namespaceService, DictionaryService dictionaryService) + { + super(namespaceService, dictionaryService); + } + + @Override + protected Log getLogger() + { + return logger; + } + + @Override + protected FieldGroup getGroup(AssociationDefinition assocDef) + { + // TODO Need to Implement this once Composite Content is implementd. + return null; + } + + @Override + public Field makeField(AssociationDefinition assocDef, Object value, FieldGroup group) + { + AssociationFieldDefinition fieldDef = makeAssociationFieldDefinition(assocDef, group); + return new ContentField(assocDef, fieldDef, value); + } + + /** + * Gets the associated value from the {@link ItemData}. + * If the value is null the method returns an empty {@link List}. + * If the value is a single Object (assumed to be a NodeRef) it returns a {@link List} containing a {@link String} representation of that object. + * If the value is a {@link Collection} of Objects, returns a {@link List} containing {@link String} representations of all the objects. + * @return An {@link ArrayList} of Strings or null. + */ + @Override + protected Object getValue(QName name, ItemData data) + { + Serializable values = data.getAssociationValue(name); + if (values == null) + { + return Collections.EMPTY_LIST; + } + if (values instanceof Collection) + { + return getValues((Collection) values); + } + return Collections.singletonList(values); + } + + private List getValues(Collection collection) + { + List results = new ArrayList(collection.size()); + for (Object value : collection) + { + results.add(value.toString()); + } + return results; + } + + public AssociationFieldDefinition makeAssociationFieldDefinition(final AssociationDefinition assocDef, FieldGroup group) + { + String name = getPrefixedName(assocDef); + String endpointType = assocDef.getTargetClass().getName().toPrefixString(namespaceService); + AssociationFieldDefinition fieldDef = new AssociationFieldDefinition(name, + endpointType, + Direction.TARGET); + + populateFieldDefinition(assocDef, fieldDef, group, ASSOC_DATA_PREFIX); + + fieldDef.setEndpointMandatory(assocDef.isTargetMandatory()); + fieldDef.setEndpointMany(assocDef.isTargetMany()); + return fieldDef; + } + + @Override + protected String getRegistryKey() + { + return FormFieldConstants.ASSOC; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.forms.processor.node.QNameFieldProcessor#getTypeDefinition(org.alfresco.service.namespace.QName, org.alfresco.repo.forms.processor.node.ItemData, boolean) + */ + @Override + protected AssociationDefinition getTypeDefinition(QName fullName, ItemData itemData, boolean isForcedField) + { + AssociationDefinition assocDefinition = itemData.getAssociationDefinition(fullName); + if (assocDefinition == null) + { + if (isForcedField) + { + assocDefinition = dictionaryService.getAssociation(fullName); + } + } + return assocDefinition; + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/forms/processor/node/ContentField.java b/source/java/org/alfresco/repo/forms/processor/node/ContentField.java new file mode 100644 index 0000000000..e3abeccbd0 --- /dev/null +++ b/source/java/org/alfresco/repo/forms/processor/node/ContentField.java @@ -0,0 +1,124 @@ +/* + * 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 . + */ + +package org.alfresco.repo.forms.processor.node; + +import org.alfresco.repo.forms.AssociationFieldDefinition; +import org.alfresco.repo.forms.Field; +import org.alfresco.repo.forms.FieldDefinition; +import org.alfresco.repo.forms.PropertyFieldDefinition; +import org.alfresco.service.cmr.dictionary.AssociationDefinition; +import org.alfresco.service.cmr.dictionary.ClassAttributeDefinition; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.namespace.QName; + +/** + * @author Nick Smith + */ +public class ContentField implements Field +{ + private final FieldDefinition fieldDefinition; + private final ClassAttributeDefinition classDefinition; + private final boolean isProperty; + private final Object value; + + public ContentField(PropertyDefinition propertyDefinition, + PropertyFieldDefinition fieldDef, Object value) + { + this.isProperty = true; + this.classDefinition = propertyDefinition; + this.fieldDefinition = fieldDef; + this.value = value; + } + + public ContentField(AssociationDefinition assocDefinition, + AssociationFieldDefinition fieldDef, Object value) + { + this.isProperty = false; + this.classDefinition = assocDefinition; + this.fieldDefinition = fieldDef; + this.value = value; + } + + /** + * This constructor should only be used to create FieldInfo for transient properties such as encoding, mimetype or size. + * @param fieldDef The PropertyFieldDefinition for the transient property. + */ + public ContentField(PropertyFieldDefinition fieldDef, Object value) + { + this.classDefinition = null; + this.isProperty = true; + this.fieldDefinition = fieldDef; + this.value = value; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.forms.processor.task.Field#isTransient() + */ + public boolean isTransient() + { + return classDefinition == null; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.forms.processor.task.Field#getFieldDefinition() + */ + public FieldDefinition getFieldDefinition() + { + return this.fieldDefinition; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.forms.processor.task.Field#getFullName() + */ + public QName getFullName() + { + if (classDefinition == null) + { + return null; + } + + return classDefinition.getName(); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.forms.processor.task.Field#getFieldName() + */ + public String getFieldName() + { + return fieldDefinition.getName(); + } + + /* + * @see java.lang.Object#toString() + */ + @Override + public String toString() + { + String type = isTransient()?"Transient ":""; + type += isProperty ? "Property" : "Association"; + return "Field: " + getFieldName() + " Type: " + type; + } + + public Object getValue() + { + return value; + } + +} diff --git a/source/java/org/alfresco/repo/forms/processor/node/ContentModelFieldProcessorRegistry.java b/source/java/org/alfresco/repo/forms/processor/node/ContentModelFieldProcessorRegistry.java new file mode 100644 index 0000000000..4e870f4fd8 --- /dev/null +++ b/source/java/org/alfresco/repo/forms/processor/node/ContentModelFieldProcessorRegistry.java @@ -0,0 +1,58 @@ +/* + * 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 . + */ + +package org.alfresco.repo.forms.processor.node; + +import org.alfresco.repo.forms.processor.FieldProcessorRegistry; + +/** + * @author Nick Smith + * + */ +public class ContentModelFieldProcessorRegistry extends FieldProcessorRegistry +{ + /* (non-Javadoc) + * @see org.alfresco.repo.forms.processor.FieldProcessorRegistry#getKey(java.lang.String) + */ + @Override + protected String getKey(String fieldName) + { + String[] parts = fieldName.split(FormFieldConstants.FIELD_NAME_SEPARATOR); + if (parts.length > 0) + { + return parts[0]; + } + else + { + return null; + } + } + + /* (non-Javadoc) + * @see org.alfresco.repo.forms.processor.FieldProcessorRegistry#useDefaultProcessor(java.lang.String) + */ + @Override + protected boolean useDefaultProcessor(String fieldName) + { + // Only use default if the fieldName follows the format + // prefix:localname + String[] parts = fieldName.split(FormFieldConstants.FIELD_NAME_SEPARATOR); + return parts.length == 2; + } +} diff --git a/source/java/org/alfresco/repo/forms/processor/node/ContentModelFormProcessor.java b/source/java/org/alfresco/repo/forms/processor/node/ContentModelFormProcessor.java index f4530e4db1..ddb356e371 100644 --- a/source/java/org/alfresco/repo/forms/processor/node/ContentModelFormProcessor.java +++ b/source/java/org/alfresco/repo/forms/processor/node/ContentModelFormProcessor.java @@ -30,21 +30,18 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import org.alfresco.model.ContentModel; -import org.alfresco.repo.forms.AssociationFieldDefinition; +import org.alfresco.repo.forms.Field; import org.alfresco.repo.forms.FieldDefinition; -import org.alfresco.repo.forms.FieldGroup; import org.alfresco.repo.forms.Form; import org.alfresco.repo.forms.FormData; import org.alfresco.repo.forms.FormException; import org.alfresco.repo.forms.PropertyFieldDefinition; -import org.alfresco.repo.forms.AssociationFieldDefinition.Direction; import org.alfresco.repo.forms.FormData.FieldData; -import org.alfresco.repo.forms.PropertyFieldDefinition.FieldConstraint; import org.alfresco.repo.forms.processor.FilteredFormProcessor; +import org.alfresco.repo.forms.processor.FormCreationData; import org.alfresco.service.cmr.dictionary.AspectDefinition; import org.alfresco.service.cmr.dictionary.AssociationDefinition; import org.alfresco.service.cmr.dictionary.ChildAssociationDefinition; -import org.alfresco.service.cmr.dictionary.Constraint; import org.alfresco.service.cmr.dictionary.ConstraintDefinition; import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.dictionary.DictionaryService; @@ -60,7 +57,6 @@ import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.repository.Period; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.RegexQNamePattern; @@ -69,7 +65,6 @@ import org.apache.commons.logging.LogFactory; import org.json.JSONArray; import org.json.JSONException; import org.springframework.extensions.surf.util.I18NUtil; -import org.springframework.util.StringUtils; /** * Abstract FormProcessor implementation that provides common functionality for @@ -201,563 +196,53 @@ public abstract class ContentModelFormProcessor extends this.contentService = contentService; } - /** - * Sets up a field definition for the given property. - *

- * NOTE: This method is static so that it can serve as a helper method for - * FormFilter implementations as adding additional property fields is likely - * to be a common extension. - *

- * - * @param propDef The PropertyDefinition of the field to generate - * @param form The Form instance to populate - * @param namespaceService NamespaceService instance - */ - public static void generatePropertyField(PropertyDefinition propDef, Form form, NamespaceService namespaceService) + @Override + protected List generateDefaultFields(FormCreationData data) { - generatePropertyField(propDef, form, null, null, namespaceService); + DefaultFieldBuilder defaultFieldBuilder = new DefaultFieldBuilder(data, fieldProcessorRegistry); + return defaultFieldBuilder.buildDefaultFields(); } - /** - * Sets up a field definition for the given property. - *

- * NOTE: This method is static so that it can serve as a helper method for - * FormFilter implementations as adding additional property fields is likely - * to be a common extension. - *

- * - * @param propDef The PropertyDefinition of the field to generate - * @param form The Form instance to populate - * @param propValue The value of the property field - * @param namespaceService NamespaceService instance - */ - public static void generatePropertyField(PropertyDefinition propDef, Form form, Serializable propValue, - NamespaceService namespaceService) + protected Map populateAssociations(TypeDefinition typeDef) { - generatePropertyField(propDef, form, propValue, null, namespaceService); + // we only get the associations of the actual type so + // we also need to manually get associations from any + // mandatory aspects + HashMap allAssocs = new HashMap(); + allAssocs.putAll(typeDef.getAssociations()); + List aspects = typeDef.getDefaultAspects(true); + for (AspectDefinition aspect : aspects) { + allAssocs.putAll(aspect.getAssociations()); + } + return Collections.unmodifiableMap(allAssocs); } - /** - * Sets up a field definition for the given property. - *

- * NOTE: This method is static so that it can serve as a helper method for - * FormFilter implementations as adding additional property fields is likely - * to be a common extension. - *

- * - * @param propDef The PropertyDefinition of the field to generate - * @param form The Form instance to populate - * @param propValue The value of the property field - * @param group The FieldGroup the property field belongs to, can be null - * @param namespaceService NamespaceService instance - */ - @SuppressWarnings("unchecked") - public static void generatePropertyField(PropertyDefinition propDef, Form form, Serializable propValue, - FieldGroup group, NamespaceService namespaceService) + @Override + protected ItemData makeItemData(ItemType item) { - String propName = propDef.getName().toPrefixString(namespaceService); - String[] nameParts = QName.splitPrefixedQName(propName); - PropertyFieldDefinition fieldDef = new PropertyFieldDefinition(propName, propDef.getDataType().getName() - .getLocalName()); - - String title = propDef.getTitle(); - if (title == null) - { - title = propName; - } - fieldDef.setLabel(title); - fieldDef.setDefaultValue(propDef.getDefaultValue()); - fieldDef.setDescription(propDef.getDescription()); - fieldDef.setMandatory(propDef.isMandatory()); - fieldDef.setProtectedField(propDef.isProtected()); - fieldDef.setRepeating(propDef.isMultiValued()); - fieldDef.setGroup(group); - - // any property from the system model (sys prefix) should be protected - // the model doesn't currently enforce this so make sure they are not - // editable - if (NamespaceService.SYSTEM_MODEL_1_0_URI.equals(propDef.getName().getNamespaceURI())) - { - fieldDef.setProtectedField(true); - } - - // define the data key name and set - String dataKeyName = PROP_DATA_PREFIX + nameParts[0] + DATA_KEY_SEPARATOR + nameParts[1]; - fieldDef.setDataKeyName(dataKeyName); - - // setup any parameters requried for the data type - if (propDef.getDataType().getName().equals(DataTypeDefinition.PERIOD)) - { - // if the property data type is d:period we need to setup a data - // type parameters object to represent the options and rules - PeriodDataTypeParameters periodOptions = new PeriodDataTypeParameters(); - Set providers = Period.getProviderNames(); - for (String provider : providers) - { - periodOptions.addPeriodProvider(Period.getProvider(provider)); - } - - fieldDef.setDataTypeParameters(periodOptions); - } - - // setup constraints for the property - List constraints = propDef.getConstraints(); - if (constraints != null && constraints.size() > 0) - { - List fieldConstraints = new ArrayList(constraints.size()); - - for (ConstraintDefinition constraintDef : constraints) - { - Constraint constraint = constraintDef.getConstraint(); - FieldConstraint fieldConstraint = new FieldConstraint(constraint.getType(), constraint.getParameters()); - fieldConstraints.add(fieldConstraint); - } - - fieldDef.setConstraints(fieldConstraints); - } - - form.addFieldDefinition(fieldDef); - - // add the property value to the form - if (propValue != null) - { - if (propValue instanceof List) - { - // temporarily add repeating field data as a comma - // separated list, this will be changed to using - // a separate field for each value once we have full - // UI support in place. - propValue = StringUtils.collectionToCommaDelimitedString((List) propValue); - } - else if (propValue instanceof ContentData) - { - // for content properties retrieve the info URL rather than the - // the object value itself - propValue = ((ContentData)propValue).getInfoUrl(); - } - - form.addData(dataKeyName, propValue); - } + TypeDefinition baseType = getBaseType(item); + Set aspects = getAspectNames(item); + TypeDefinition anonType = dictionaryService.getAnonymousType(baseType.getName(), aspects); + Map propDefs = anonType.getProperties(); + Map assocDefs = anonType.getAssociations(); + Map propValues = getPropertyValues(item); + Map assocValues = getAssociationValues(item); + Map transientValues = getTransientValues(item); + return new ItemData(item, propDefs, assocDefs, propValues, assocValues, transientValues); } - /** - * Sets up a field definition for the given association. - *

- * NOTE: This method is static so that it can serve as a helper method for - * FormFilter implementations as adding additional association fields is - * likely to be a common extension. - *

- * - * @param assocDef The AssociationDefinition of the field to generate - * @param form The Form instance to populate - * @param namespaceService NamespaceService instance - */ - public static void generateAssociationField(AssociationDefinition assocDef, Form form, - NamespaceService namespaceService) + protected Set getAspectNames(ItemType item) { - generateAssociationField(assocDef, form, null, null, namespaceService); + return getBaseType(item).getDefaultAspectNames(); } - /** - * Sets up a field definition for the given association. - *

- * NOTE: This method is static so that it can serve as a helper method for - * FormFilter implementations as adding additional association fields is - * likely to be a common extension. - *

- * - * @param assocDef The AssociationDefinition of the field to generate - * @param form The Form instance to populate - * @param assocValues The values of the association field, can be null - * @param namespaceService NamespaceService instance - */ - @SuppressWarnings("unchecked") - public static void generateAssociationField(AssociationDefinition assocDef, Form form, List assocValues, - NamespaceService namespaceService) - { - generateAssociationField(assocDef, form, assocValues, null, namespaceService); - } + protected abstract Map getAssociationValues(ItemType item); - /** - * Sets up a field definition for the given association. - *

- * NOTE: This method is static so that it can serve as a helper method for - * FormFilter implementations as adding additional association fields is - * likely to be a common extension. - *

- * - * @param assocDef The AssociationDefinition of the field to generate - * @param form The Form instance to populate - * @param assocValues The values of the association field, can be null - * @param group The FieldGroup the association field belongs to, can be null - * @param namespaceService NamespaceService instance - */ - @SuppressWarnings("unchecked") - public static void generateAssociationField(AssociationDefinition assocDef, Form form, List assocValues, - FieldGroup group, NamespaceService namespaceService) - { - String assocName = assocDef.getName().toPrefixString(namespaceService); - String[] nameParts = QName.splitPrefixedQName(assocName); - AssociationFieldDefinition fieldDef = new AssociationFieldDefinition(assocName, assocDef.getTargetClass() - .getName().toPrefixString(namespaceService), Direction.TARGET); - String title = assocDef.getTitle(); - if (title == null) - { - title = assocName; - } - fieldDef.setLabel(title); - fieldDef.setDescription(assocDef.getDescription()); - fieldDef.setProtectedField(assocDef.isProtected()); - fieldDef.setEndpointMandatory(assocDef.isTargetMandatory()); - fieldDef.setEndpointMany(assocDef.isTargetMany()); - fieldDef.setGroup(group); + protected abstract Map getPropertyValues(ItemType item); - // define the data key name and set - String dataKeyName = ASSOC_DATA_PREFIX + nameParts[0] + DATA_KEY_SEPARATOR + nameParts[1]; - fieldDef.setDataKeyName(dataKeyName); + protected abstract Map getTransientValues(ItemType item); - // add definition to the form - form.addFieldDefinition(fieldDef); - - if (assocValues != null) - { - // add the association value to the form - // determine the type of association values data and extract - // accordingly - List values = new ArrayList(4); - for (Object value : assocValues) - { - if (value instanceof ChildAssociationRef) - { - values.add(((ChildAssociationRef) value).getChildRef().toString()); - } - else if (value instanceof AssociationRef) - { - values.add(((AssociationRef) value).getTargetRef().toString()); - } - else - { - values.add(value.toString()); - } - } - - // Add the list as the value for the association. - form.addData(dataKeyName, values); - } - } - - /** - * Retrieves a logger instance to log to. - * - * @return Log instance to log to. - */ - protected abstract Log getLogger(); - - /** - * Sets up the field definitions for all the requested fields. - *

- * A NodeRef or TypeDefinition can be provided, however, if a NodeRef is - * provided all type information will be derived from the NodeRef and the - * TypeDefinition will be ignored. - *

- *

- * If any of the requested fields are not present on the type and they - * appear in the forcedFields list an attempt to find a model definition for - * those fields is made so they can be included. - *

- * - * @param nodeRef The NodeRef of the item being generated - * @param typeDef The TypeDefiniton of the item being generated - * @param fields Restricted list of fields to include - * @param forcedFields List of field names that should be included even if - * the field is not currently present - * @param form The Form instance to populate - */ - protected void generateSelectedFields(NodeRef nodeRef, TypeDefinition typeDef, List fields, - List forcedFields, Form form) - { - // ensure a NodeRef or TypeDefinition is provided - if (nodeRef == null && typeDef == null) { throw new IllegalArgumentException( - "A NodeRef or TypeDefinition must be provided"); } - - if (getLogger().isDebugEnabled()) - getLogger().debug("Generating selected fields: " + fields + " and forcing: " + forcedFields); - - // get data dictionary definition for node if it is provided - QName type = null; - Map propValues = Collections.emptyMap(); - Map propDefs = null; - Map assocDefs = null; - - if (nodeRef != null) - { - type = this.nodeService.getType(nodeRef); - typeDef = this.dictionaryService.getAnonymousType(type, this.nodeService.getAspects(nodeRef)); - - // NOTE: the anonymous type returns all property and association - // defs - // for all aspects applied as well as the type - propDefs = typeDef.getProperties(); - assocDefs = typeDef.getAssociations(); - propValues = this.nodeService.getProperties(nodeRef); - } - else - { - type = typeDef.getName(); - - // we only get the properties and associations of the actual type so - // we also need to manually get properties and associations from any - // mandatory aspects - propDefs = new HashMap(16); - assocDefs = new HashMap(16); - propDefs.putAll(typeDef.getProperties()); - assocDefs.putAll(typeDef.getAssociations()); - - List aspects = typeDef.getDefaultAspects(true); - for (AspectDefinition aspect : aspects) - { - propDefs.putAll(aspect.getProperties()); - assocDefs.putAll(aspect.getAssociations()); - } - } - - for (String fieldName : fields) - { - // try and split the field name - String[] parts = fieldName.split(":"); - if (parts.length == 2 || parts.length == 3) - { - boolean foundField = false; - boolean tryProperty = true; - boolean tryAssociation = true; - String qNamePrefix = null; - String localName = null; - - if (parts.length == 2) - { - qNamePrefix = parts[0]; - localName = parts[1]; - } - else - { - // if there are 3 parts to the field name the first one - // represents - // whether the field is a property or association i.e. - // prop:prefix:local - // or assoc:prefix:local, determine the prefix and ensure - // it's valid - if (PROP.equals(parts[0])) - { - tryAssociation = false; - } - else if (ASSOC.equals(parts[0])) - { - tryProperty = false; - } - else - { - if (getLogger().isWarnEnabled()) - getLogger() - .warn( - "\"" - + parts[0] - + "\" is an invalid prefix for requesting a property or association"); - - continue; - } - - qNamePrefix = parts[1]; - localName = parts[2]; - } - - // create qname of field name - QName fullQName = QName.createQName(qNamePrefix, localName, namespaceService); - - // try the field as a property - if (tryProperty) - { - // lookup property def on node - PropertyDefinition propDef = propDefs.get(fullQName); - if (propDef != null) - { - // generate the property field - generatePropertyField(propDef, form, propValues.get(fullQName), this.namespaceService); - - // no need to try and find an association - tryAssociation = false; - foundField = true; - } - } - - // try the field as an association - if (tryAssociation) - { - AssociationDefinition assocDef = assocDefs.get(fullQName); - if (assocDef != null) - { - // generate the association field - generateAssociationField(assocDef, form, (nodeRef != null) ? retrieveAssociationValues(nodeRef, - assocDef) : null, this.namespaceService); - - foundField = true; - } - } - - // still not found the field, is it a force'd field? - if (!foundField) - { - if (forcedFields != null && forcedFields.size() > 0 && forcedFields.contains(fieldName)) - { - generateForcedField(fieldName, form); - } - else if (getLogger().isDebugEnabled()) - { - getLogger().debug( - "Ignoring field \"" + fieldName + "\" as it is not defined for the current " - + ((nodeRef != null) ? "node" : "type") - + " and it does not appear in the 'force' list"); - } - } - } - else - { - // see if the fieldName is a well known transient property - if (TRANSIENT_MIMETYPE.equals(fieldName) || TRANSIENT_ENCODING.equals(fieldName) - || TRANSIENT_SIZE.equals(fieldName)) - { - // if the node type is content or sublcass thereof generate appropriate field - if (this.dictionaryService.isSubClass(type, ContentModel.TYPE_CONTENT)) - { - ContentData content = null; - - if (nodeRef != null) - { - content = (ContentData) this.nodeService.getProperty(nodeRef, ContentModel.PROP_CONTENT); - } - - if (TRANSIENT_MIMETYPE.equals(fieldName)) - { - generateMimetypePropertyField(content, form); - } - else if (TRANSIENT_ENCODING.equals(fieldName)) - { - generateEncodingPropertyField(content, form); - } - else if (TRANSIENT_SIZE.equals(fieldName)) - { - generateSizePropertyField(content, form); - } - } - } - else if (getLogger().isWarnEnabled()) - { - getLogger().warn("Ignoring unrecognised field \"" + fieldName + "\""); - } - } - } - } - - /** - * Generates a field definition for the given field that is being forced to - * show. - * - * @param fieldName Name of the field to force - * @param form The Form instance to populated - */ - protected void generateForcedField(String fieldName, Form form) - { - if (getLogger().isDebugEnabled()) - getLogger().debug("Attempting to force the inclusion of field \"" + fieldName + "\""); - - String[] parts = fieldName.split(":"); - if (parts.length == 2 || parts.length == 3) - { - boolean foundField = false; - boolean tryProperty = true; - boolean tryAssociation = true; - String qNamePrefix = null; - String localName = null; - - if (parts.length == 2) - { - qNamePrefix = parts[0]; - localName = parts[1]; - } - else - { - // if there are 3 parts to the field name the first one - // represents - // whether the field is a property or association i.e. - // prop:prefix:local - // or assoc:prefix:local, determine the prefix and ensure it's - // valid - if (PROP.equals(parts[0])) - { - tryAssociation = false; - } - else if (ASSOC.equals(parts[0])) - { - tryProperty = false; - } - else - { - if (getLogger().isWarnEnabled()) - getLogger().warn( - "\"" + parts[0] - + "\" is an invalid prefix for requesting a property or association"); - - return; - } - - qNamePrefix = parts[1]; - localName = parts[2]; - } - - // create qname of field name - QName fullQName = QName.createQName(qNamePrefix, localName, namespaceService); - - if (tryProperty) - { - // lookup the field as a property in the whole model - PropertyDefinition propDef = this.dictionaryService.getProperty(fullQName); - if (propDef != null) - { - // generate the property field - generatePropertyField(propDef, form, this.namespaceService); - - // no need to try and find an association - tryAssociation = false; - foundField = true; - } - } - - if (tryAssociation) - { - // lookup the field as an association in the whole model - AssociationDefinition assocDef = this.dictionaryService.getAssociation(fullQName); - if (assocDef != null) - { - // generate the association field - generateAssociationField(assocDef, form, this.namespaceService); - - foundField = true; - } - } - - if (!foundField && getLogger().isDebugEnabled()) - { - getLogger() - .debug( - "Ignoring field \"" - + fieldName - + "\" as it is not defined for the current node and can not be found in any model"); - } - } - else if (getLogger().isWarnEnabled()) - { - getLogger().warn("Ignoring unrecognised field \"" + fieldName + "\""); - } - } + protected abstract TypeDefinition getBaseType(ItemType item); /** * Generates the field definition for the transient mimetype property diff --git a/source/java/org/alfresco/repo/forms/processor/node/DefaultFieldBuilder.java b/source/java/org/alfresco/repo/forms/processor/node/DefaultFieldBuilder.java new file mode 100644 index 0000000000..3121324cbc --- /dev/null +++ b/source/java/org/alfresco/repo/forms/processor/node/DefaultFieldBuilder.java @@ -0,0 +1,150 @@ +/* + * 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 . + */ + +package org.alfresco.repo.forms.processor.node; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.alfresco.repo.forms.Field; +import org.alfresco.repo.forms.processor.FieldProcessor; +import org.alfresco.repo.forms.processor.FieldProcessorRegistry; +import org.alfresco.repo.forms.processor.FormCreationData; +import org.alfresco.service.namespace.QName; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * @author Nick Smith + */ +public class DefaultFieldBuilder +{ + private static final String ASSOC_WARN = "Could not build Association Field as no valid FieldProcessor was specified"; + private static final String PROP_WARNING = "Could not build Property Field as no valid FieldProcessor was specified"; + + private static final Log MY_LOGGER = LogFactory.getLog(DefaultFieldBuilder.class); + + private final FormCreationData formData; + private final ItemData ItemData; + private final FieldProcessorRegistry registry; + + private final Log logger; + + public DefaultFieldBuilder(FormCreationData data, + FieldProcessorRegistry registry) + { + this(data, registry, MY_LOGGER); + } + + public DefaultFieldBuilder(FormCreationData formData, + FieldProcessorRegistry registry, + Log logger) + { + this.logger = logger; + this.formData = formData; + this.registry = registry; + this.ItemData = (ItemData) formData.getItemData(); + } + + public List buildDefaultFields() + { + List assocFields = buildDefaultAssociationFields(); + List propFields = buildDefaultPropertyFields(); + List transFields = buildDefaultTransientFields(); + int size = assocFields.size() + propFields.size() + transFields.size(); + ArrayList fields = new ArrayList(size); + fields.addAll(assocFields); + fields.addAll(propFields); + fields.addAll(transFields); + return fields; + } + + public List buildDefaultPropertyFields() + { + Collection names = ItemData.getAllPropertyDefinitionNames(); + List fields = new ArrayList(names.size()); + for (QName name : names) + { + fields.add(buildPropertyField(name)); + } + return fields; + } + + public List buildDefaultAssociationFields() + { + Collection names = ItemData.getAllAssociationDefinitionNames(); + List fields = new ArrayList(names.size()); + for (QName name : names) + { + fields.add(buildAssociationField(name)); + } + return fields; + } + + public List buildDefaultTransientFields() + { + Collection names = ItemData.getAllTransientFieldNames(); + List fields = new ArrayList(names.size()); + for (String name : names) + { + fields.add(buildTransientField(name)); + } + return fields; + } + + public Field buildAssociationField(QName assocName) + { + return buildQNameField(assocName, FormFieldConstants.ASSOC, ASSOC_WARN); + } + + public Field buildPropertyField(QName propName) + { + return buildQNameField(propName, FormFieldConstants.PROP, PROP_WARNING); + } + + private Field buildQNameField(QName assocName, String key, String warningMsg) + { + FieldProcessor fieldProcessor = registry.get(key); + if (fieldProcessor != null && fieldProcessor instanceof QNameFieldProcessor) + { + QNameFieldProcessor qnameProcessor = (QNameFieldProcessor) fieldProcessor; + return qnameProcessor.generateField(assocName, ItemData, false); + } + + if (logger.isWarnEnabled()) + logger.warn(warningMsg); + + return null; + } + + public Field buildTransientField(String name) + { + FieldProcessor fieldProcessor = registry.get(name); + if (fieldProcessor != null) + { + return fieldProcessor.generateField(name, formData); + } + + if (logger.isWarnEnabled()) + logger.warn("Could not build Transient Field: "+ name +" as no FieldProcessor specified"); + + return null; + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/forms/processor/node/DefaultFieldProcessor.java b/source/java/org/alfresco/repo/forms/processor/node/DefaultFieldProcessor.java new file mode 100644 index 0000000000..1fc8bd23f4 --- /dev/null +++ b/source/java/org/alfresco/repo/forms/processor/node/DefaultFieldProcessor.java @@ -0,0 +1,119 @@ +/* + * 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 . + */ + +package org.alfresco.repo.forms.processor.node; + +import org.alfresco.repo.forms.Field; +import org.alfresco.repo.forms.FieldGroup; +import org.alfresco.service.cmr.dictionary.ClassAttributeDefinition; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.ParameterCheck; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.InitializingBean; + +public class DefaultFieldProcessor extends QNameFieldProcessor implements InitializingBean +{ + private static final Log logger = LogFactory.getLog(DefaultFieldProcessor.class); + + private final AssociationFieldProcessor assocProcessor = new AssociationFieldProcessor(); + private final PropertyFieldProcessor propProcessor = new PropertyFieldProcessor(); + + @Override + protected Log getLogger() + { + return logger; + } + + @Override + protected QName getFullName(String name) + { + String[] parts = name.split(FormFieldConstants.FIELD_NAME_SEPARATOR); + int position = parts.length - 1; + String localName = parts[position];// local name is always the last + // string in the arry + position--; + // prefix is always the penultimate string in the array. + String prefix = parts[position]; + return QName.createQName(prefix, localName, namespaceService); + } + + @Override + public Field generateField(QName fullName, ItemData itemData, boolean isForcedField) + { + Field fieldInfo = propProcessor.generateField(fullName, itemData, isForcedField); + if (fieldInfo == null) + { + fieldInfo = assocProcessor.generateField(fullName, itemData, isForcedField); + } + return fieldInfo; + } + + @Override + protected String getRegistryKey() + { + return ""; + } + + public void afterPropertiesSet() throws Exception + { + ParameterCheck.mandatory("dictionaryService", dictionaryService); + ParameterCheck.mandatory("namespaceService", namespaceService); + assocProcessor.setDictionaryService(dictionaryService); + assocProcessor.setNamespaceService(namespaceService); + propProcessor.setDictionaryService(dictionaryService); + propProcessor.setNamespaceService(namespaceService); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.forms.processor.node.QNameFieldProcessor#getGroup(org.alfresco.service.cmr.dictionary.ClassAttributeDefinition) + */ + @Override + protected FieldGroup getGroup(ClassAttributeDefinition typeDef) + { + throw new UnsupportedOperationException("This method should never be called!"); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.forms.processor.node.QNameFieldProcessor#getTypeDefinition(org.alfresco.service.namespace.QName, org.alfresco.repo.forms.processor.node.ItemData, boolean) + */ + @Override + protected ClassAttributeDefinition getTypeDefinition(QName fullName, ItemData itemData, boolean isForcedField) + { + throw new UnsupportedOperationException("This method should never be called!"); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.forms.processor.node.QNameFieldProcessor#getValue(org.alfresco.service.namespace.QName, org.alfresco.repo.forms.processor.node.ItemData) + */ + @Override + protected Object getValue(QName fullName, ItemData itemData) + { + throw new UnsupportedOperationException("This method should never be called!"); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.forms.processor.node.QNameFieldProcessor#makeField(org.alfresco.service.cmr.dictionary.ClassAttributeDefinition, java.lang.Object, org.alfresco.repo.forms.FieldGroup) + */ + @Override + protected Field makeField(ClassAttributeDefinition typeDef, Object value, FieldGroup group) + { + throw new UnsupportedOperationException("This method should never be called!"); + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/forms/processor/node/FieldProcessorTest.java b/source/java/org/alfresco/repo/forms/processor/node/FieldProcessorTest.java new file mode 100644 index 0000000000..6625ee66a1 --- /dev/null +++ b/source/java/org/alfresco/repo/forms/processor/node/FieldProcessorTest.java @@ -0,0 +1,189 @@ +/* + * 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 . + */ + +package org.alfresco.repo.forms.processor.node; + +import static org.alfresco.repo.forms.processor.node.FormFieldConstants.ASSOC; +import static org.alfresco.repo.forms.processor.node.FormFieldConstants.PROP; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +import junit.framework.TestCase; + +import org.alfresco.repo.forms.AssociationFieldDefinition; +import org.alfresco.repo.forms.Field; +import org.alfresco.repo.forms.PropertyFieldDefinition; +import org.alfresco.repo.forms.AssociationFieldDefinition.Direction; +import org.alfresco.repo.forms.processor.FormCreationData; +import org.alfresco.service.cmr.dictionary.AssociationDefinition; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.NamespaceServiceMemoryImpl; +import org.alfresco.service.namespace.QName; + +/** + * @author Nick Smith + */ +public class FieldProcessorTest extends TestCase +{ + private static final String PREFIX = "test"; + private static final String URI = "http://test/"; + private static final String NAME1 = "Name1"; + private static final String NAME2 = "Name2"; + private static final String DESCRIPTION1 = "Description"; + private static final String DESCRIPTION2 = "Another Description"; + private static final String TITLE = "Title"; + private static final QName qName1 = QName.createQName(URI, NAME1); + private static final QName qName2 = QName.createQName(URI, NAME2); + + private NamespaceService namespaceService; + private FormCreationData data; + + public void testMakeAssociationFieldDefinition() throws Exception + { + AssociationFieldProcessor processor = new AssociationFieldProcessor(); + processor.setNamespaceService(namespaceService); + + String name1 = ASSOC + ":"+ PREFIX +":"+ NAME1; + Field field = processor.generateField(name1, data); + AssociationFieldDefinition assocFieldDef = (AssociationFieldDefinition) field.getFieldDefinition(); + + assertNotNull(assocFieldDef); + assertEquals("assoc_" + PREFIX + "_" + NAME1, assocFieldDef.getDataKeyName()); + assertEquals(PREFIX + ":" + NAME1, assocFieldDef.getName()); + assertEquals(PREFIX + ":" + NAME1, assocFieldDef.getLabel()); + assertEquals(Direction.TARGET, assocFieldDef.getEndpointDirection()); + assertEquals(PREFIX + ":Target", assocFieldDef.getEndpointType()); + assertEquals(DESCRIPTION1, assocFieldDef.getDescription()); + assertFalse(assocFieldDef.isProtectedField()); + assertFalse(assocFieldDef.isEndpointMandatory()); + assertFalse(assocFieldDef.isEndpointMany()); + + // Repeat using different params to ensuere the fieldDefinition values + // are dependant on the AssociationDefinition values. + String name2 = ASSOC + ":" + PREFIX +":"+ NAME2; + field = processor.generateField(name2, data); + assocFieldDef = (AssociationFieldDefinition) field.getFieldDefinition(); + + assertEquals(TITLE, assocFieldDef.getLabel()); + assertEquals(DESCRIPTION2, assocFieldDef.getDescription()); + assertTrue(assocFieldDef.isProtectedField()); + assertTrue(assocFieldDef.isEndpointMandatory()); + assertTrue(assocFieldDef.isEndpointMany()); + } + + public void testMakePropertyFieldDefinition() throws Exception + { + PropertyFieldProcessor processor = new PropertyFieldProcessor(); + processor.setNamespaceService(namespaceService); + + String name1 = PROP+ ":" + PREFIX + ":" + NAME1; + Field field = processor.generateField(name1, data); + PropertyFieldDefinition propFieldDef = (PropertyFieldDefinition) field.getFieldDefinition(); + assertNotNull(propFieldDef); + assertEquals("prop_" + PREFIX + "_" + NAME1, propFieldDef.getDataKeyName()); + assertEquals(PREFIX + ":" + NAME1, propFieldDef.getName()); + assertEquals(PREFIX + ":" + NAME1, propFieldDef.getLabel()); + assertEquals("Default1", propFieldDef.getDefaultValue()); + assertEquals(DESCRIPTION1, propFieldDef.getDescription()); + assertFalse(propFieldDef.isProtectedField()); + assertFalse(propFieldDef.isMandatory()); + assertFalse(propFieldDef.isRepeating());// Maps to isMultiValued() on + + // Repeat using different params to ensuere the fieldDefinition values + // are dependant on the PropertyDefinition values. + String name2 = PROP + ":" + PREFIX + ":" + NAME2; + field = processor.generateField(name2, data); + propFieldDef = (PropertyFieldDefinition) field.getFieldDefinition(); + assertEquals(TITLE, propFieldDef.getLabel()); + assertEquals(DESCRIPTION2, propFieldDef.getDescription()); + assertEquals("Default2", propFieldDef.getDefaultValue()); + assertTrue(propFieldDef.isProtectedField()); + assertTrue(propFieldDef.isMandatory()); + assertTrue(propFieldDef.isRepeating()); + } + + /* + * @see junit.framework.TestCase#setUp() + */ + @Override + protected void setUp() throws Exception + { + super.setUp(); + namespaceService = makeNamespaceService(); + data = new FormCreationData(makeItemData(), null, null); + } + + private ItemData makeItemData() + { + Map propDefs = makePropertyDefs(); + Map assocDefs = makeAssociationDefs(); + + Map propValues = new HashMap(); + Map assocValues = new HashMap(); + Map transientValues = new HashMap(); + return new ItemData(null, propDefs, assocDefs, propValues, assocValues, transientValues); + } + + private Map makeAssociationDefs() + { + QName targetClass = QName.createQName(URI, "Target"); + AssociationDefinition assocDef1 = MockClassAttributeDefinition.mockAssociationDefinition( + qName1, targetClass, + null,// Defalt title, so sets label to be same as name. + DESCRIPTION1, false, false, false); + MockClassAttributeDefinition assocDef2 = MockClassAttributeDefinition.mockAssociationDefinition( + qName2, targetClass, + TITLE, DESCRIPTION2, + true, true, true); + Map assocDefs = new HashMap(); + assocDefs.put(qName1, assocDef1); + assocDefs.put(qName2, assocDef2); + return assocDefs; + } + + private Map makePropertyDefs() + { + QName dataTypeName = QName.createQName(URI, "Type"); + PropertyDefinition propDef1 = MockClassAttributeDefinition.mockPropertyDefinition( + qName1, dataTypeName, + null,// Defalt title, so sets label to be same as name. + DESCRIPTION1, false, + "Default1", false, false); + PropertyDefinition propDef2 = MockClassAttributeDefinition.mockPropertyDefinition( + qName2, dataTypeName, + TITLE, + DESCRIPTION2, true, + "Default2", true, true); + Map propDefs = new HashMap(); + propDefs.put(qName1, propDef1); + propDefs.put(qName2, propDef2); + return propDefs; + } + + private NamespaceService makeNamespaceService() + { + NamespaceServiceMemoryImpl nsService = new NamespaceServiceMemoryImpl(); + namespaceService.registerNamespace(PREFIX, URI); + return nsService; + } + +} diff --git a/source/java/org/alfresco/repo/forms/processor/node/FieldUtils.java b/source/java/org/alfresco/repo/forms/processor/node/FieldUtils.java new file mode 100644 index 0000000000..aca0a00033 --- /dev/null +++ b/source/java/org/alfresco/repo/forms/processor/node/FieldUtils.java @@ -0,0 +1,131 @@ +/* + * 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 . + */ + +package org.alfresco.repo.forms.processor.node; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import org.alfresco.repo.forms.Field; +import org.alfresco.repo.forms.FieldGroup; +import org.alfresco.service.cmr.dictionary.AssociationDefinition; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.namespace.NamespaceService; + +/** + * Utility class to assist in creating {@link Field Fields} which represent + * {@link PropertyDefinition PropertyDefinitions} and + * {@link AssociationDefinition AssociationDefinitions} + * + * @author Nick Smith + * + */ +public class FieldUtils +{ + + public static Field makePropertyField( + PropertyDefinition property, + Object value, + FieldGroup group, + NamespaceService namespaceService) + { + PropertyFieldProcessor processor = new PropertyFieldProcessor(namespaceService, null); + return processor.makeField(property, value, group); + } + + public static List makePropertyFields( + Collection propDefs, + FieldGroup group, + NamespaceService namespaceService) + { + return makePropertyFields(propDefs, null, group, namespaceService); + } + + public static List makePropertyFields( + Map propDefAndValue, + FieldGroup group, + NamespaceService namespaceService) + { + return makePropertyFields(propDefAndValue.keySet(), propDefAndValue, group, namespaceService); + } + + public static List makePropertyFields( + Collection propDefs, + Map values, + FieldGroup group, + NamespaceService namespaceService) + { + PropertyFieldProcessor processor = new PropertyFieldProcessor(namespaceService, null); + ArrayList fields = new ArrayList(propDefs.size()); + for (PropertyDefinition propDef : propDefs) + { + Object value = values==null?null:values.get(propDef); + Field field = processor.makeField(propDef, value, group); + fields.add(field); + } + return fields; + } + + public static Field makeAssociationField( + AssociationDefinition assocDef, + Object value, + FieldGroup group, + NamespaceService namespaceService) + { + AssociationFieldProcessor processor = new AssociationFieldProcessor(namespaceService, null); + return processor.makeField(assocDef, value, group); + } + + + public static List makeAssociationFields( + Collection assocDefs, + FieldGroup group, + NamespaceService namespaceService) + { + return makeAssociationFields(assocDefs, null, group, namespaceService); + } + + public static List makeAssociationFields( + Map assocDefAndValue, + FieldGroup group, + NamespaceService namespaceService) + { + return makeAssociationFields(assocDefAndValue.keySet(), assocDefAndValue, group, namespaceService); + } + + public static List makeAssociationFields( + Collection assocDefs, + Map values, + FieldGroup group, + NamespaceService namespaceService) + { + AssociationFieldProcessor processor = new AssociationFieldProcessor(namespaceService, null); + ArrayList fields = new ArrayList(assocDefs.size()); + for (AssociationDefinition propDef : assocDefs) + { + Object value = values==null?null:values.get(propDef); + Field field = processor.makeField(propDef, value, group); + fields.add(field); + } + return fields; + } + +} diff --git a/source/java/org/alfresco/repo/forms/processor/node/FormFieldConstants.java b/source/java/org/alfresco/repo/forms/processor/node/FormFieldConstants.java index 8877537eaa..c8c7fab675 100644 --- a/source/java/org/alfresco/repo/forms/processor/node/FormFieldConstants.java +++ b/source/java/org/alfresco/repo/forms/processor/node/FormFieldConstants.java @@ -31,6 +31,8 @@ public interface FormFieldConstants public static final String ASSOC = "assoc"; + public static final String FIELD_NAME_SEPARATOR = ":"; + public static final String DATA_KEY_SEPARATOR = "_"; public static final String PROP_DATA_PREFIX = PROP + DATA_KEY_SEPARATOR; @@ -47,4 +49,7 @@ public interface FormFieldConstants public static final String TRANSIENT_ENCODING = "encoding"; + public static final String ADDED = "added"; + + public static final String REMOVED = "removed"; } diff --git a/source/java/org/alfresco/repo/forms/processor/node/ItemData.java b/source/java/org/alfresco/repo/forms/processor/node/ItemData.java new file mode 100644 index 0000000000..b68505a648 --- /dev/null +++ b/source/java/org/alfresco/repo/forms/processor/node/ItemData.java @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2005-2009 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ + +package org.alfresco.repo.forms.processor.node; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; + +import org.alfresco.service.cmr.dictionary.AssociationDefinition; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.namespace.QName; + +/** + * Simple data transfer object used by the ContentModelFormProcessor and its + * descendants. + * + * @author Nick Smith + */ +public class ItemData implements TransientValueGetter +{ + private final ItemType item; + private final Map propDefs; + private final Map assocDefs; + private final Map propValues; + private final Map assocValues; + private final Map transientValues; + + public ItemData(ItemType item, + Map propDefs, + Map assocDefs, + Map propValues, + Map assocValues, + Map transientValues) + { + this.item = item; + this.propDefs = propDefs; + this.assocDefs = assocDefs; + this.propValues = propValues; + this.assocValues = assocValues; + this.transientValues = transientValues; + } + + /** + * @return the item + */ + public ItemType getItem() + { + return this.item; + } + + /** + * @return the property value associated with the key or + * null if none exists. + */ + public Serializable getPropertyValue(QName key) + { + return getValue(key, propValues); + } + + /** + * @return the association value associated with the key or + * null if none exists. + */ + public Serializable getAssociationValue(QName key) + { + return getValue(key, assocValues); + } + + /** + * @return the value associated with the transient property specified by the + * fieldName or null if none exists. + */ + public Object getTransientValue(String fieldName) + { + Object value = null; + + if (transientValues != null) + { + value = transientValues.get(fieldName); + } + + return value; + } + + private Serializable getValue(QName key, Map values) + { + Serializable value = null; + if (values != null) + { + value = values.get(key); + } + return value; + } + + /** + * @return The PropertyDefinition associated with the propName + * or null if none exists. + */ + public PropertyDefinition getPropertyDefinition(QName propName) + { + PropertyDefinition propDef = null; + if (propDefs != null) + { + propDef = propDefs.get(propName); + } + return propDef; + } + + /** + * @return The AssociationDefinition associated with the + * assocName or null if none exists. + */ + public AssociationDefinition getAssociationDefinition(QName assocName) + { + AssociationDefinition assocDef = null; + if (assocDefs != null) + { + assocDef = assocDefs.get(assocName); + } + return assocDef; + } + + /** + * @return Returns an unmodifiable Collection containing all the association + * definition {@link QName QNames} for the item. + */ + public Collection getAllAssociationDefinitionNames() + { + if (assocDefs == null) + { + return Collections.emptyList(); + } + else + { + return Collections.unmodifiableCollection(assocDefs.keySet()); + } + } + + /** + * @return Returns an unmodifiable Collection containing all the property + * definitions for the item. + */ + public Collection getAllPropertyDefinitionNames() + { + if (propDefs == null) + { + return Collections.emptyList(); + } + else + { + return Collections.unmodifiableCollection(propDefs.keySet()); + } + } + + /** + * @return Returns an unmodifiable Collection containing all the property + * definitions for the item. + */ + public Collection getAllTransientFieldNames() + { + if (transientValues == null) + { + return Collections.emptyList(); + } + else + { + return Collections.unmodifiableCollection(transientValues.keySet()); + } + } +} diff --git a/source/java/org/alfresco/repo/forms/processor/node/MockClassAttributeDefinition.java b/source/java/org/alfresco/repo/forms/processor/node/MockClassAttributeDefinition.java new file mode 100644 index 0000000000..fe6a7f28b0 --- /dev/null +++ b/source/java/org/alfresco/repo/forms/processor/node/MockClassAttributeDefinition.java @@ -0,0 +1,372 @@ +/* + * Copyright (C) 2005-2009 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ + +package org.alfresco.repo.forms.processor.node; + +import static org.mockito.Mockito.*; + +import java.util.List; + +import org.alfresco.repo.dictionary.IndexTokenisationMode; +import org.alfresco.service.cmr.dictionary.AssociationDefinition; +import org.alfresco.service.cmr.dictionary.ClassDefinition; +import org.alfresco.service.cmr.dictionary.ConstraintDefinition; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.dictionary.ModelDefinition; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.namespace.QName; + +/** + * @author Nick Smith + */ +public class MockClassAttributeDefinition implements PropertyDefinition, AssociationDefinition +{ + + private final QName name; + private DataTypeDefinition dataType = mock(DataTypeDefinition.class); + private ClassDefinition targetClass = mock(ClassDefinition.class); + private String description = null; + private String defaultValue = null; + private String title = null; + + private boolean targetMandatory = false; + private boolean targetMany = false; + private boolean isProtected = false; + private boolean mandatory = false; + private boolean multiValued = false; + + private MockClassAttributeDefinition(QName name) + { + this.name = name; + } + + private MockClassAttributeDefinition(QName name, String title, String description, boolean isProtected) + { + this(name); + this.title = title; + this.description = description; + this.isProtected = isProtected; + } + + public static MockClassAttributeDefinition mockPropertyDefinition(QName name, QName dataTypeName) + { + MockClassAttributeDefinition mock = new MockClassAttributeDefinition(name); + mockDataTypeName(dataTypeName, mock); + return mock; + } + + public static MockClassAttributeDefinition mockPropertyDefinition(QName name,// + QName dataTypeName,// + String title,// + String description,// + boolean isProtected,// + String defaultValue,// + boolean Mandatory,// + boolean multiValued) + { + MockClassAttributeDefinition mock = new MockClassAttributeDefinition(name, title, description, isProtected); + mockDataTypeName(dataTypeName, mock); + mock.defaultValue = defaultValue; + mock.mandatory = Mandatory; + mock.multiValued = multiValued; + return mock; + } + + public static MockClassAttributeDefinition mockAssociationDefinition(QName name, QName targetClassName) + { + MockClassAttributeDefinition mock = new MockClassAttributeDefinition(name); + mockTargetClassName(targetClassName, mock); + return mock; + } + + public static MockClassAttributeDefinition mockAssociationDefinition(QName name,// + QName targetClassName,// + String title,// + String description,// + boolean isProtected,// + boolean targetMandatory,// + boolean targetMany) + { + MockClassAttributeDefinition mock = new MockClassAttributeDefinition(name, title, description, isProtected); + mockTargetClassName(targetClassName, mock); + mock.targetMandatory = targetMandatory; + mock.targetMany = targetMany; + return mock; + } + + private static void mockDataTypeName(QName dataTypeName, MockClassAttributeDefinition mock) + { + when(mock.dataType.getName()).thenReturn(dataTypeName); + } + + private static void mockTargetClassName(QName targetClassName, MockClassAttributeDefinition mock) + { + when(mock.targetClass.getName()).thenReturn(targetClassName); + } + + /* + * @see + * org.alfresco.service.cmr.dictionary.PropertyDefinition#getConstraints() + */ + public List getConstraints() + { + return null; + } + + /* + * @see + * org.alfresco.service.cmr.dictionary.PropertyDefinition#getContainerClass + * () + */ + public ClassDefinition getContainerClass() + { + return null; + } + + /* + * @see org.alfresco.service.cmr.dictionary.PropertyDefinition#getDataType() + */ + public DataTypeDefinition getDataType() + { + return dataType; + } + + /* + * @see + * org.alfresco.service.cmr.dictionary.PropertyDefinition#getDefaultValue() + */ + public String getDefaultValue() + { + return defaultValue; + } + + /* + * @see + * org.alfresco.service.cmr.dictionary.PropertyDefinition#getDescription() + */ + public String getDescription() + { + return description; + } + + /* + * @seeorg.alfresco.service.cmr.dictionary.PropertyDefinition# + * getIndexTokenisationMode() + */ + public IndexTokenisationMode getIndexTokenisationMode() + { + return null; + } + + /* + * @see org.alfresco.service.cmr.dictionary.PropertyDefinition#getModel() + */ + public ModelDefinition getModel() + { + return null; + } + + /* + * @see org.alfresco.service.cmr.dictionary.PropertyDefinition#getName() + */ + public QName getName() + { + return name; + } + + /* + * @see org.alfresco.service.cmr.dictionary.PropertyDefinition#getTitle() + */ + public String getTitle() + { + return title; + } + + /* + * @see org.alfresco.service.cmr.dictionary.PropertyDefinition#isIndexed() + */ + public boolean isIndexed() + { + return false; + } + + /* + * @see + * org.alfresco.service.cmr.dictionary.PropertyDefinition#isIndexedAtomically + * () + */ + public boolean isIndexedAtomically() + { + return false; + } + + /* + * @see org.alfresco.service.cmr.dictionary.PropertyDefinition#isMandatory() + */ + public boolean isMandatory() + { + return mandatory; + } + + /* + * @see + * org.alfresco.service.cmr.dictionary.PropertyDefinition#isMandatoryEnforced + * () + */ + public boolean isMandatoryEnforced() + { + return false; + } + + /* + * @see + * org.alfresco.service.cmr.dictionary.PropertyDefinition#isMultiValued() + */ + public boolean isMultiValued() + { + return multiValued; + } + + /* + * @see org.alfresco.service.cmr.dictionary.PropertyDefinition#isOverride() + */ + public boolean isOverride() + { + return false; + } + + /* + * @see org.alfresco.service.cmr.dictionary.PropertyDefinition#isProtected() + */ + public boolean isProtected() + { + return isProtected; + } + + /* + * @see + * org.alfresco.service.cmr.dictionary.PropertyDefinition#isStoredInIndex() + */ + public boolean isStoredInIndex() + { + return false; + } + + /* + * @see + * org.alfresco.service.cmr.dictionary.AssociationDefinition#getSourceClass + * () + */ + public ClassDefinition getSourceClass() + { + return null; + } + + /* + * @see + * org.alfresco.service.cmr.dictionary.AssociationDefinition#getSourceRoleName + * () + */ + public QName getSourceRoleName() + { + return null; + } + + /* + * @see + * org.alfresco.service.cmr.dictionary.AssociationDefinition#getTargetClass + * () + */ + public ClassDefinition getTargetClass() + { + return targetClass; + } + + /* + * @see + * org.alfresco.service.cmr.dictionary.AssociationDefinition#getTargetRoleName + * () + */ + public QName getTargetRoleName() + { + return null; + } + + /* + * @see org.alfresco.service.cmr.dictionary.AssociationDefinition#isChild() + */ + public boolean isChild() + { + return false; + } + + /* + * @see + * org.alfresco.service.cmr.dictionary.AssociationDefinition#isSourceMandatory + * () + */ + public boolean isSourceMandatory() + { + return false; + } + + /* + * @see + * org.alfresco.service.cmr.dictionary.AssociationDefinition#isSourceMany() + */ + public boolean isSourceMany() + { + return false; + } + + /* + * @see + * org.alfresco.service.cmr.dictionary.AssociationDefinition#isTargetMandatory + * () + */ + public boolean isTargetMandatory() + { + return targetMandatory; + } + + /* + * @seeorg.alfresco.service.cmr.dictionary.AssociationDefinition# + * isTargetMandatoryEnforced() + */ + public boolean isTargetMandatoryEnforced() + { + return false; + } + + /* + * @see + * org.alfresco.service.cmr.dictionary.AssociationDefinition#isTargetMany() + */ + public boolean isTargetMany() + { + return targetMany; + } + +} diff --git a/source/java/org/alfresco/repo/forms/processor/node/MockFieldProcessorRegistry.java b/source/java/org/alfresco/repo/forms/processor/node/MockFieldProcessorRegistry.java new file mode 100644 index 0000000000..908d8985f6 --- /dev/null +++ b/source/java/org/alfresco/repo/forms/processor/node/MockFieldProcessorRegistry.java @@ -0,0 +1,75 @@ +package org.alfresco.repo.forms.processor.node; + +import static org.alfresco.repo.forms.processor.node.FormFieldConstants.ASSOC; +import static org.alfresco.repo.forms.processor.node.FormFieldConstants.PROP; +import static org.alfresco.repo.forms.processor.node.FormFieldConstants.TRANSIENT_ENCODING; +import static org.alfresco.repo.forms.processor.node.FormFieldConstants.TRANSIENT_MIMETYPE; +import static org.alfresco.repo.forms.processor.node.FormFieldConstants.TRANSIENT_SIZE; + +import org.alfresco.repo.forms.processor.FieldProcessor; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.namespace.NamespaceService; + +public class MockFieldProcessorRegistry extends ContentModelFieldProcessorRegistry +{ + public MockFieldProcessorRegistry(NamespaceService namespaceService, DictionaryService dictionaryService) + { + register(PROP, makePropertyFieldProcessor(namespaceService, dictionaryService)); + register(ASSOC, makeAssociationFieldProcessor(namespaceService, dictionaryService)); + register(TRANSIENT_ENCODING, makeEncodingFieldProcessor(namespaceService)); + register(TRANSIENT_MIMETYPE, makeMimetypeFieldProcessor(namespaceService)); + register(TRANSIENT_SIZE, makeSizeFieldProcessor(namespaceService)); + setDefaultProcessor(makeDefaultFieldProcessor(namespaceService, dictionaryService)); + } + + private FieldProcessor makeDefaultFieldProcessor(NamespaceService namespaceService, + DictionaryService dictionaryService) + { + DefaultFieldProcessor processor = new DefaultFieldProcessor(); + processor.setDictionaryService(dictionaryService); + processor.setNamespaceService(namespaceService); + try + { + processor.afterPropertiesSet(); + } + catch (Exception e) + { + throw new RuntimeException(e); + } + return processor; + } + + private TransientEncodingFieldProcessor makeEncodingFieldProcessor(NamespaceService namespaceService) + { + return new TransientEncodingFieldProcessor(); + } + + private TransientMimetypeFieldProcessor makeMimetypeFieldProcessor(NamespaceService namespaceService) + { + return new TransientMimetypeFieldProcessor(); + } + + private TransientSizeFieldProcessor makeSizeFieldProcessor(NamespaceService namespaceService) + { + return new TransientSizeFieldProcessor(); + } + + private PropertyFieldProcessor makePropertyFieldProcessor(NamespaceService namespaceService, + DictionaryService dictionaryService) + { + PropertyFieldProcessor processor = new PropertyFieldProcessor(); + processor.setDictionaryService(dictionaryService); + processor.setNamespaceService(namespaceService); + return processor; + } + + private AssociationFieldProcessor makeAssociationFieldProcessor(NamespaceService namespaceService, + DictionaryService dictionaryService) + { + AssociationFieldProcessor processor = new AssociationFieldProcessor(); + processor.setDictionaryService(dictionaryService); + processor.setNamespaceService(namespaceService); + return processor; + } + +} diff --git a/source/java/org/alfresco/repo/forms/processor/node/NodeFormProcessor.java b/source/java/org/alfresco/repo/forms/processor/node/NodeFormProcessor.java index e7f21788aa..6f6b577316 100644 --- a/source/java/org/alfresco/repo/forms/processor/node/NodeFormProcessor.java +++ b/source/java/org/alfresco/repo/forms/processor/node/NodeFormProcessor.java @@ -20,21 +20,24 @@ package org.alfresco.repo.forms.processor.node; import java.io.Serializable; +import java.util.HashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Set; import org.alfresco.model.ContentModel; -import org.alfresco.repo.forms.Form; import org.alfresco.repo.forms.FormData; import org.alfresco.repo.forms.FormNotFoundException; import org.alfresco.repo.forms.Item; -import org.alfresco.service.cmr.dictionary.AssociationDefinition; -import org.alfresco.service.cmr.dictionary.PropertyDefinition; import org.alfresco.service.cmr.dictionary.TypeDefinition; +import org.alfresco.service.cmr.repository.AssociationRef; +import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.InvalidNodeRefException; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.RegexQNamePattern; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -89,7 +92,8 @@ public class NodeFormProcessor extends ContentModelFormProcessor fields, List forcedFields, Form form, - Map context) + protected String getItemType(NodeRef item) { - if (logger.isDebugEnabled()) logger.debug("Generating form for: " + item); - - // generate the form for the node - generateNode(item, fields, forcedFields, form); - - if (logger.isDebugEnabled()) logger.debug("Generated form: " + form); + QName type = this.nodeService.getType(item); + return type.toPrefixString(this.namespaceService); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.forms.processor.FilteredFormProcessor#getItemURI(java.lang.Object) + */ + @Override + protected String getItemURI(NodeRef item) + { + StringBuilder builder = new StringBuilder("/api/node/"); + builder.append(item.getStoreRef().getProtocol()).append("/"); + builder.append(item.getStoreRef().getIdentifier()).append("/"); + builder.append(item.getId()); + return builder.toString(); + } + + @Override + protected Map getPropertyValues(NodeRef nodeRef) + { + return nodeService.getProperties(nodeRef); } - /** - * Sets up the Form object for the given NodeRef - * - * @param nodeRef The NodeRef to generate a Form for - * @param fields Restricted list of fields to include - * @param forcedFields List of fields to forcibly include - * @param form The Form instance to populate - */ - protected void generateNode(NodeRef nodeRef, List fields, List forcedFields, Form form) + @Override + protected Map getAssociationValues(NodeRef item) { - // set the type and URL of the item - QName type = this.nodeService.getType(nodeRef); - setFormItemType(form, type.toPrefixString(this.namespaceService)); - - StringBuilder builder = new StringBuilder("/api/node/"); - builder.append(nodeRef.getStoreRef().getProtocol()).append("/"); - builder.append(nodeRef.getStoreRef().getIdentifier()).append("/"); - builder.append(nodeRef.getId()); - setFormItemUrl(form, builder.toString()); - - if (fields != null && fields.size() > 0) + HashMap assocs = new HashMap(); + List targetAssocs = nodeService.getTargetAssocs(item, RegexQNamePattern.MATCH_ALL); + List childAssocs = nodeService.getChildAssocs(item); + for (ChildAssociationRef childAssoc : childAssocs) { - generateSelectedFields(nodeRef, null, fields, forcedFields, form); + QName name = childAssoc.getTypeQName(); + NodeRef target = childAssoc.getChildRef(); + addAssocToMap(name, target, assocs); + } + for (AssociationRef associationRef : targetAssocs) + { + QName name = associationRef.getTypeQName(); + NodeRef target = associationRef.getTargetRef(); + addAssocToMap(name, target, assocs); + } + return assocs; + } + + @SuppressWarnings("unchecked") + private void addAssocToMap(QName name, NodeRef target, HashMap assocs) + { + Serializable value = assocs.get(name); + if(value == null) + { + LinkedHashSet values = new LinkedHashSet(); + values.add(target); + assocs.put(name, values); } else { - // setup field definitions and data - generateAllPropertyFields(nodeRef, form); - generateAllAssociationFields(nodeRef, form); - generateTransientFields(nodeRef, form); - } - - // process working copy nodes, just returns if it's not - processWorkingCopy(nodeRef, form); - } - - /** - * Sets up the field definitions for all the node's properties. - * - * @param nodeRef The NodeRef of the node being setup - * @param form The Form instance to populate - */ - protected void generateAllPropertyFields(NodeRef nodeRef, Form form) - { - // get data dictionary definition for node - QName type = this.nodeService.getType(nodeRef); - TypeDefinition typeDef = this.dictionaryService.getAnonymousType(type, this.nodeService.getAspects(nodeRef)); - - // iterate round the property definitions for the node and create - // the equivalent field definition and setup the data for the property - Map propDefs = typeDef.getProperties(); - Map propValues = this.nodeService.getProperties(nodeRef); - for (PropertyDefinition propDef : propDefs.values()) - { - generatePropertyField(propDef, form, propValues.get(propDef.getName()), this.namespaceService); - } - } - - /** - * Sets up the field definitions for all the node's associations. - * - * @param nodeRef The NodeRef of the node being setup - * @param form The Form instance to populate - */ - protected void generateAllAssociationFields(NodeRef nodeRef, Form form) - { - // get data dictionary definition for the node - QName type = this.nodeService.getType(nodeRef); - TypeDefinition typeDef = this.dictionaryService.getAnonymousType(type, this.nodeService.getAspects(nodeRef)); - - // iterate round the association defintions and setup field definition - Map assocDefs = typeDef.getAssociations(); - for (AssociationDefinition assocDef : assocDefs.values()) - { - generateAssociationField(assocDef, form, retrieveAssociationValues(nodeRef, assocDef), - this.namespaceService); - } - } - - /** - * Sets up the field definitions for any transient fields that may be - * useful, for example, 'mimetype', 'size' and 'encoding'. - * - * @param nodeRef The NodeRef of the node being setup - * @param form The Form instance to populate - */ - protected void generateTransientFields(NodeRef nodeRef, Form form) - { - // if the node is content add the 'mimetype', 'size' and 'encoding' - // fields. - QName type = this.nodeService.getType(nodeRef); - if (this.dictionaryService.isSubClass(type, ContentModel.TYPE_CONTENT)) - { - ContentData content = (ContentData) this.nodeService.getProperty(nodeRef, ContentModel.PROP_CONTENT); - if (content != null) + if(value instanceof Set) { - // setup mimetype field - generateMimetypePropertyField(content, form); - - // setup encoding field - generateEncodingPropertyField(content, form); - - // setup size field - generateSizePropertyField(content, form); + ((Set)value).add(target); } } } + + @Override + protected Map getTransientValues(NodeRef item) + { + Map values = new HashMap(3); + ContentData contentData = getContentData(item); + if(contentData!=null) + { + values.put(TRANSIENT_ENCODING, contentData.getEncoding()); + values.put(TRANSIENT_MIMETYPE, contentData.getMimetype()); + values.put(TRANSIENT_SIZE, contentData.getSize()); + } + return values; + } + + @Override + protected Set getAspectNames(NodeRef nodeRef) + { + return nodeService.getAspects(nodeRef); + } + + @Override + protected TypeDefinition getBaseType(NodeRef nodeRef) + { + QName typeName = nodeService.getType(nodeRef); + return dictionaryService.getType(typeName); + } + + private ContentData getContentData(NodeRef nodeRef) + { + // Checks if the node is content and if so gets the ContentData + QName type = this.nodeService.getType(nodeRef); + ContentData content = null; + if (this.dictionaryService.isSubClass(type, ContentModel.TYPE_CONTENT)) + { + content = (ContentData) this.nodeService.getProperty(nodeRef, ContentModel.PROP_CONTENT); + } + return content; + } /* * @see @@ -244,7 +231,8 @@ public class NodeFormProcessor extends ContentModelFormProcessor. + */ + +package org.alfresco.repo.forms.processor.node; + +import static org.alfresco.repo.forms.processor.node.FormFieldConstants.PROP_DATA_PREFIX; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +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.service.cmr.dictionary.Constraint; +import org.alfresco.service.cmr.dictionary.ConstraintDefinition; +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.ContentData; +import org.alfresco.service.cmr.repository.Period; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.util.StringUtils; + +public class PropertyFieldProcessor extends QNameFieldProcessor +{ + private static final Log logger = LogFactory.getLog(PropertyFieldProcessor.class); + + public PropertyFieldProcessor() + { + // Constructor for Spring. + } + + public PropertyFieldProcessor(NamespaceService namespaceService, DictionaryService dictionaryService) + { + super(namespaceService, dictionaryService); + } + + @Override + protected Log getLogger() + { + return logger; + } + + @Override + protected PropertyDefinition getTypeDefinition(QName fullName, ItemData itemData, boolean isForcedField) + { + PropertyDefinition propDef = itemData.getPropertyDefinition(fullName); + if (propDef == null) + { + if (isForcedField) + { + propDef = dictionaryService.getProperty(fullName); + } + } + return propDef; + } + + @Override + public Field makeField(PropertyDefinition propDef, Object value, FieldGroup group) + { + PropertyFieldDefinition fieldDef = makePropertyFieldDefinition(propDef, group); + return new ContentField(propDef, fieldDef, value); + } + + @Override + protected FieldGroup getGroup(PropertyDefinition propDef) + { + // TODO Need to Implement this once Composite Content is implementd. + return null; + } + + @Override + protected Object getValue(QName name, ItemData data) + { + Serializable value = data.getPropertyValue(name); + if (value == null) + { + return null; + } + + if (value instanceof Collection) + { + // temporarily add repeating field data as a comma + // separated list, this will be changed to using + // a separate field for each value once we have full + // UI support in place. + Collection values = (Collection) value; + return StringUtils.collectionToCommaDelimitedString(values); + } + else if (value instanceof ContentData) + { + // for content properties retrieve the info URL rather than the + // the object value itself + ContentData contentData = (ContentData)value; + return contentData.getInfoUrl(); + } + return value; + } + + private PropertyFieldDefinition makePropertyFieldDefinition(final PropertyDefinition propDef, FieldGroup group) + { + String name = getPrefixedName(propDef); + QName dataType = propDef.getDataType().getName(); + PropertyFieldDefinition fieldDef = new PropertyFieldDefinition(name, dataType.getLocalName()); + + populateFieldDefinition(propDef, fieldDef, group, PROP_DATA_PREFIX); + + fieldDef.setDefaultValue(propDef.getDefaultValue()); + fieldDef.setMandatory(propDef.isMandatory()); + fieldDef.setRepeating(propDef.isMultiValued()); + + // any property from the system model (sys prefix) should be protected + // the model doesn't + // currently enforce this so make sure they are not editable + if (NamespaceService.SYSTEM_MODEL_1_0_URI.equals(propDef.getName().getNamespaceURI())) + { + fieldDef.setProtectedField(true); + } + + // If the property data type is d:period we need to setup a data + // type parameters object to represent the options and rules + if (dataType.equals(DataTypeDefinition.PERIOD)) + { + PeriodDataTypeParameters periodOptions = getPeriodOptions(); + fieldDef.setDataTypeParameters(periodOptions); + } + + // setup constraints for the property + List fieldConstraints = makeFieldConstraints(propDef); + fieldDef.setConstraints(fieldConstraints); + return fieldDef; + } + + private List makeFieldConstraints(PropertyDefinition propDef) + { + List fieldConstraints = null; + List constraints = propDef.getConstraints(); + if (constraints != null && constraints.size() > 0) + { + fieldConstraints = new ArrayList(constraints.size()); + for (ConstraintDefinition constraintDef : constraints) + { + Constraint constraint = constraintDef.getConstraint(); + String type = constraint.getType(); + Map params = constraint.getParameters(); + FieldConstraint fieldConstraint = new FieldConstraint(type, params); + fieldConstraints.add(fieldConstraint); + } + } + return fieldConstraints; + } + + private PeriodDataTypeParameters getPeriodOptions() + { + PeriodDataTypeParameters periodOptions = new PeriodDataTypeParameters(); + Set providers = Period.getProviderNames(); + for (String provider : providers) + { + periodOptions.addPeriodProvider(Period.getProvider(provider)); + } + return periodOptions; + } + + @Override + protected String getRegistryKey() + { + return FormFieldConstants.PROP; + } +} diff --git a/source/java/org/alfresco/repo/forms/processor/node/QNameFieldProcessor.java b/source/java/org/alfresco/repo/forms/processor/node/QNameFieldProcessor.java new file mode 100644 index 0000000000..6e88297154 --- /dev/null +++ b/source/java/org/alfresco/repo/forms/processor/node/QNameFieldProcessor.java @@ -0,0 +1,163 @@ +/* + * 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 . + */ +package org.alfresco.repo.forms.processor.node; + +import static org.alfresco.repo.forms.processor.node.FormFieldConstants.DATA_KEY_SEPARATOR; + +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.FormCreationData; +import org.alfresco.service.cmr.dictionary.ClassAttributeDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + +public abstract class QNameFieldProcessor extends AbstractFieldProcessor> +{ + protected NamespaceService namespaceService; + protected DictionaryService dictionaryService; + + public QNameFieldProcessor() + { + // Constructor for Spring. + } + + public QNameFieldProcessor(NamespaceService namespaceService, DictionaryService dictionaryService) + { + this.namespaceService = namespaceService; + this.dictionaryService = dictionaryService; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.forms.field.processor.AbstractFieldProcessor#generateTypedField(java.lang.String, java.lang.Object) + */ + @Override + protected Field generateTypedField(String fieldName, FormCreationData formData, ItemData typedData) + { + QName fullName = getFullName(fieldName); + boolean isForcedField = formData.isForcedField(fieldName); + Field field = generateField(fullName, typedData, isForcedField); + return field; + } + + protected QName getFullName(String name) + { + String[] parts = name.split(FormFieldConstants.FIELD_NAME_SEPARATOR); + String prefix = parts[1]; + String localName = parts[2]; + return QName.createQName(prefix, localName, namespaceService); + } + + protected String getPrefixedName(ClassAttributeDefinition attribDef) + { + return attribDef.getName().toPrefixString(namespaceService); + } + + public Field generateField(QName fullName, ItemData itemData, boolean isForcedField) + { + Type propDef = getTypeDefinition(fullName, itemData, isForcedField); + Field field = null; + if (propDef != null) + { + Object value = getValue(fullName, itemData); + FieldGroup group = getGroup(propDef); + field = makeField(propDef, value, group); + } + return field; + } + + /** + * Sets several properties on the {@link FieldDefinition}, including name, + * label, description, dataKeyName and whether the field is protected. These + * values are derived from the attribDef parameter. + * + * @param attribDef Used to set the values of name, description, label, + * dataKeyName and isProtected properties on the returned object. + * @param fieldDef A factory object used to create the FieldDefinition to be + * returned. + * @param group Used to set the group on the returned FieldDefinition. + */ + protected void populateFieldDefinition(Type attribDef, FieldDefinition fieldDef, + FieldGroup group, String dataKeyPrefix) + { + String attribName = fieldDef.getName(); + fieldDef.setGroup(group); + String title = attribDef.getTitle(); + title = title == null ? attribName : title; + fieldDef.setLabel(title); + fieldDef.setDescription(attribDef.getDescription()); + fieldDef.setProtectedField(attribDef.isProtected()); + + // define the data key name and set + String dataKeyName = makeDataKeyForName(attribName, dataKeyPrefix); + fieldDef.setDataKeyName(dataKeyName); + } + + protected String makeDataKeyForName(String propName, String prefix) + { + String[] nameParts = QName.splitPrefixedQName(propName); + String firstPart = nameParts[0]; + StringBuilder builder = new StringBuilder(prefix); + if (firstPart.length() > 0) + { + builder.append(firstPart); + builder.append(DATA_KEY_SEPARATOR); + } + builder.append(nameParts[1]); + return builder.toString(); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.forms.field.processor.AbstractFieldProcessor#getExpectedDataType() + */ + @SuppressWarnings("unchecked") + @Override + protected Class> getExpectedDataType() + { + // This is nasty but unavoidable because of generics. + Object clazz = ItemData.class; + return (Class>)clazz; + } + + /** + * @param namespaceService the namespaceService to set + */ + public void setNamespaceService(NamespaceService namespaceService) + { + this.namespaceService = namespaceService; + } + + /** + * @param dictionaryService the dictionaryService to set + */ + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + protected abstract Field makeField(Type typeDef, Object value, FieldGroup group); + + protected abstract FieldGroup getGroup(Type typeDef); + + protected abstract Object getValue(QName fullName, ItemData itemData); + + protected abstract Type getTypeDefinition(QName fullName, ItemData itemData, boolean isForcedField); +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/forms/processor/node/TransientEncodingFieldProcessor.java b/source/java/org/alfresco/repo/forms/processor/node/TransientEncodingFieldProcessor.java new file mode 100644 index 0000000000..27e9bd541f --- /dev/null +++ b/source/java/org/alfresco/repo/forms/processor/node/TransientEncodingFieldProcessor.java @@ -0,0 +1,61 @@ +/* + * 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 . + */ + +package org.alfresco.repo.forms.processor.node; + +import static org.alfresco.repo.forms.processor.node.FormFieldConstants.PROP_DATA_PREFIX; +import static org.alfresco.repo.forms.processor.node.FormFieldConstants.TRANSIENT_ENCODING; + +import org.alfresco.repo.forms.PropertyFieldDefinition; +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; + +public class TransientEncodingFieldProcessor extends TransientFieldProcessor +{ + private static final Log logger = LogFactory.getLog(TransientEncodingFieldProcessor.class); + + private static final String MSG_ENCODING_LABEL = "form_service.encoding.label"; + private static final String MSG_ENCODING_DESC = "form_service.encoding.description"; + + @Override + protected Log getLogger() + { + return logger; + } + + @Override + protected PropertyFieldDefinition makeTransientPropertyDefinition() + { + String dataKeyName = PROP_DATA_PREFIX + TRANSIENT_ENCODING; + PropertyFieldDefinition encodingField = new PropertyFieldDefinition(TRANSIENT_ENCODING, + DataTypeDefinition.TEXT.getLocalName()); + encodingField.setLabel(I18NUtil.getMessage(MSG_ENCODING_LABEL)); + encodingField.setDescription(I18NUtil.getMessage(MSG_ENCODING_DESC)); + encodingField.setDataKeyName(dataKeyName); + return encodingField; + } + + @Override + protected String getRegistryKey() + { + return FormFieldConstants.TRANSIENT_ENCODING; + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/forms/processor/node/TransientFieldProcessor.java b/source/java/org/alfresco/repo/forms/processor/node/TransientFieldProcessor.java new file mode 100644 index 0000000000..37f8957b2e --- /dev/null +++ b/source/java/org/alfresco/repo/forms/processor/node/TransientFieldProcessor.java @@ -0,0 +1,63 @@ +/* + * 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 . + */ + +package org.alfresco.repo.forms.processor.node; + +import org.alfresco.repo.forms.Field; +import org.alfresco.repo.forms.PropertyFieldDefinition; +import org.alfresco.repo.forms.processor.AbstractFieldProcessor; +import org.alfresco.repo.forms.processor.FormCreationData; + +public abstract class TransientFieldProcessor extends AbstractFieldProcessor +{ + /* (non-Javadoc) + * @see org.alfresco.repo.forms.processor.AbstractFieldProcessor#generateTypedField(java.lang.String, org.alfresco.repo.forms.processor.node.FormCreationData, java.lang.Object) + */ + @Override + protected Field generateTypedField(String fieldName, FormCreationData formData, TransientValueGetter typedData) + { + PropertyFieldDefinition transientPropDef = makeTransientPropertyDefinition(); + Field fieldInfo = null; + Object value = getValue(fieldName, typedData); + if (transientPropDef != null) + { + fieldInfo = new ContentField(transientPropDef, value); + } + return fieldInfo; + } + + protected Object getValue(String fieldName, TransientValueGetter data) + { + return data.getTransientValue(fieldName); + } + + /* + * (non-Javadoc) + * + * @seeorg.alfresco.repo.forms.field.processor.AbstractFieldProcessor# + * getExpectedDataType() + */ + @Override + protected Class getExpectedDataType() + { + return TransientValueGetter.class; + } + + protected abstract PropertyFieldDefinition makeTransientPropertyDefinition(); +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/forms/processor/node/TransientMimetypeFieldProcessor.java b/source/java/org/alfresco/repo/forms/processor/node/TransientMimetypeFieldProcessor.java new file mode 100644 index 0000000000..48bc291602 --- /dev/null +++ b/source/java/org/alfresco/repo/forms/processor/node/TransientMimetypeFieldProcessor.java @@ -0,0 +1,58 @@ +/* + * 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 . + */ + +package org.alfresco.repo.forms.processor.node; + +import static org.alfresco.repo.forms.processor.node.FormFieldConstants.PROP_DATA_PREFIX; +import static org.alfresco.repo.forms.processor.node.FormFieldConstants.TRANSIENT_MIMETYPE; + +import org.alfresco.repo.forms.PropertyFieldDefinition; +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; + +public class TransientMimetypeFieldProcessor extends TransientFieldProcessor +{ + private static final Log logger = LogFactory.getLog(TransientMimetypeFieldProcessor.class); + private static final String MSG_MIMETYPE_LABEL = "form_service.mimetype.label"; + private static final String MSG_MIMETYPE_DESC = "form_service.mimetype.description"; + + @Override + protected Log getLogger() { + return logger; + } + + @Override + protected PropertyFieldDefinition makeTransientPropertyDefinition() { + String dataKeyName = PROP_DATA_PREFIX + TRANSIENT_MIMETYPE; + PropertyFieldDefinition mimetypeField = new PropertyFieldDefinition(TRANSIENT_MIMETYPE, DataTypeDefinition.TEXT + .getLocalName()); + mimetypeField.setLabel(I18NUtil.getMessage(MSG_MIMETYPE_LABEL)); + mimetypeField.setDescription(I18NUtil.getMessage(MSG_MIMETYPE_DESC)); + mimetypeField.setDataKeyName(dataKeyName); + return mimetypeField; + } + + @Override + protected String getRegistryKey() + { + return FormFieldConstants.TRANSIENT_MIMETYPE; + } +} diff --git a/source/java/org/alfresco/repo/forms/processor/node/TransientSizeFieldProcessor.java b/source/java/org/alfresco/repo/forms/processor/node/TransientSizeFieldProcessor.java new file mode 100644 index 0000000000..97e95167e0 --- /dev/null +++ b/source/java/org/alfresco/repo/forms/processor/node/TransientSizeFieldProcessor.java @@ -0,0 +1,61 @@ +/* + * 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 . + */ + +package org.alfresco.repo.forms.processor.node; + +import static org.alfresco.repo.forms.processor.node.FormFieldConstants.PROP_DATA_PREFIX; +import static org.alfresco.repo.forms.processor.node.FormFieldConstants.TRANSIENT_SIZE; + +import org.alfresco.repo.forms.PropertyFieldDefinition; +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; + +public class TransientSizeFieldProcessor extends TransientFieldProcessor +{ + private static final Log logger = LogFactory.getLog(TransientSizeFieldProcessor.class); + private static final String MSG_SIZE_LABEL = "form_service.size.label"; + private static final String MSG_SIZE_DESC = "form_service.size.description"; + + @Override + protected Log getLogger() + { + return logger; + } + + @Override + protected PropertyFieldDefinition makeTransientPropertyDefinition() + { + String dataKeyName = PROP_DATA_PREFIX + TRANSIENT_SIZE; + PropertyFieldDefinition sizeField = new PropertyFieldDefinition(TRANSIENT_SIZE, + DataTypeDefinition.LONG.getLocalName()); + sizeField.setLabel(I18NUtil.getMessage(MSG_SIZE_LABEL)); + sizeField.setDescription(I18NUtil.getMessage(MSG_SIZE_DESC)); + sizeField.setDataKeyName(dataKeyName); + sizeField.setProtectedField(true); + return sizeField; + } + + @Override + protected String getRegistryKey() + { + return FormFieldConstants.TRANSIENT_SIZE; + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/forms/processor/node/TransientValueGetter.java b/source/java/org/alfresco/repo/forms/processor/node/TransientValueGetter.java new file mode 100644 index 0000000000..f53d5887dc --- /dev/null +++ b/source/java/org/alfresco/repo/forms/processor/node/TransientValueGetter.java @@ -0,0 +1,30 @@ +/* + * 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 . + */ + +package org.alfresco.repo.forms.processor.node; + +/** + * @author Nick Smith + * + */ +public interface TransientValueGetter +{ + + Object getTransientValue(String name); +} diff --git a/source/java/org/alfresco/repo/forms/processor/node/TypeFormProcessor.java b/source/java/org/alfresco/repo/forms/processor/node/TypeFormProcessor.java index 16153634e4..80798eb8c9 100644 --- a/source/java/org/alfresco/repo/forms/processor/node/TypeFormProcessor.java +++ b/source/java/org/alfresco/repo/forms/processor/node/TypeFormProcessor.java @@ -21,20 +21,15 @@ package org.alfresco.repo.forms.processor.node; import java.io.Serializable; import java.util.HashMap; -import java.util.List; import java.util.Map; import org.alfresco.model.ContentModel; -import org.alfresco.repo.forms.Form; import org.alfresco.repo.forms.FormData; import org.alfresco.repo.forms.FormException; import org.alfresco.repo.forms.FormNotFoundException; import org.alfresco.repo.forms.Item; import org.alfresco.repo.forms.FormData.FieldData; import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.service.cmr.dictionary.AspectDefinition; -import org.alfresco.service.cmr.dictionary.AssociationDefinition; -import org.alfresco.service.cmr.dictionary.PropertyDefinition; import org.alfresco.service.cmr.dictionary.TypeDefinition; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.InvalidQNameException; @@ -113,8 +108,11 @@ public class TypeFormProcessor extends ContentModelFormProcessor fields, - List forcedFields, Form form, Map context) - { - if (logger.isDebugEnabled()) logger.debug("Generating form for item: " + item); - - // generate the form for the node - generateType(item, fields, forcedFields, form); - if (logger.isDebugEnabled()) logger.debug("Generating form: " + form); - } - - /** - * Sets up the Form object for the given NodeRef - * - * @param nodeRef The NodeRef to generate a Form for - * @param fields Restricted list of fields to include - * @param forcedFields List of fields to forcibly include - * @param form The Form instance to populate - */ - protected void generateType(TypeDefinition typeDef, List fields, - List forcedFields, Form form) - { - // set the type and URL of the item - form.getItem().setType(typeDef.getName().toPrefixString(this.namespaceService)); - form.getItem().setUrl( - "/api/classes/" - + typeDef.getName().toPrefixString(this.namespaceService).replace( - ":", "_")); - - if (fields != null && fields.size() > 0) - { - generateSelectedFields(null, typeDef, fields, forcedFields, form); - } - else - { - // setup field definitions and data - generateAllPropertyFields(typeDef, form); - generateAllAssociationFields(typeDef, form); - - // TODO: generate transient properties for content types? - } - } - - /** - * Sets up the field definitions for all the type's properties. - * - * @param typeDef The type being setup - * @param form The Form instance to populate - */ - protected void generateAllPropertyFields(TypeDefinition typeDef, Form form) - { - // iterate round the property defintions and setup field definition - Map propDefs = typeDef.getProperties(); - for (PropertyDefinition propDef : propDefs.values()) - { - generatePropertyField(propDef, form, this.namespaceService); - } - - // get all default aspects for the type and iterate round their - // property definitions too - List aspects = typeDef.getDefaultAspects(true); - for (AspectDefinition aspect : aspects) - { - propDefs = aspect.getProperties(); - for (PropertyDefinition propDef : propDefs.values()) - { - generatePropertyField(propDef, form, this.namespaceService); - } - } - } - - /** - * Sets up the field definitions for all the type's associations. - * - * @param typeDef The type being setup - * @param form The Form instance to populate - */ - protected void generateAllAssociationFields(TypeDefinition typeDef, Form form) - { - // iterate round the association defintions and setup field definition - Map assocDefs = typeDef.getAssociations(); - for (AssociationDefinition assocDef : assocDefs.values()) - { - generateAssociationField(assocDef, form, this.namespaceService); - } - - // get all default aspects for the type and iterate round their - // association definitions too - List aspects = typeDef.getDefaultAspects(true); - for (AspectDefinition aspect : aspects) - { - assocDefs = aspect.getAssociations(); - for (AssociationDefinition assocDef : assocDefs.values()) - { - generateAssociationField(assocDef, form, this.namespaceService); - } - } - } - /* * @see * org.alfresco.repo.forms.processor.node.NodeFormProcessor#internalPersist @@ -238,30 +131,29 @@ public class TypeFormProcessor extends ContentModelFormProcessor() - { - public Object doWork() throws Exception - { - persistNode(nodeRef, data); - return null; - } - - }, - AuthenticationUtil.getSystemUserName()); + // persist the form data as the admin user + AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + public Object doWork() throws Exception + { + persistNode(nodeRef, data); + return null; + } + }, + AuthenticationUtil.getSystemUserName()); } else { - // persist the form data - persistNode(nodeRef, data); + // persist the form data + persistNode(nodeRef, data); } // return the newly created node @@ -293,9 +185,12 @@ public class TypeFormProcessor extends ContentModelFormProcessor getAssociationValues(TypeDefinition item) + { + return null; + } + + @Override + protected Map getPropertyValues(TypeDefinition item) + { + return null; + } + + @Override + protected Map getTransientValues(TypeDefinition item) + { + return null; + } } diff --git a/source/java/org/alfresco/repo/forms/processor/workflow/WorkflowFormProcessor.java b/source/java/org/alfresco/repo/forms/processor/workflow/WorkflowFormProcessor.java index fdde815119..a39f8e87f1 100644 --- a/source/java/org/alfresco/repo/forms/processor/workflow/WorkflowFormProcessor.java +++ b/source/java/org/alfresco/repo/forms/processor/workflow/WorkflowFormProcessor.java @@ -20,25 +20,38 @@ package org.alfresco.repo.forms.processor.workflow; import java.io.Serializable; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import org.alfresco.model.ContentModel; import org.alfresco.repo.forms.AssociationFieldDefinition; +import org.alfresco.repo.forms.FieldGroup; import org.alfresco.repo.forms.Form; import org.alfresco.repo.forms.FormData; import org.alfresco.repo.forms.FormNotFoundException; import org.alfresco.repo.forms.Item; +import org.alfresco.repo.forms.PropertyFieldDefinition; import org.alfresco.repo.forms.AssociationFieldDefinition.Direction; +import org.alfresco.repo.forms.PropertyFieldDefinition.FieldConstraint; import org.alfresco.repo.forms.processor.node.ContentModelFormProcessor; +import org.alfresco.repo.forms.processor.node.PeriodDataTypeParameters; import org.alfresco.repo.workflow.WorkflowModel; import org.alfresco.service.cmr.dictionary.AspectDefinition; import org.alfresco.service.cmr.dictionary.AssociationDefinition; +import org.alfresco.service.cmr.dictionary.Constraint; +import org.alfresco.service.cmr.dictionary.ConstraintDefinition; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.dictionary.PropertyDefinition; import org.alfresco.service.cmr.dictionary.TypeDefinition; +import org.alfresco.service.cmr.repository.AssociationRef; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.Period; import org.alfresco.service.cmr.workflow.WorkflowDefinition; import org.alfresco.service.cmr.workflow.WorkflowException; import org.alfresco.service.cmr.workflow.WorkflowInstance; @@ -326,4 +339,597 @@ public class WorkflowFormProcessor extends ContentModelFormProcessor + * NOTE: This method is static so that it can serve as a helper method for + * FormFilter implementations as adding additional property fields is likely + * to be a common extension. + *

+ * + * @param propDef The PropertyDefinition of the field to generate + * @param form The Form instance to populate + * @param namespaceService NamespaceService instance + */ + protected void generatePropertyField(PropertyDefinition propDef, Form form, NamespaceService namespaceService) + { + generatePropertyField(propDef, form, null, null, namespaceService); + } + + /** + * Sets up a field definition for the given property. + *

+ * NOTE: This method is static so that it can serve as a helper method for + * FormFilter implementations as adding additional property fields is likely + * to be a common extension. + *

+ * + * @param propDef The PropertyDefinition of the field to generate + * @param form The Form instance to populate + * @param propValue The value of the property field + * @param namespaceService NamespaceService instance + */ + protected void generatePropertyField(PropertyDefinition propDef, Form form, Serializable propValue, + NamespaceService namespaceService) + { + generatePropertyField(propDef, form, propValue, null, namespaceService); + } + + /** + * Sets up a field definition for the given property. + *

+ * NOTE: This method is static so that it can serve as a helper method for + * FormFilter implementations as adding additional property fields is likely + * to be a common extension. + *

+ * + * @param propDef The PropertyDefinition of the field to generate + * @param form The Form instance to populate + * @param propValue The value of the property field + * @param group The FieldGroup the property field belongs to, can be null + * @param namespaceService NamespaceService instance + */ + @SuppressWarnings("unchecked") + protected void generatePropertyField(PropertyDefinition propDef, Form form, Serializable propValue, + FieldGroup group, NamespaceService namespaceService) + { + String propName = propDef.getName().toPrefixString(namespaceService); + String[] nameParts = QName.splitPrefixedQName(propName); + PropertyFieldDefinition fieldDef = new PropertyFieldDefinition(propName, propDef.getDataType().getName() + .getLocalName()); + + String title = propDef.getTitle(); + if (title == null) + { + title = propName; + } + fieldDef.setLabel(title); + fieldDef.setDefaultValue(propDef.getDefaultValue()); + fieldDef.setDescription(propDef.getDescription()); + fieldDef.setMandatory(propDef.isMandatory()); + fieldDef.setProtectedField(propDef.isProtected()); + fieldDef.setRepeating(propDef.isMultiValued()); + fieldDef.setGroup(group); + + // any property from the system model (sys prefix) should be protected + // the model doesn't currently enforce this so make sure they are not + // editable + if (NamespaceService.SYSTEM_MODEL_1_0_URI.equals(propDef.getName().getNamespaceURI())) + { + fieldDef.setProtectedField(true); + } + + // define the data key name and set + String dataKeyName = PROP_DATA_PREFIX + nameParts[0] + DATA_KEY_SEPARATOR + nameParts[1]; + fieldDef.setDataKeyName(dataKeyName); + + // setup any parameters requried for the data type + if (propDef.getDataType().getName().equals(DataTypeDefinition.PERIOD)) + { + // if the property data type is d:period we need to setup a data + // type parameters object to represent the options and rules + PeriodDataTypeParameters periodOptions = new PeriodDataTypeParameters(); + Set providers = Period.getProviderNames(); + for (String provider : providers) + { + periodOptions.addPeriodProvider(Period.getProvider(provider)); + } + + fieldDef.setDataTypeParameters(periodOptions); + } + + // setup constraints for the property + List constraints = propDef.getConstraints(); + if (constraints != null && constraints.size() > 0) + { + List fieldConstraints = new ArrayList(constraints.size()); + + for (ConstraintDefinition constraintDef : constraints) + { + Constraint constraint = constraintDef.getConstraint(); + FieldConstraint fieldConstraint = new FieldConstraint(constraint.getType(), constraint.getParameters()); + fieldConstraints.add(fieldConstraint); + } + + fieldDef.setConstraints(fieldConstraints); + } + + form.addFieldDefinition(fieldDef); + + // add the property value to the form + if (propValue != null) + { + if (propValue instanceof List) + { + // temporarily add repeating field data as a comma + // separated list, this will be changed to using + // a separate field for each value once we have full + // UI support in place. + propValue = StringUtils.collectionToCommaDelimitedString((List) propValue); + } + else if (propValue instanceof ContentData) + { + // for content properties retrieve the info URL rather than the + // the object value itself + propValue = ((ContentData)propValue).getInfoUrl(); + } + + form.addData(dataKeyName, propValue); + } + } + + /** + * Sets up a field definition for the given association. + *

+ * NOTE: This method is static so that it can serve as a helper method for + * FormFilter implementations as adding additional association fields is + * likely to be a common extension. + *

+ * + * @param assocDef The AssociationDefinition of the field to generate + * @param form The Form instance to populate + * @param namespaceService NamespaceService instance + */ + protected void generateAssociationField(AssociationDefinition assocDef, Form form, + NamespaceService namespaceService) + { + generateAssociationField(assocDef, form, null, null, namespaceService); + } + + /** + * Sets up a field definition for the given association. + *

+ * NOTE: This method is static so that it can serve as a helper method for + * FormFilter implementations as adding additional association fields is + * likely to be a common extension. + *

+ * + * @param assocDef The AssociationDefinition of the field to generate + * @param form The Form instance to populate + * @param assocValues The values of the association field, can be null + * @param namespaceService NamespaceService instance + */ + @SuppressWarnings("unchecked") + protected void generateAssociationField(AssociationDefinition assocDef, Form form, List assocValues, + NamespaceService namespaceService) + { + generateAssociationField(assocDef, form, assocValues, null, namespaceService); + } + + /** + * Sets up a field definition for the given association. + *

+ * NOTE: This method is static so that it can serve as a helper method for + * FormFilter implementations as adding additional association fields is + * likely to be a common extension. + *

+ * + * @param assocDef The AssociationDefinition of the field to generate + * @param form The Form instance to populate + * @param assocValues The values of the association field, can be null + * @param group The FieldGroup the association field belongs to, can be null + * @param namespaceService NamespaceService instance + */ + @SuppressWarnings("unchecked") + protected static void generateAssociationField(AssociationDefinition assocDef, Form form, List assocValues, + FieldGroup group, NamespaceService namespaceService) + { + String assocName = assocDef.getName().toPrefixString(namespaceService); + String[] nameParts = QName.splitPrefixedQName(assocName); + AssociationFieldDefinition fieldDef = new AssociationFieldDefinition(assocName, assocDef.getTargetClass() + .getName().toPrefixString(namespaceService), Direction.TARGET); + String title = assocDef.getTitle(); + if (title == null) + { + title = assocName; + } + fieldDef.setLabel(title); + fieldDef.setDescription(assocDef.getDescription()); + fieldDef.setProtectedField(assocDef.isProtected()); + fieldDef.setEndpointMandatory(assocDef.isTargetMandatory()); + fieldDef.setEndpointMany(assocDef.isTargetMany()); + fieldDef.setGroup(group); + + // define the data key name and set + String dataKeyName = ASSOC_DATA_PREFIX + nameParts[0] + DATA_KEY_SEPARATOR + nameParts[1]; + fieldDef.setDataKeyName(dataKeyName); + + // add definition to the form + form.addFieldDefinition(fieldDef); + + if (assocValues != null) + { + // add the association value to the form + // determine the type of association values data and extract + // accordingly + List values = new ArrayList(4); + for (Object value : assocValues) + { + if (value instanceof ChildAssociationRef) + { + values.add(((ChildAssociationRef) value).getChildRef().toString()); + } + else if (value instanceof AssociationRef) + { + values.add(((AssociationRef) value).getTargetRef().toString()); + } + else + { + values.add(value.toString()); + } + } + + // Add the list as the value for the association. + form.addData(dataKeyName, values); + } + } + + /** + * Sets up the field definitions for all the requested fields. + *

+ * A NodeRef or TypeDefinition can be provided, however, if a NodeRef is + * provided all type information will be derived from the NodeRef and the + * TypeDefinition will be ignored. + *

+ *

+ * If any of the requested fields are not present on the type and they + * appear in the forcedFields list an attempt to find a model definition for + * those fields is made so they can be included. + *

+ * + * @param nodeRef The NodeRef of the item being generated + * @param typeDef The TypeDefiniton of the item being generated + * @param fields Restricted list of fields to include + * @param forcedFields List of field names that should be included even if + * the field is not currently present + * @param form The Form instance to populate + */ + protected void generateSelectedFields(NodeRef nodeRef, TypeDefinition typeDef, List fields, + List forcedFields, Form form) + { + // ensure a NodeRef or TypeDefinition is provided + if (nodeRef == null && typeDef == null) { throw new IllegalArgumentException( + "A NodeRef or TypeDefinition must be provided"); } + + if (getLogger().isDebugEnabled()) + getLogger().debug("Generating selected fields: " + fields + " and forcing: " + forcedFields); + + // get data dictionary definition for node if it is provided + QName type = null; + Map propValues = Collections.emptyMap(); + Map propDefs = null; + Map assocDefs = null; + + if (nodeRef != null) + { + type = this.nodeService.getType(nodeRef); + typeDef = this.dictionaryService.getAnonymousType(type, this.nodeService.getAspects(nodeRef)); + + // NOTE: the anonymous type returns all property and association + // defs + // for all aspects applied as well as the type + propDefs = typeDef.getProperties(); + assocDefs = typeDef.getAssociations(); + propValues = this.nodeService.getProperties(nodeRef); + } + else + { + type = typeDef.getName(); + + // we only get the properties and associations of the actual type so + // we also need to manually get properties and associations from any + // mandatory aspects + propDefs = new HashMap(16); + assocDefs = new HashMap(16); + propDefs.putAll(typeDef.getProperties()); + assocDefs.putAll(typeDef.getAssociations()); + + List aspects = typeDef.getDefaultAspects(true); + for (AspectDefinition aspect : aspects) + { + propDefs.putAll(aspect.getProperties()); + assocDefs.putAll(aspect.getAssociations()); + } + } + + for (String fieldName : fields) + { + // try and split the field name + String[] parts = fieldName.split(":"); + if (parts.length == 2 || parts.length == 3) + { + boolean foundField = false; + boolean tryProperty = true; + boolean tryAssociation = true; + String qNamePrefix = null; + String localName = null; + + if (parts.length == 2) + { + qNamePrefix = parts[0]; + localName = parts[1]; + } + else + { + // if there are 3 parts to the field name the first one + // represents + // whether the field is a property or association i.e. + // prop:prefix:local + // or assoc:prefix:local, determine the prefix and ensure + // it's valid + if (PROP.equals(parts[0])) + { + tryAssociation = false; + } + else if (ASSOC.equals(parts[0])) + { + tryProperty = false; + } + else + { + if (getLogger().isWarnEnabled()) + getLogger() + .warn( + "\"" + + parts[0] + + "\" is an invalid prefix for requesting a property or association"); + + continue; + } + + qNamePrefix = parts[1]; + localName = parts[2]; + } + + // create qname of field name + QName fullQName = QName.createQName(qNamePrefix, localName, namespaceService); + + // try the field as a property + if (tryProperty) + { + // lookup property def on node + PropertyDefinition propDef = propDefs.get(fullQName); + if (propDef != null) + { + // generate the property field + generatePropertyField(propDef, form, propValues.get(fullQName), this.namespaceService); + + // no need to try and find an association + tryAssociation = false; + foundField = true; + } + } + + // try the field as an association + if (tryAssociation) + { + AssociationDefinition assocDef = assocDefs.get(fullQName); + if (assocDef != null) + { + // generate the association field + generateAssociationField(assocDef, form, (nodeRef != null) ? retrieveAssociationValues(nodeRef, + assocDef) : null, this.namespaceService); + + foundField = true; + } + } + + // still not found the field, is it a force'd field? + if (!foundField) + { + if (forcedFields != null && forcedFields.size() > 0 && forcedFields.contains(fieldName)) + { + generateForcedField(fieldName, form); + } + else if (getLogger().isDebugEnabled()) + { + getLogger().debug( + "Ignoring field \"" + fieldName + "\" as it is not defined for the current " + + ((nodeRef != null) ? "node" : "type") + + " and it does not appear in the 'force' list"); + } + } + } + else + { + // see if the fieldName is a well known transient property + if (TRANSIENT_MIMETYPE.equals(fieldName) || TRANSIENT_ENCODING.equals(fieldName) + || TRANSIENT_SIZE.equals(fieldName)) + { + // if the node type is content or sublcass thereof generate appropriate field + if (this.dictionaryService.isSubClass(type, ContentModel.TYPE_CONTENT)) + { + ContentData content = null; + + if (nodeRef != null) + { + content = (ContentData) this.nodeService.getProperty(nodeRef, ContentModel.PROP_CONTENT); + } + + if (TRANSIENT_MIMETYPE.equals(fieldName)) + { + generateMimetypePropertyField(content, form); + } + else if (TRANSIENT_ENCODING.equals(fieldName)) + { + generateEncodingPropertyField(content, form); + } + else if (TRANSIENT_SIZE.equals(fieldName)) + { + generateSizePropertyField(content, form); + } + } + } + else if (getLogger().isWarnEnabled()) + { + getLogger().warn("Ignoring unrecognised field \"" + fieldName + "\""); + } + } + } + } + + /** + * Generates a field definition for the given field that is being forced to + * show. + * + * @param fieldName Name of the field to force + * @param form The Form instance to populated + */ + protected void generateForcedField(String fieldName, Form form) + { + if (getLogger().isDebugEnabled()) + getLogger().debug("Attempting to force the inclusion of field \"" + fieldName + "\""); + + String[] parts = fieldName.split(":"); + if (parts.length == 2 || parts.length == 3) + { + boolean foundField = false; + boolean tryProperty = true; + boolean tryAssociation = true; + String qNamePrefix = null; + String localName = null; + + if (parts.length == 2) + { + qNamePrefix = parts[0]; + localName = parts[1]; + } + else + { + // if there are 3 parts to the field name the first one + // represents + // whether the field is a property or association i.e. + // prop:prefix:local + // or assoc:prefix:local, determine the prefix and ensure it's + // valid + if (PROP.equals(parts[0])) + { + tryAssociation = false; + } + else if (ASSOC.equals(parts[0])) + { + tryProperty = false; + } + else + { + if (getLogger().isWarnEnabled()) + getLogger().warn( + "\"" + parts[0] + + "\" is an invalid prefix for requesting a property or association"); + + return; + } + + qNamePrefix = parts[1]; + localName = parts[2]; + } + + // create qname of field name + QName fullQName = QName.createQName(qNamePrefix, localName, namespaceService); + + if (tryProperty) + { + // lookup the field as a property in the whole model + PropertyDefinition propDef = this.dictionaryService.getProperty(fullQName); + if (propDef != null) + { + // generate the property field + generatePropertyField(propDef, form, this.namespaceService); + + // no need to try and find an association + tryAssociation = false; + foundField = true; + } + } + + if (tryAssociation) + { + // lookup the field as an association in the whole model + AssociationDefinition assocDef = this.dictionaryService.getAssociation(fullQName); + if (assocDef != null) + { + // generate the association field + generateAssociationField(assocDef, form, this.namespaceService); + + foundField = true; + } + } + + if (!foundField && getLogger().isDebugEnabled()) + { + getLogger() + .debug( + "Ignoring field \"" + + fieldName + + "\" as it is not defined for the current node and can not be found in any model"); + } + } + else if (getLogger().isWarnEnabled()) + { + getLogger().warn("Ignoring unrecognised field \"" + fieldName + "\""); + } + } + + /* (non-Javadoc) + * @see org.alfresco.repo.forms.processor.FilteredFormProcessor#getItemType(java.lang.Object) + */ + @Override + protected String getItemType(WorkflowDefinition item) + { + return item.name; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.forms.processor.FilteredFormProcessor#getItemURI(java.lang.Object) + */ + @Override + protected String getItemURI(WorkflowDefinition item) + { + return "/api/workflow-definitions/" + item.id; + } + + @Override + protected Map getAssociationValues(WorkflowDefinition item) + { + return null; + } + + @Override + protected Map getPropertyValues(WorkflowDefinition item) + { + return null; + } + + @Override + protected Map getTransientValues(WorkflowDefinition item) + { + return null; + } + + @Override + protected TypeDefinition getBaseType(WorkflowDefinition item) + { + return item.getStartTaskDefinition().metadata; + } }