From 65a61fa3066852c71a2580b295bc9a7b37ec87ee Mon Sep 17 00:00:00 2001 From: Gavin Cornwell Date: Tue, 9 Jun 2009 15:00:51 +0000 Subject: [PATCH] First cut of MOB-837 (type support in forms) git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@14606 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- config/alfresco/form-services-context.xml | 13 ++ .../repo/forms/FormServiceImplTest.java | 51 ++++- .../processor/node/NodeFormProcessor.java | 55 +++-- .../processor/node/TypeFormProcessor.java | 196 ++++++++++++++++++ .../repo/forms/script/ScriptForm.java | 9 +- .../repo/forms/script/ScriptFormData.java | 9 +- 6 files changed, 313 insertions(+), 20 deletions(-) create mode 100644 source/java/org/alfresco/repo/forms/processor/node/TypeFormProcessor.java diff --git a/config/alfresco/form-services-context.xml b/config/alfresco/form-services-context.xml index 92a524d64e..588d9efdc0 100644 --- a/config/alfresco/form-services-context.xml +++ b/config/alfresco/form-services-context.xml @@ -78,6 +78,19 @@ + + + + + + + + type + + + diff --git a/source/java/org/alfresco/repo/forms/FormServiceImplTest.java b/source/java/org/alfresco/repo/forms/FormServiceImplTest.java index caa42fe3ff..e90f703d80 100644 --- a/source/java/org/alfresco/repo/forms/FormServiceImplTest.java +++ b/source/java/org/alfresco/repo/forms/FormServiceImplTest.java @@ -39,7 +39,6 @@ import org.alfresco.repo.forms.AssociationFieldDefinition.Direction; import org.alfresco.repo.forms.PropertyFieldDefinition.FieldConstraint; import org.alfresco.repo.jscript.ClasspathScriptLocation; import org.alfresco.repo.security.authentication.AuthenticationComponent; -import org.alfresco.service.cmr.repository.AssociationRef; import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.ContentWriter; @@ -108,6 +107,7 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest private static final String USER_ONE = "UserOne_FormServiceImplTest"; private static final String NODE_FORM_ITEM_KIND = "node"; + private static final String TYPE_FORM_ITEM_KIND = "type"; /** * Called during the transaction setup @@ -804,6 +804,55 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest */ } + // TODO: enable this once the RM caveat stuff is fixed + public void xtestGetAllCreateForm() throws Exception + { + // get a form for the cm:content type + Form form = this.formService.getForm(new Item(TYPE_FORM_ITEM_KIND, "cm:content")); + + // check a form got returned + assertNotNull("Expecting form to be present", form); + + // check item identifier matches + assertEquals(TYPE_FORM_ITEM_KIND, form.getItem().getKind()); + assertEquals("cm:content", form.getItem().getId()); + + // check the type is correct + assertEquals(ContentModel.TYPE_CONTENT.toPrefixString(this.namespaceService), + form.getItem().getType()); + + // check there is no group info + assertNull("Expecting the form groups to be null!", form.getFieldGroups()); + + // check the field definitions + Collection fieldDefs = form.getFieldDefinitions(); + assertNotNull("Expecting to find fields", fieldDefs); + assertEquals("Expecting to find 11 fields", 11, fieldDefs.size()); + + // create a Map of the field definitions + // NOTE: we can safely do this as we know there are no duplicate field names and we're not + // concerned with ordering! + Map fieldDefMap = new HashMap(fieldDefs.size()); + for (FieldDefinition fieldDef : fieldDefs) + { + fieldDefMap.put(fieldDef.getName(), fieldDef); + } + + // find the fields + PropertyFieldDefinition nameField = (PropertyFieldDefinition)fieldDefMap.get("cm:name"); + PropertyFieldDefinition createdField = (PropertyFieldDefinition)fieldDefMap.get("cm:created"); + PropertyFieldDefinition creatorField = (PropertyFieldDefinition)fieldDefMap.get("cm:creator"); + PropertyFieldDefinition modifiedField = (PropertyFieldDefinition)fieldDefMap.get("cm:modified"); + PropertyFieldDefinition modifierField = (PropertyFieldDefinition)fieldDefMap.get("cm:modifier"); + + // check fields are present + assertNotNull("Expecting to find the cm:name field", nameField); + assertNotNull("Expecting to find the cm:created field", createdField); + assertNotNull("Expecting to find the cm:creator field", creatorField); + assertNotNull("Expecting to find the cm:modified field", modifiedField); + assertNotNull("Expecting to find the cm:modifier field", modifierField); + } + public void testNoForm() throws Exception { // test that a form can not be retrieved for a non-existent item 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 8085b3221f..fda1877605 100644 --- a/source/java/org/alfresco/repo/forms/processor/node/NodeFormProcessor.java +++ b/source/java/org/alfresco/repo/forms/processor/node/NodeFormProcessor.java @@ -26,6 +26,7 @@ package org.alfresco.repo.forms.processor.node; import java.io.Serializable; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -260,7 +261,7 @@ public class NodeFormProcessor extends FilteredFormProcessor if (fields != null && fields.size() > 0) { - generateSelectedFields(nodeRef, fields, forcedFields, form); + generateSelectedFields(nodeRef, null, fields, forcedFields, form); } else { @@ -273,28 +274,52 @@ public class NodeFormProcessor extends FilteredFormProcessor /** * Sets up the field definitions for all the requested fields. - * If any of the requested fields are not present on the node and they - * appear in the forcedFields list an attempt to find a model + *

