From 4cd5e901108848be789a1f45eeff1accefedaee4 Mon Sep 17 00:00:00 2001 From: Gavin Cornwell Date: Tue, 13 Jul 2010 15:03:19 +0000 Subject: [PATCH] Merged BRANCHES/DEV/FORM-REFACTOR-MERGE to HEAD: 20935: Branch for form processor refactor merge 21075: Utility class to help with writing tests which require creation/management of a person. 21076: Forms Processor Refactor. Refactored the FilteredFormProcessor and ContentModelFormProcessor classes to be more concise. Also introduced the notion of a Field, FieldProcessors and the FieldProcessorRegistry. These are currently responsible for generating different types of fields such as Association fields, Property fields and the various transient fields. 21083: Moved some utility classes into the correct packages for Forms. 21084: Created 21087: Created TaskFormProcessor 21101: Added test for fdk:period property, in particular the retrieval of it's data type parameters 21109: Fixed some issues witht he field processor logging. Also fixed an issue where data type properties were not being set correctly for Period property fields. 21121: Changed AssociationFieldProcessor so that it now adds an empty list as a value instead of null. 21124: Formatting updates and disabled FDK test ready for merge to HEAD This commit contains all the re-factoring Nick has been doing to make the form processor mechansim more flexible for form filter developers and more importantly to make it easier to add additional form processor implementations, in particular the TaskFormProcessor and WorkflowFormProcessor. A field processor mechanism has also been introduced, this allows the processing of fields to be handled outside of the form processor and means common field handling code can be reused across different form processors. The field processors are also pluggable via Spring config so it's easy for customers to add their own or replace/override ours. We have run numerous tests (unit and manual UI) and all seems well but as with any change of this size there maybe some edge cases that get missed so please let one of us know if you see any strange behaviour. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@21134 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- config/alfresco/form-services-context.xml | 50 +- .../java/org/alfresco/repo/forms/Field.java | 43 ++ source/java/org/alfresco/repo/forms/Form.java | 35 + .../repo/forms/FormServiceImplTest.java | 24 +- .../processor/AbstractFieldProcessor.java | 101 +++ .../repo/forms/processor/FieldProcessor.java | 27 + .../processor/FieldProcessorRegistry.java | 111 ++++ .../processor/FilteredFormProcessor.java | 115 +++- .../forms/processor/FormCreationData.java | 77 +++ .../node/AssociationFieldProcessor.java | 125 ++++ .../forms/processor/node/ContentField.java | 124 ++++ .../ContentModelFieldProcessorRegistry.java | 58 ++ .../node/ContentModelFormProcessor.java | 583 +---------------- .../processor/node/DefaultFieldBuilder.java | 150 +++++ .../processor/node/DefaultFieldProcessor.java | 119 ++++ .../processor/node/FieldProcessorTest.java | 189 ++++++ .../repo/forms/processor/node/FieldUtils.java | 131 ++++ .../processor/node/FormFieldConstants.java | 5 + .../repo/forms/processor/node/ItemData.java | 194 ++++++ .../node/MockClassAttributeDefinition.java | 372 +++++++++++ .../node/MockFieldProcessorRegistry.java | 75 +++ .../processor/node/NodeFormProcessor.java | 220 +++---- .../node/PropertyFieldProcessor.java | 193 ++++++ .../processor/node/QNameFieldProcessor.java | 163 +++++ .../node/TransientEncodingFieldProcessor.java | 61 ++ .../node/TransientFieldProcessor.java | 63 ++ .../node/TransientMimetypeFieldProcessor.java | 58 ++ .../node/TransientSizeFieldProcessor.java | 61 ++ .../processor/node/TransientValueGetter.java | 30 + .../processor/node/TypeFormProcessor.java | 197 ++---- .../workflow/WorkflowFormProcessor.java | 606 ++++++++++++++++++ 31 files changed, 3550 insertions(+), 810 deletions(-) create mode 100644 source/java/org/alfresco/repo/forms/Field.java create mode 100644 source/java/org/alfresco/repo/forms/processor/AbstractFieldProcessor.java create mode 100644 source/java/org/alfresco/repo/forms/processor/FieldProcessor.java create mode 100644 source/java/org/alfresco/repo/forms/processor/FieldProcessorRegistry.java create mode 100644 source/java/org/alfresco/repo/forms/processor/FormCreationData.java create mode 100644 source/java/org/alfresco/repo/forms/processor/node/AssociationFieldProcessor.java create mode 100644 source/java/org/alfresco/repo/forms/processor/node/ContentField.java create mode 100644 source/java/org/alfresco/repo/forms/processor/node/ContentModelFieldProcessorRegistry.java create mode 100644 source/java/org/alfresco/repo/forms/processor/node/DefaultFieldBuilder.java create mode 100644 source/java/org/alfresco/repo/forms/processor/node/DefaultFieldProcessor.java create mode 100644 source/java/org/alfresco/repo/forms/processor/node/FieldProcessorTest.java create mode 100644 source/java/org/alfresco/repo/forms/processor/node/FieldUtils.java create mode 100644 source/java/org/alfresco/repo/forms/processor/node/ItemData.java create mode 100644 source/java/org/alfresco/repo/forms/processor/node/MockClassAttributeDefinition.java create mode 100644 source/java/org/alfresco/repo/forms/processor/node/MockFieldProcessorRegistry.java create mode 100644 source/java/org/alfresco/repo/forms/processor/node/PropertyFieldProcessor.java create mode 100644 source/java/org/alfresco/repo/forms/processor/node/QNameFieldProcessor.java create mode 100644 source/java/org/alfresco/repo/forms/processor/node/TransientEncodingFieldProcessor.java create mode 100644 source/java/org/alfresco/repo/forms/processor/node/TransientFieldProcessor.java create mode 100644 source/java/org/alfresco/repo/forms/processor/node/TransientMimetypeFieldProcessor.java create mode 100644 source/java/org/alfresco/repo/forms/processor/node/TransientSizeFieldProcessor.java create mode 100644 source/java/org/alfresco/repo/forms/processor/node/TransientValueGetter.java 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; + } }