+ * 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 node being setup + * @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, List fields, List forcedFields, Form form) + 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 (logger.isDebugEnabled()) logger.debug("Generating selected fields: " + fields + " and forcing: " + forcedFields); - // get data dictionary definition for node - QName type = this.nodeService.getType(nodeRef); - TypeDefinition typeDef = this.dictionaryService.getAnonymousType(type, - this.nodeService.getAspects(nodeRef)); + // get data dictionary definition for node if it is provided + QName type = null; + Map propValues = Collections.emptyMap(); + + if (nodeRef != null) + { + type = this.nodeService.getType(nodeRef); + typeDef = this.dictionaryService.getAnonymousType(type, this.nodeService.getAspects(nodeRef)); + propValues = this.nodeService.getProperties(nodeRef); + } + else + { + type = typeDef.getName(); + } + Map propDefs = typeDef.getProperties(); - Map assocDefs = typeDef.getAssociations(); - Map propValues = this.nodeService.getProperties(nodeRef); + Map assocDefs = typeDef.getAssociations(); for (String fieldName : fields) { @@ -365,7 +390,8 @@ public class NodeFormProcessor extends FilteredFormProcessor { // generate the association field generateAssociationField(assocDef, - retrieveAssociationValues(nodeRef, assocDef), form); + (nodeRef != null) ? retrieveAssociationValues(nodeRef, assocDef) : null, + form); foundField = true; } @@ -382,7 +408,8 @@ public class NodeFormProcessor extends FilteredFormProcessor else if (logger.isDebugEnabled()) { logger.debug("Ignoring field \"" + fieldName + - "\" as it is not defined for the current node and it does not appear in the 'force' list"); + "\" as it is not defined for the current " + ((nodeRef != null) ? "node" : "type") + + " and it does not appear in the 'force' list"); } } } @@ -393,7 +420,7 @@ public class NodeFormProcessor extends FilteredFormProcessor TRANSIENT_SIZE.equals(fieldName)) { // if the node type is content or sublcass thereof generate appropriate field - if (this.dictionaryService.isSubClass(type, ContentModel.TYPE_CONTENT)) + if (nodeRef != null && this.dictionaryService.isSubClass(type, ContentModel.TYPE_CONTENT)) { ContentData content = (ContentData)this.nodeService.getProperty(nodeRef, ContentModel.PROP_CONTENT); if (content != null) diff --git a/source/java/org/alfresco/repo/forms/processor/node/TypeFormProcessor.java b/source/java/org/alfresco/repo/forms/processor/node/TypeFormProcessor.java new file mode 100644 index 0000000000..f47b770775 --- /dev/null +++ b/source/java/org/alfresco/repo/forms/processor/node/TypeFormProcessor.java @@ -0,0 +1,196 @@ +/* + * 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.util.List; +import java.util.Map; + +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.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.namespace.InvalidQNameException; +import org.alfresco.service.namespace.QName; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * FormProcessor implementation that can generate and persist Form objects + * for types in the Alfresco content model. + * + * @author Gavin Cornwell + */ +public class TypeFormProcessor extends NodeFormProcessor +{ + /** Logger */ + private static Log logger = LogFactory.getLog(TypeFormProcessor.class); + + @Override + protected Object getTypedItem(Item item) + { + TypeDefinition typeDef = null; + + try + { + // convert the prefix type into full QName representation + // TODO: Also look for and deal with full QName as itemId + QName type = QName.createQName(item.getId(), this.namespaceService); + + // retrieve the type from the dictionary + typeDef = this.dictionaryService.getType(type); + + if (typeDef == null) + { + throw new FormNotFoundException(item, + new IllegalArgumentException("Type does not exist: " + item.getId())); + } + } + catch (InvalidQNameException iqne) + { + throw new FormNotFoundException(item, iqne); + } + + // return the type definition object for the requested type + return typeDef; + } + + @Override + protected void internalGenerate(Object item, List fields, List forcedFields, Form form) + { + if (logger.isDebugEnabled()) + logger.debug("Generating form for item: " + item); + + // cast to the expected NodeRef representation + TypeDefinition typeDef = (TypeDefinition)item; + + // generate the form for the node + generateType(typeDef, 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); + } + } + + /** + * 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, null, form); + } + + // 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, null, form); + } + } + } + + /** + * 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()) + { + this.generateAssociationField(assocDef, null, form); + } + + // 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()) + { + this.generateAssociationField(assocDef, null, form); + } + } + } + + @Override + protected Object internalPersist(Object item, FormData data) + { + if (logger.isDebugEnabled()) + logger.debug("Persisting form for: " + item); + + // cast to the expected NodeRef representation + TypeDefinition typeDef = (TypeDefinition)item; + + if (logger.isWarnEnabled()) + logger.warn("Persisting of 'type' form items has not been implemented yet!"); + + return item; + } + +} diff --git a/source/java/org/alfresco/repo/forms/script/ScriptForm.java b/source/java/org/alfresco/repo/forms/script/ScriptForm.java index 6093be3646..c91bb2a01d 100644 --- a/source/java/org/alfresco/repo/forms/script/ScriptForm.java +++ b/source/java/org/alfresco/repo/forms/script/ScriptForm.java @@ -51,8 +51,13 @@ public class ScriptForm implements Serializable this.form = formObject; fieldDefinitionData = new HashMap(); - for (FieldDefinition fd : form.getFieldDefinitions()) { - fieldDefinitionData.put(fd.getName(), fd); + List fieldDefs = form.getFieldDefinitions(); + if (fieldDefs != null) + { + for (FieldDefinition fd : fieldDefs) + { + fieldDefinitionData.put(fd.getName(), fd); + } } } diff --git a/source/java/org/alfresco/repo/forms/script/ScriptFormData.java b/source/java/org/alfresco/repo/forms/script/ScriptFormData.java index cb0e87eaba..2665d04e65 100644 --- a/source/java/org/alfresco/repo/forms/script/ScriptFormData.java +++ b/source/java/org/alfresco/repo/forms/script/ScriptFormData.java @@ -51,10 +51,13 @@ public class ScriptFormData implements Serializable public ScriptableHashMap getData() { ScriptableHashMap result = new ScriptableHashMap(); - for (String k : formData.getData().keySet()) + if (this.formData != null) { - ScriptFieldData wrappedFieldData = new ScriptFieldData(formData.getData().get(k)); - result.put(k, wrappedFieldData); + for (String k : formData.getData().keySet()) + { + ScriptFieldData wrappedFieldData = new ScriptFieldData(formData.getData().get(k)); + result.put(k, wrappedFieldData); + } } return result; }