Merged DEV/FORMS-REFACTOR branch to HEAD

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@14000 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Gavin Cornwell
2009-04-18 22:42:15 +00:00
parent 6568c04fa0
commit 5b0a1b589a
19 changed files with 1422 additions and 346 deletions

View File

@@ -71,7 +71,7 @@
<property name="handlerRegistry" ref="nodeHandlerRegistry" /> <property name="handlerRegistry" ref="nodeHandlerRegistry" />
<property name="nodeService" ref="NodeService" /> <property name="nodeService" ref="NodeService" />
<property name="matchPattern"> <property name="matchPattern">
<value>workspace://[\w\-\/]*</value> <value>node</value>
</property> </property>
</bean> </bean>
@@ -99,7 +99,6 @@
<property name="extensionName"> <property name="extensionName">
<value>formService</value> <value>formService</value>
</property> </property>
<property name="serviceRegistry" ref="ServiceRegistry"/>
<property name="formService" ref="FormService"/> <property name="formService" ref="FormService"/>
</bean> </bean>

View File

@@ -35,7 +35,7 @@ import java.util.List;
*/ */
public class Form public class Form
{ {
protected String item; protected Item item;
protected String type; protected String type;
protected List<FieldDefinition> fieldDefinitions; protected List<FieldDefinition> fieldDefinitions;
protected Collection<FieldGroup> fieldGroups; protected Collection<FieldGroup> fieldGroups;
@@ -44,20 +44,19 @@ public class Form
/** /**
* Constructs a Form * Constructs a Form
* *
* @param item An identifier for the item the form is for * @param item The item the form is for
*/ */
public Form(String item) public Form(Item item)
{ {
this.item = item; this.item = item;
} }
/** /**
* Returns an identifier for the item the form is for, in the case of a node * Returns the item the form is for
* it will be a NodeRef, for a task, a task id etc.
* *
* @return The item * @return The item
*/ */
public String getItem() public Item getItem()
{ {
return this.item; return this.item;
} }
@@ -167,7 +166,8 @@ public class Form
} }
/** /**
* Sets the data this form should display * Sets the data this form should display. This will overwrite
* any existing form data being held
* *
* @param data FormData instance containing the data * @param data FormData instance containing the data
*/ */
@@ -176,6 +176,22 @@ public class Form
this.data = data; this.data = data;
} }
/**
* Adds some data to be displayed by the form
*
* @param fieldName Name of the field the data is for
* @param fieldData The value
*/
public void addData(String fieldName, Object fieldData)
{
if (this.data == null)
{
this.data = new FormData();
}
this.data.addData(fieldName, fieldData);
}
/* /*
* @see java.lang.Object#toString() * @see java.lang.Object#toString()
*/ */

View File

@@ -35,13 +35,13 @@ public class FormException extends AlfrescoRuntimeException
{ {
private static final long serialVersionUID = 688834574410335422L; private static final long serialVersionUID = 688834574410335422L;
public FormException(String msg) public FormException(String msgId)
{ {
super(msg); super(msgId);
} }
public FormException(String msg, Throwable cause) public FormException(String msgId, Throwable cause)
{ {
super(msg, cause); super(msgId, cause);
} }
} }

View File

@@ -0,0 +1,49 @@
/*
* 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;
import org.alfresco.error.AlfrescoRuntimeException;
/**
* Exception used by the Form service when a form can not be found for the given item
*
* @author Gavin Cornwell
*/
public class FormNotFoundException extends AlfrescoRuntimeException
{
private static final long serialVersionUID = 688834574410335422L;
public FormNotFoundException(Item item)
{
// TODO: replace strings with msg ids
super("A form could not be found for item: " + item);
}
public FormNotFoundException(Item item, Throwable cause)
{
// TODO: replace strings with msg ids
super("A form could not be found for item: " + item, cause);
}
}

View File

@@ -24,6 +24,8 @@
*/ */
package org.alfresco.repo.forms; package org.alfresco.repo.forms;
import java.util.List;
/** /**
* Form service API. * Form service API.
@@ -35,18 +37,47 @@ package org.alfresco.repo.forms;
public interface FormService public interface FormService
{ {
/** /**
* Returns a form representation of the given item * Returns a form representation of the given item,
* all known fields for the item are included.
* *
* @param item The item to get a form for * @param item The item to get a form for
* @return The Form representation * @return The Form representation
*/ */
public Form getForm(String item); public Form getForm(Item item);
/** /**
* Persists the given form representation for the given item * Returns a form representation of the given item consisting
* only of the given fields.
*
* @param item The item to get a form for
* @param fields Restricted list of fields to include, null
* indicates all possible fields for the item
* should be included
* @return The Form representation
*/
public Form getForm(Item item, List<String> fields);
/**
* Returns a form representation of the given item consisting
* only of the given fields.
*
* @param item The item to get a form for
* @param fields Restricted list of fields to include, null
* indicates all possible fields for the item
* should be included
* @param forcedFields List of field names from 'fields' list
* that should be forcibly included, it is
* up to the form processor implementation
* to determine how to enforce this
* @return The Form representation
*/
public Form getForm(Item item, List<String> fields, List<String> forcedFields);
/**
* Persists the given form representation for the given item.
* *
* @param item The item to persist the form for * @param item The item to persist the form for
* @param data An object representing the form data to persist * @param data An object representing the form data to persist
*/ */
public void saveForm(String item, FormData data); public void saveForm(Item item, FormData data);
} }

View File

@@ -24,9 +24,10 @@
*/ */
package org.alfresco.repo.forms; package org.alfresco.repo.forms;
import java.util.List;
import org.alfresco.repo.forms.processor.FormProcessor; import org.alfresco.repo.forms.processor.FormProcessor;
import org.alfresco.repo.forms.processor.FormProcessorRegistry; import org.alfresco.repo.forms.processor.FormProcessorRegistry;
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
@@ -53,10 +54,26 @@ public class FormServiceImpl implements FormService
this.processorRegistry = registry; this.processorRegistry = registry;
} }
/** /*
* @see org.alfresco.repo.forms.FormService#getForm(java.lang.String) * @see org.alfresco.repo.forms.FormService#getForm(org.alfresco.repo.forms.Item)
*/ */
public Form getForm(String item) public Form getForm(Item item)
{
return getForm(item, null, null);
}
/*
* @see org.alfresco.repo.forms.FormService#getForm(org.alfresco.repo.forms.Item, java.util.List)
*/
public Form getForm(Item item, List<String> fields)
{
return getForm(item, fields, null);
}
/*
* @see org.alfresco.repo.forms.FormService#getForm(org.alfresco.repo.forms.Item, java.util.List, java.util.List)
*/
public Form getForm(Item item, List<String> fields, List<String> forcedFields)
{ {
if (this.processorRegistry == null) if (this.processorRegistry == null)
{ {
@@ -74,26 +91,14 @@ public class FormServiceImpl implements FormService
} }
else else
{ {
Form result = null; return processor.generate(item, fields, forcedFields);
try
{
result = processor.generate(item);
}
catch (InvalidNodeRefException inrx)
{
if (logger.isDebugEnabled())
{
logger.debug(inrx);
}
}
return result;
} }
} }
/* /*
* @see org.alfresco.repo.forms.FormService#saveForm(java.lang.String, org.alfresco.repo.forms.FormData) * @see org.alfresco.repo.forms.FormService#saveForm(org.alfresco.repo.forms.Item, org.alfresco.repo.forms.FormData)
*/ */
public void saveForm(String item, FormData data) public void saveForm(Item item, FormData data)
{ {
if (this.processorRegistry == null) if (this.processorRegistry == null)
{ {

View File

@@ -69,7 +69,10 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
private NodeRef document; private NodeRef document;
private NodeRef associatedDoc; private NodeRef associatedDoc;
private NodeRef childDoc;
private NodeRef folder;
private String documentName; private String documentName;
private String folderName;
private static String VALUE_TITLE = "This is the title for the test document"; private static String VALUE_TITLE = "This is the title for the test document";
private static String VALUE_DESCRIPTION = "This is the description for the test document"; private static String VALUE_DESCRIPTION = "This is the description for the test document";
@@ -88,6 +91,9 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
private static String LABEL_NAME = "Name"; private static String LABEL_NAME = "Name";
private static String LABEL_TITLE = "Title"; private static String LABEL_TITLE = "Title";
private static String LABEL_DESCRIPTION = "Description"; private static String LABEL_DESCRIPTION = "Description";
private static String LABEL_AUTHOR = "Author";
private static String LABEL_MODIFIED = "Modified Date";
private static String LABEL_MODIFIER = "Modifier";
private static String LABEL_MIMETYPE = "Mimetype"; private static String LABEL_MIMETYPE = "Mimetype";
private static String LABEL_ENCODING = "Encoding"; private static String LABEL_ENCODING = "Encoding";
private static String LABEL_SIZE = "Size"; private static String LABEL_SIZE = "Size";
@@ -97,9 +103,10 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
private static String LABEL_SUBJECT = "Subject"; private static String LABEL_SUBJECT = "Subject";
private static String LABEL_SENT_DATE = "Sent Date"; private static String LABEL_SENT_DATE = "Sent Date";
private static String LABEL_REFERENCES = "References"; private static String LABEL_REFERENCES = "References";
private static String LABEL_CONTAINS = "Contains";
private static final String USER_ONE = "UserOne_SiteServiceImplTest"; private static final String USER_ONE = "UserOne_SiteServiceImplTest";
private static final String NODE_FORM_ITEM_KIND = "node";
/** /**
* Called during the transaction setup * Called during the transaction setup
@@ -129,8 +136,9 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore")); new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore"));
Map<QName, Serializable> folderProps = new HashMap<QName, Serializable>(1); Map<QName, Serializable> folderProps = new HashMap<QName, Serializable>(1);
folderProps.put(ContentModel.PROP_NAME, "testFolder" + guid); this.folderName = "testFolder" + guid;
NodeRef folder = this.nodeService.createNode( folderProps.put(ContentModel.PROP_NAME, this.folderName);
this.folder = this.nodeService.createNode(
rootNode, rootNode,
ContentModel.ASSOC_CHILDREN, ContentModel.ASSOC_CHILDREN,
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "testFolder" + guid), QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "testFolder" + guid),
@@ -142,7 +150,7 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
this.documentName = "testDocument" + guid + ".txt"; this.documentName = "testDocument" + guid + ".txt";
docProps.put(ContentModel.PROP_NAME, this.documentName); docProps.put(ContentModel.PROP_NAME, this.documentName);
this.document = this.nodeService.createNode( this.document = this.nodeService.createNode(
folder, this.folder,
ContentModel.ASSOC_CONTAINS, ContentModel.ASSOC_CONTAINS,
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "testDocument" + guid + ".txt"), QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "testDocument" + guid + ".txt"),
ContentModel.TYPE_CONTENT, ContentModel.TYPE_CONTENT,
@@ -151,12 +159,21 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
// create a node to use as target of association // create a node to use as target of association
docProps.put(ContentModel.PROP_NAME, "associatedDocument" + guid + ".txt"); docProps.put(ContentModel.PROP_NAME, "associatedDocument" + guid + ".txt");
this.associatedDoc = this.nodeService.createNode( this.associatedDoc = this.nodeService.createNode(
folder, this.folder,
ContentModel.ASSOC_CONTAINS, ContentModel.ASSOC_CONTAINS,
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "associatedDocument" + guid + ".txt"), QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "associatedDocument" + guid + ".txt"),
ContentModel.TYPE_CONTENT, ContentModel.TYPE_CONTENT,
docProps).getChildRef(); docProps).getChildRef();
// create a node to use as a 2nd child node of the folder
docProps.put(ContentModel.PROP_NAME, "childDocument" + guid + ".txt");
this.childDoc = this.nodeService.createNode(
this.folder,
ContentModel.ASSOC_CONTAINS,
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "childDocument" + guid + ".txt"),
ContentModel.TYPE_CONTENT,
docProps).getChildRef();
// add some content to the nodes // add some content to the nodes
ContentWriter writer = this.contentService.getWriter(this.document, ContentModel.PROP_CONTENT, true); ContentWriter writer = this.contentService.getWriter(this.document, ContentModel.PROP_CONTENT, true);
writer.setMimetype(VALUE_MIMETYPE); writer.setMimetype(VALUE_MIMETYPE);
@@ -190,9 +207,6 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
aspectProps.clear(); aspectProps.clear();
this.nodeService.addAspect(document, ContentModel.ASPECT_REFERENCING, aspectProps); this.nodeService.addAspect(document, ContentModel.ASPECT_REFERENCING, aspectProps);
this.nodeService.createAssociation(this.document, this.associatedDoc, ContentModel.ASSOC_REFERENCES); this.nodeService.createAssociation(this.document, this.associatedDoc, ContentModel.ASSOC_REFERENCES);
// setComplete();
// endTransaction();
} }
private void createUser(String userName) private void createUser(String userName)
@@ -213,15 +227,16 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void testGetForm() throws Exception public void testGetAllDocForm() throws Exception
{ {
Form form = this.formService.getForm(this.document.toString()); Form form = this.formService.getForm(new Item(NODE_FORM_ITEM_KIND, this.document.toString()));
// check a form got returned // check a form got returned
assertNotNull("Expecting form to be present", form); assertNotNull("Expecting form to be present", form);
// check item identifier matches // check item identifier matches
assertEquals(this.document.toString(), form.getItem()); assertEquals(NODE_FORM_ITEM_KIND, form.getItem().getKind());
assertEquals(this.document.toString(), form.getItem().getId());
// check the type is correct // check the type is correct
assertEquals(ContentModel.TYPE_CONTENT.toPrefixString(this.namespaceService), assertEquals(ContentModel.TYPE_CONTENT.toPrefixString(this.namespaceService),
@@ -233,7 +248,7 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
// check the field definitions // check the field definitions
Collection<FieldDefinition> fieldDefs = form.getFieldDefinitions(); Collection<FieldDefinition> fieldDefs = form.getFieldDefinitions();
assertNotNull("Expecting to find fields", fieldDefs); assertNotNull("Expecting to find fields", fieldDefs);
assertEquals("Expecting to find 23 fields", 23, fieldDefs.size()); assertEquals("Expecting to find 22 fields", 22, fieldDefs.size());
// create a Map of the field definitions // 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 // NOTE: we can safely do this as we know there are no duplicate field names and we're not
@@ -364,6 +379,339 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
assertEquals(this.associatedDoc.toString(), targets.get(0)); assertEquals(this.associatedDoc.toString(), targets.get(0));
} }
@SuppressWarnings("unchecked")
public void testGetSelectedFieldsDocForm() throws Exception
{
// define a list of fields to retrieve from the node
List<String> fields = new ArrayList<String>(8);
fields.add("cm:name");
fields.add("cm:title");
fields.add("mimetype");
fields.add("cm:modified");
fields.add("cm:modifier");
fields.add("cm:subjectline");
fields.add("cm:sentdate");
fields.add("cm:references");
Form form = this.formService.getForm(new Item(NODE_FORM_ITEM_KIND, this.document.toString()), fields);
// check a form got returned
assertNotNull("Expecting form to be present", form);
// check item identifier matches
assertEquals(NODE_FORM_ITEM_KIND, form.getItem().getKind());
assertEquals(this.document.toString(), form.getItem().getId());
// check the type is correct
assertEquals(ContentModel.TYPE_CONTENT.toPrefixString(this.namespaceService),
form.getType());
// check there is no group info
assertNull("Expecting the form groups to be null!", form.getFieldGroups());
// check the field definitions
Collection<FieldDefinition> fieldDefs = form.getFieldDefinitions();
assertNotNull("Expecting to find fields", fieldDefs);
assertEquals("Expecting to find " + fields.size() + " fields", fields.size(), 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<String, FieldDefinition> fieldDefMap = new HashMap<String, FieldDefinition>(fieldDefs.size());
for (FieldDefinition fieldDef : fieldDefs)
{
fieldDefMap.put(fieldDef.getName(), fieldDef);
}
// find the fields
PropertyFieldDefinition nameField = (PropertyFieldDefinition)fieldDefMap.get("cm:name");
PropertyFieldDefinition titleField = (PropertyFieldDefinition)fieldDefMap.get("cm:title");
PropertyFieldDefinition mimetypeField = (PropertyFieldDefinition)fieldDefMap.get("mimetype");
PropertyFieldDefinition modifiedField = (PropertyFieldDefinition)fieldDefMap.get("cm:modified");
PropertyFieldDefinition modifierField = (PropertyFieldDefinition)fieldDefMap.get("cm:modifier");
PropertyFieldDefinition subjectField = (PropertyFieldDefinition)fieldDefMap.get("cm:subjectline");
PropertyFieldDefinition sentDateField = (PropertyFieldDefinition)fieldDefMap.get("cm:sentdate");
AssociationFieldDefinition referencesField = (AssociationFieldDefinition)fieldDefMap.get("cm:references");
// check fields are present
assertNotNull("Expecting to find the cm:name field", nameField);
assertNotNull("Expecting to find the cm:title field", titleField);
assertNotNull("Expecting to find the mimetype field", mimetypeField);
assertNotNull("Expecting to find the cm:modified field", modifiedField);
assertNotNull("Expecting to find the cm:modifier field", modifierField);
assertNotNull("Expecting to find the cm:subjectline field", subjectField);
assertNotNull("Expecting to find the cm:sentdate field", sentDateField);
assertNotNull("Expecting to find the cm:references field", referencesField);
// check the labels of all the fields
assertEquals("Expecting cm:name label to be " + LABEL_NAME,
LABEL_NAME, nameField.getLabel());
assertEquals("Expecting cm:title label to be " + LABEL_TITLE,
LABEL_TITLE, titleField.getLabel());
assertEquals("Expecting mimetype label to be " + LABEL_MIMETYPE,
LABEL_MIMETYPE, mimetypeField.getLabel());
assertEquals("Expecting cm:modified label to be " + LABEL_MODIFIED,
LABEL_MODIFIED, modifiedField.getLabel());
assertEquals("Expecting cm:modifier label to be " + LABEL_MODIFIER,
LABEL_MODIFIER, modifierField.getLabel());
assertEquals("Expecting cm:subjectline label to be " + LABEL_SUBJECT,
LABEL_SUBJECT, subjectField.getLabel());
assertEquals("Expecting cm:sentdate label to be " + LABEL_SENT_DATE,
LABEL_SENT_DATE, sentDateField.getLabel());
assertEquals("Expecting cm:references label to be " + LABEL_REFERENCES,
LABEL_REFERENCES, referencesField.getLabel());
// check the details of the modified field
assertEquals("Expecting cm:modified type to be d:datetime", "d:datetime", modifiedField.getDataType());
assertTrue("Expecting cm:modified to be mandatory", modifiedField.isMandatory());
assertFalse("Expecting cm:modified to be single valued", modifiedField.isRepeating());
// check the details of the association field
assertEquals("Expecting cm:references endpoint type to be cm:content", "cm:content",
referencesField.getEndpointType());
assertEquals("Expecting cm:references endpoint direction to be TARGET",
Direction.TARGET.toString(),
referencesField.getEndpointDirection().toString());
assertFalse("Expecting cm:references endpoint to be optional",
referencesField.isEndpointMandatory());
assertTrue("Expecting cm:references endpoint to be 1 to many",
referencesField.isEndpointMany());
// check the form data
FormData data = form.getFormData();
assertNotNull("Expecting form data", data);
Map<String, FormData.FieldData> fieldData = data.getData();
assertNotNull("Expecting field data", fieldData);
assertEquals(this.documentName, fieldData.get("prop:cm:name").getValue());
assertEquals(VALUE_TITLE, fieldData.get("prop:cm:title").getValue());
assertEquals(VALUE_MIMETYPE, fieldData.get("prop:mimetype").getValue());
assertEquals(VALUE_SUBJECT, fieldData.get("prop:cm:subjectline").getValue());
assertEquals(USER_ONE, fieldData.get("prop:cm:modifier").getValue());
Date modifiedDate = (Date)fieldData.get("prop:cm:modified").getValue();
assertNotNull("Expecting to find modified date", modifiedDate);
assertTrue("Expecting modified field to return a Date", (modifiedDate instanceof Date));
Calendar calTestValue = Calendar.getInstance();
calTestValue.setTime(VALUE_SENT_DATE);
Calendar calServiceValue = Calendar.getInstance();
calServiceValue.setTime((Date)fieldData.get("prop:cm:sentdate").getValue());
assertEquals(calTestValue.getTimeInMillis(), calServiceValue.getTimeInMillis());
List<String> targets = (List<String>)fieldData.get("assoc:cm:references").getValue();
assertEquals("Expecting 1 target", 1, targets.size());
assertEquals(this.associatedDoc.toString(), targets.get(0));
}
public void testMissingFieldsDocForm() throws Exception
{
// define a list of fields to retrieve from the node
List<String> fields = new ArrayList<String>(8);
fields.add("cm:name");
fields.add("cm:title");
// add fields that will not be present
fields.add("cm:author");
fields.add("wrong-name");
Form form = this.formService.getForm(new Item(NODE_FORM_ITEM_KIND, this.document.toString()), fields);
// check a form got returned
assertNotNull("Expecting form to be present", form);
// check the field definitions
Collection<FieldDefinition> fieldDefs = form.getFieldDefinitions();
assertNotNull("Expecting to find fields", fieldDefs);
assertEquals("Expecting to find " + (fields.size()-2) + " fields", fields.size()-2, 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<String, FieldDefinition> fieldDefMap = new HashMap<String, FieldDefinition>(fieldDefs.size());
for (FieldDefinition fieldDef : fieldDefs)
{
fieldDefMap.put(fieldDef.getName(), fieldDef);
}
// find the fields
PropertyFieldDefinition nameField = (PropertyFieldDefinition)fieldDefMap.get("cm:name");
PropertyFieldDefinition titleField = (PropertyFieldDefinition)fieldDefMap.get("cm:title");
PropertyFieldDefinition authorField = (PropertyFieldDefinition)fieldDefMap.get("cm:author");
// check fields are present
assertNotNull("Expecting to find the cm:name field", nameField);
assertNotNull("Expecting to find the cm:title field", titleField);
assertNull("Expecting cm:author field to be missing", authorField);
// check the labels of all the fields
assertEquals("Expecting cm:name label to be " + LABEL_NAME,
LABEL_NAME, nameField.getLabel());
assertEquals("Expecting cm:title label to be " + LABEL_TITLE,
LABEL_TITLE, titleField.getLabel());
// check the form data
FormData data = form.getFormData();
assertNotNull("Expecting form data", data);
Map<String, FormData.FieldData> fieldData = data.getData();
assertNotNull("Expecting field data", fieldData);
assertEquals(this.documentName, fieldData.get("prop:cm:name").getValue());
assertEquals(VALUE_TITLE, fieldData.get("prop:cm:title").getValue());
}
public void testForcedFieldsDocForm() throws Exception
{
// define a list of fields to retrieve from the node
List<String> fields = new ArrayList<String>(4);
fields.add("cm:name");
fields.add("cm:title");
// add fields that will not be present
fields.add("cm:author");
fields.add("cm:never");
fields.add("wrong-name");
// try and force the missing fields to appear
List<String> forcedFields = new ArrayList<String>(2);
forcedFields.add("cm:author");
forcedFields.add("cm:never");
forcedFields.add("wrong-name");
Form form = this.formService.getForm(new Item(NODE_FORM_ITEM_KIND, this.document.toString()), fields, forcedFields);
// check a form got returned
assertNotNull("Expecting form to be present", form);
// check the field definitions
Collection<FieldDefinition> fieldDefs = form.getFieldDefinitions();
assertNotNull("Expecting to find fields", fieldDefs);
assertEquals("Expecting to find " + (fields.size()-2) + " fields", fields.size()-2, 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<String, FieldDefinition> fieldDefMap = new HashMap<String, FieldDefinition>(fieldDefs.size());
for (FieldDefinition fieldDef : fieldDefs)
{
fieldDefMap.put(fieldDef.getName(), fieldDef);
}
// find the fields
PropertyFieldDefinition nameField = (PropertyFieldDefinition)fieldDefMap.get("cm:name");
PropertyFieldDefinition titleField = (PropertyFieldDefinition)fieldDefMap.get("cm:title");
PropertyFieldDefinition authorField = (PropertyFieldDefinition)fieldDefMap.get("cm:author");
PropertyFieldDefinition neverField = (PropertyFieldDefinition)fieldDefMap.get("cm:never");
PropertyFieldDefinition wrongField = (PropertyFieldDefinition)fieldDefMap.get("wrong-name");
// check fields are present
assertNotNull("Expecting to find the cm:name field", nameField);
assertNotNull("Expecting to find the cm:title field", titleField);
assertNotNull("Expecting to find the cm:author field", authorField);
assertNull("Expecting cm:never field to be missing", neverField);
assertNull("Expecting wrong-name field to be missing", wrongField);
// check the labels of all the fields
assertEquals("Expecting cm:name label to be " + LABEL_NAME,
LABEL_NAME, nameField.getLabel());
assertEquals("Expecting cm:title label to be " + LABEL_TITLE,
LABEL_TITLE, titleField.getLabel());
assertEquals("Expecting cm:author label to be " + LABEL_AUTHOR,
LABEL_AUTHOR, authorField.getLabel());
// check the form data
FormData data = form.getFormData();
assertNotNull("Expecting form data", data);
Map<String, FormData.FieldData> fieldData = data.getData();
assertNotNull("Expecting field data", fieldData);
assertEquals(this.documentName, fieldData.get("prop:cm:name").getValue());
assertEquals(VALUE_TITLE, fieldData.get("prop:cm:title").getValue());
assertNull("Didn't expect to find a value for cm:author", fieldData.get("prop:cm:author"));
}
@SuppressWarnings("unchecked")
public void testGetAllFolderForm() throws Exception
{
Form form = this.formService.getForm(new Item(NODE_FORM_ITEM_KIND, this.folder.toString()));
// check a form got returned
assertNotNull("Expecting form to be present", form);
// check item identifier matches
assertEquals(NODE_FORM_ITEM_KIND, form.getItem().getKind());
assertEquals(this.folder.toString(), form.getItem().getId());
// check the type is correct
assertEquals(ContentModel.TYPE_FOLDER.toPrefixString(this.namespaceService),
form.getType());
// check there is no group info
assertNull("Expecting the form groups to be null!", form.getFieldGroups());
// check the field definitions
Collection<FieldDefinition> 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<String, FieldDefinition> fieldDefMap = new HashMap<String, FieldDefinition>(fieldDefs.size());
for (FieldDefinition fieldDef : fieldDefs)
{
fieldDefMap.put(fieldDef.getName(), fieldDef);
}
// find the fields
PropertyFieldDefinition nameField = (PropertyFieldDefinition)fieldDefMap.get("cm:name");
AssociationFieldDefinition containsField = (AssociationFieldDefinition)fieldDefMap.get("cm:contains");
// check fields are present
assertNotNull("Expecting to find the cm:name field", nameField);
assertNotNull("Expecting to find the cm:contains field", containsField);
// check the labels of all the fields
assertEquals("Expecting cm:name label to be " + LABEL_NAME,
LABEL_NAME, nameField.getLabel());
assertEquals("Expecting cm:contains label to be " + LABEL_CONTAINS,
LABEL_CONTAINS, containsField.getLabel());
// check details of name field
assertEquals("Expecting cm:name type to be d:text", "d:text", nameField.getDataType());
assertTrue("Expecting cm:name to be mandatory", nameField.isMandatory());
assertFalse("Expecting cm:name to be single valued", nameField.isRepeating());
// check the details of the association field
assertEquals("Expecting cm:contains endpoint type to be sys:base", "sys:base",
containsField.getEndpointType());
assertEquals("Expecting cm:contains endpoint direction to be TARGET",
Direction.TARGET.toString(),
containsField.getEndpointDirection().toString());
assertFalse("Expecting cm:contains endpoint to be optional",
containsField.isEndpointMandatory());
assertTrue("Expecting cm:contains endpoint to be 1 to many",
containsField.isEndpointMany());
// check the form data
FormData data = form.getFormData();
assertNotNull("Expecting form data", data);
Map<String, FormData.FieldData> fieldData = data.getData();
assertNotNull("Expecting field data", fieldData);
assertEquals(this.folderName, fieldData.get("prop:cm:name").getValue());
List<String> children = (List<String>)fieldData.get("assoc:cm:contains").getValue();
assertEquals("Expecting 3 children", 3, children.size());
assertEquals(this.document.toString(), children.get(0));
assertEquals(this.associatedDoc.toString(), children.get(1));
assertEquals(this.childDoc.toString(), children.get(2));
}
@SuppressWarnings("unchecked")
public void testGetSelectedFieldsFolderForm() throws Exception
{
// attempt to get a form with fields that are not appropriate
// for a folder type i.e. mimetype and encoding
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void testSaveForm() throws Exception public void testSaveForm() throws Exception
{ {
@@ -398,7 +746,7 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
data.addData("cm:wrong", "This should not be persisted"); data.addData("cm:wrong", "This should not be persisted");
// persist the data // persist the data
this.formService.saveForm(this.document.toString(), data); this.formService.saveForm(new Item(NODE_FORM_ITEM_KIND, this.document.toString()), data);
// retrieve the data directly from the node service to ensure its been changed // retrieve the data directly from the node service to ensure its been changed
Map<QName, Serializable> updatedProps = this.nodeService.getProperties(this.document); Map<QName, Serializable> updatedProps = this.nodeService.getProperties(this.document);
@@ -429,29 +777,13 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
} }
} }
// public void testApplyAspectProgrammatically() throws Exception
// {
// NodeRef nrSrc = new NodeRef("workspace://SpacesStore/320c74ad-dc79-4812-adf1-8160c37fdecb");
// NodeRef nrDest = new NodeRef("workspace://SpacesStore/5ac5ccac-3409-4f4a-9338-a39ee1acd2cb");
//
// Map<QName, Serializable> aspectProps = new HashMap<QName, Serializable>(2);
// // add referencing aspect (has association)
// this.nodeService.addAspect(nrSrc, ContentModel.ASPECT_REFERENCING, aspectProps);
// this.nodeService.createAssociation(nrSrc, nrDest, ContentModel.ASSOC_REFERENCES);
//
// setComplete();
// endTransaction();
//
// System.out.println("did it work?");
// }
//
public void testNoForm() throws Exception public void testNoForm() throws Exception
{ {
// test that a form can not be retrieved for a non-existent item // test that a form can not be retrieved for a non-existent item
try try
{ {
this.formService.getForm("Invalid Item"); this.formService.getForm(new Item("Invalid Kind", "Invalid Id"));
fail("Expecting getForm for 'Invalid Item' to fail"); fail("Expecting getForm for 'Invalid Kind/Item' to fail");
} }
catch (Exception e) catch (Exception e)
{ {
@@ -459,14 +791,22 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
} }
String missingNode = this.document.toString().replace("-", "x"); String missingNode = this.document.toString().replace("-", "x");
Form form = this.formService.getForm(missingNode);
assertNull("Expecting getForm for a missing node to be null", form); try
{
this.formService.getForm(new Item(NODE_FORM_ITEM_KIND, missingNode));
fail("Expecting getForm for a missing node to fail");
}
catch (FormNotFoundException fnne)
{
// expected
}
// test that a form can not be saved for a non-existent item // test that a form can not be saved for a non-existent item
try try
{ {
this.formService.saveForm("Invalid Item", new FormData()); this.formService.saveForm(new Item("Invalid Kind", "Invalid Id"), new FormData());
fail("Expecting saveForm for 'Invalid Item' to fail"); fail("Expecting saveForm for 'Invalid Kind/Item' to fail");
} }
catch (Exception e) catch (Exception e)
{ {
@@ -475,7 +815,7 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
try try
{ {
this.formService.saveForm(missingNode, new FormData()); this.formService.saveForm(new Item(NODE_FORM_ITEM_KIND, missingNode), new FormData());
fail("Expecting saveForm for a missing node to fail"); fail("Expecting saveForm for a missing node to fail");
} }
catch (Exception e) catch (Exception e)
@@ -488,7 +828,10 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
{ {
Map<String, Object> model = new HashMap<String, Object>(); Map<String, Object> model = new HashMap<String, Object>();
model.put("testDoc", this.document.toString()); model.put("testDoc", this.document.toString());
model.put("testDocName", this.documentName);
model.put("testAssociatedDoc", this.associatedDoc.toString()); model.put("testAssociatedDoc", this.associatedDoc.toString());
model.put("folder", this.folder.toString());
model.put("folderName", this.folderName);
ScriptLocation location = new ClasspathScriptLocation("org/alfresco/repo/forms/script/test_formService.js"); ScriptLocation location = new ClasspathScriptLocation("org/alfresco/repo/forms/script/test_formService.js");
this.scriptService.executeScript(location, model); this.scriptService.executeScript(location, model);

View File

@@ -0,0 +1,82 @@
/*
* 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;
import org.alfresco.util.ParameterCheck;
/**
* Represents an item a form is generated for.
*
* @author Gavin Cornwell
*/
public class Item
{
private String kind;
private String id;
/**
* Constructs an item.
*
* @param kind The kind of item, for example, 'node', 'task'
* @param id The identifier of the item
*/
public Item(String kind, String id)
{
ParameterCheck.mandatoryString("kind", kind);
ParameterCheck.mandatoryString("id", id);
this.kind = kind;
this.id = id;
}
/**
* Returns the kind of item.
*
* @return The kind of item
*/
public String getKind()
{
return this.kind;
}
/**
* Returns the identifier of the item
*
* @return The identifier of the item
*/
public String getId()
{
return this.id;
}
/*
* @see java.lang.Object#toString()
*/
@Override
public String toString()
{
return "[" + this.kind + "]" + this.id;
}
}

View File

@@ -27,6 +27,7 @@ package org.alfresco.repo.forms.processor;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.alfresco.repo.forms.Item;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
@@ -114,14 +115,14 @@ public abstract class AbstractFormProcessor implements FormProcessor
} }
/* /*
* @see org.alfresco.repo.forms.processor.FormProcessor#isApplicable(java.lang.String) * @see org.alfresco.repo.forms.processor.FormProcessor#isApplicable(org.alfresco.repo.forms.Item)
*/ */
public boolean isApplicable(String item) public boolean isApplicable(Item item)
{ {
// this form processor matches if the match pattern provided matches // this form processor matches if the match pattern provided matches
// the item provided // the kind of the item provided
Matcher matcher = patternMatcher.matcher(item); Matcher matcher = patternMatcher.matcher(item.getKind());
boolean matches = matcher.matches(); boolean matches = matcher.matches();
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())

View File

@@ -24,9 +24,12 @@
*/ */
package org.alfresco.repo.forms.processor; package org.alfresco.repo.forms.processor;
import java.util.List;
import org.alfresco.repo.forms.Form; import org.alfresco.repo.forms.Form;
import org.alfresco.repo.forms.FormData; import org.alfresco.repo.forms.FormData;
import org.alfresco.repo.forms.FormException; import org.alfresco.repo.forms.FormException;
import org.alfresco.repo.forms.Item;
/** /**
* Abstract base class for all FormProcessor implementations that wish to use the * Abstract base class for all FormProcessor implementations that wish to use the
@@ -52,11 +55,13 @@ public abstract class AbstractFormProcessorByHandlers extends AbstractFormProces
* Generates a Form for the given item, constructed by calling each * Generates a Form for the given item, constructed by calling each
* applicable registered handler * applicable registered handler
* *
* @see org.alfresco.repo.forms.processor.FormProcessor#generate(java.lang.String) * @see org.alfresco.repo.forms.processor.FormProcessor#generate(org.alfresco.repo.forms.Item, java.util.List, java.util.List)
* @param item The item to generate a form for * @param item The item to generate a form for
* @param fields Restricted list of fields to include
* @param forcedFields List of fields to forcibly include
* @return The generated Form * @return The generated Form
*/ */
public Form generate(String item) public Form generate(Item item, List<String> fields, List<String> forcedFields)
{ {
if (this.handlerRegistry == null) if (this.handlerRegistry == null)
{ {
@@ -72,7 +77,7 @@ public abstract class AbstractFormProcessorByHandlers extends AbstractFormProces
// execute each applicable handler // execute each applicable handler
for (Handler handler: this.handlerRegistry.getApplicableHandlers(typedItem)) for (Handler handler: this.handlerRegistry.getApplicableHandlers(typedItem))
{ {
form = handler.handleGenerate(typedItem, form); form = handler.handleGenerate(typedItem, fields, forcedFields, form);
} }
return form; return form;
@@ -82,11 +87,11 @@ public abstract class AbstractFormProcessorByHandlers extends AbstractFormProces
* Persists the given form data for the given item, completed by calling * Persists the given form data for the given item, completed by calling
* each applicable registered handler * each applicable registered handler
* *
* @see org.alfresco.repo.forms.processor.FormProcessor#persist(java.lang.String, org.alfresco.repo.forms.FormData) * @see org.alfresco.repo.forms.processor.FormProcessor#persist(org.alfresco.repo.forms.Item, org.alfresco.repo.forms.FormData)
* @param item The item to save the form for * @param item The item to save the form for
* @param data The object representing the form data * @param data The object representing the form data
*/ */
public void persist(String item, FormData data) public void persist(Item item, FormData data)
{ {
if (this.handlerRegistry == null) if (this.handlerRegistry == null)
{ {
@@ -113,5 +118,5 @@ public abstract class AbstractFormProcessorByHandlers extends AbstractFormProces
* @param item The item to get a typed object for * @param item The item to get a typed object for
* @return The typed object * @return The typed object
*/ */
protected abstract Object getTypedItem(String item); protected abstract Object getTypedItem(Item item);
} }

View File

@@ -85,7 +85,7 @@ public abstract class AbstractHandler implements Handler
} }
/* /*
* @see org.alfresco.repo.forms.processor.FormProcessorHandler#isApplicable(java.lang.String) * @see org.alfresco.repo.forms.processor.Handler#isApplicable(java.lang.String)
*/ */
public boolean isApplicable(Object item) public boolean isApplicable(Object item)
{ {

View File

@@ -24,8 +24,11 @@
*/ */
package org.alfresco.repo.forms.processor; package org.alfresco.repo.forms.processor;
import java.util.List;
import org.alfresco.repo.forms.Form; import org.alfresco.repo.forms.Form;
import org.alfresco.repo.forms.FormData; import org.alfresco.repo.forms.FormData;
import org.alfresco.repo.forms.Item;
/** /**
* Interface definition of a form processor which is responsible * Interface definition of a form processor which is responsible
@@ -44,7 +47,7 @@ public interface FormProcessor
* @param item The item the form is being generated for * @param item The item the form is being generated for
* @return true if the processor is applicable * @return true if the processor is applicable
*/ */
public boolean isApplicable(String item); public boolean isApplicable(Item item);
/** /**
* Determines whether this form processor is active * Determines whether this form processor is active
@@ -57,9 +60,16 @@ public interface FormProcessor
* Returns a Form representation for an item * Returns a Form representation for an item
* *
* @param item The item to generate a Form object for * @param item The item to generate a Form object for
* @param fields Restricted list of fields to include, null
* indicates all possible fields for the item
* should be included
* @param forcedFields List of field names from 'fields' list
* that should be forcibly included, it is
* up to the form processor implementation
* to determine how to enforce this
* @return The Form representation * @return The Form representation
*/ */
public Form generate(String item); public Form generate(Item item, List<String> fields, List<String> forcedFields);
/** /**
* Persists the given object representing the form data * Persists the given object representing the form data
@@ -68,5 +78,5 @@ public interface FormProcessor
* @param item The item to generate a Form object for * @param item The item to generate a Form object for
* @param data An object representing the data of the form * @param data An object representing the data of the form
*/ */
public void persist(String item, FormData data); public void persist(Item item, FormData data);
} }

View File

@@ -27,6 +27,7 @@ package org.alfresco.repo.forms.processor;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.alfresco.repo.forms.Item;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
@@ -85,7 +86,7 @@ public class FormProcessorRegistry
* @param item The item to find a form processor for * @param item The item to find a form processor for
* @return An applicable FormProcessor * @return An applicable FormProcessor
*/ */
public FormProcessor getApplicableFormProcessor(String item) public FormProcessor getApplicableFormProcessor(Item item)
{ {
FormProcessor selectedProcessor = null; FormProcessor selectedProcessor = null;

View File

@@ -24,6 +24,8 @@
*/ */
package org.alfresco.repo.forms.processor; package org.alfresco.repo.forms.processor;
import java.util.List;
import org.alfresco.repo.forms.Form; import org.alfresco.repo.forms.Form;
import org.alfresco.repo.forms.FormData; import org.alfresco.repo.forms.FormData;
@@ -35,8 +37,6 @@ import org.alfresco.repo.forms.FormData;
*/ */
public interface Handler public interface Handler
{ {
// TODO: Investigate whether Generics can be used instead of Object
/** /**
* Determines whether the handler is applicable for the given item. * Determines whether the handler is applicable for the given item.
* <p> * <p>
@@ -63,11 +63,14 @@ public interface Handler
* to a more appropriate object, for example all the Node based handlers * to a more appropriate object, for example all the Node based handlers
* can expect a NodeRef object and therefore cast to that. * can expect a NodeRef object and therefore cast to that.
* *
* @see org.alfresco.repo.forms.processor.FormProcessor#generate(org.alfresco.repo.forms.Item, java.util.List, java.util.List)
* @param item The item to generate a Form for * @param item The item 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 * @param form The Form object
* @return The modified Form object * @return The modified Form object
*/ */
public Form handleGenerate(Object item, Form form); public Form handleGenerate(Object item, List<String> fields, List<String> forcedFields, Form form);
/** /**
* Handles the persistence of form data for the given item. * Handles the persistence of form data for the given item.

View File

@@ -24,9 +24,13 @@
*/ */
package org.alfresco.repo.forms.processor; package org.alfresco.repo.forms.processor;
import org.alfresco.repo.forms.FormNotFoundException;
import org.alfresco.repo.forms.Item;
import org.alfresco.service.cmr.repository.InvalidNodeRefException; import org.alfresco.service.cmr.repository.InvalidNodeRefException;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.NodeService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/** /**
* FormProcessor implementation that can generate and persist Form objects * FormProcessor implementation that can generate and persist Form objects
@@ -36,6 +40,9 @@ import org.alfresco.service.cmr.repository.NodeService;
*/ */
public class NodeFormProcessor extends AbstractFormProcessorByHandlers public class NodeFormProcessor extends AbstractFormProcessorByHandlers
{ {
/** Logger */
private static Log logger = LogFactory.getLog(NodeFormProcessor.class);
/** Services */ /** Services */
protected NodeService nodeService; protected NodeService nodeService;
@@ -50,18 +57,50 @@ public class NodeFormProcessor extends AbstractFormProcessorByHandlers
} }
/* /*
* @see org.alfresco.repo.forms.processor.AbstractFormProcessor#getTypedItem(java.lang.String) * @see org.alfresco.repo.forms.processor.AbstractFormProcessorByHandlers#getTypedItem(org.alfresco.repo.forms.Item)
*/ */
@Override @Override
protected Object getTypedItem(String item) protected Object getTypedItem(Item item)
{ {
// create NodeRef representation // create NodeRef representation, the id could already be in a valid
NodeRef nodeRef = new NodeRef(item); // NodeRef format or it may be in a URL friendly format
NodeRef nodeRef = null;
if (NodeRef.isNodeRef(item.getId()))
{
nodeRef = new NodeRef(item.getId());
}
else
{
// split the string into the 3 required parts
String[] parts = item.getId().split("/");
if (parts.length == 3)
{
try
{
nodeRef = new NodeRef(parts[0], parts[1], parts[2]);
}
catch (IllegalArgumentException iae)
{
// ignored for now, dealt with below
if (logger.isDebugEnabled())
logger.debug("NodeRef creation failed for: " + item.getId(), iae);
}
}
}
// check we have a valid node ref
if (nodeRef == null)
{
throw new FormNotFoundException(item,
new IllegalArgumentException(item.getId()));
}
// check the node itself exists // check the node itself exists
if (this.nodeService.exists(nodeRef) == false) if (this.nodeService.exists(nodeRef) == false)
{ {
throw new InvalidNodeRefException("Node does not exist: " + nodeRef, nodeRef); throw new FormNotFoundException(item,
new InvalidNodeRefException("Node does not exist: " + nodeRef, nodeRef));
} }
else else
{ {

View File

@@ -26,7 +26,6 @@ package org.alfresco.repo.forms.processor;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
@@ -163,9 +162,9 @@ public class NodeHandler extends AbstractHandler
} }
/* /*
* @see org.alfresco.repo.forms.processor.FormProcessorHandler#handleGenerate(java.lang.Object, org.alfresco.repo.forms.Form) * @see org.alfresco.repo.forms.processor.Handler#handleGenerate(java.lang.Object, java.util.List, java.util.List, org.alfresco.repo.forms.Form)
*/ */
public Form handleGenerate(Object item, Form form) public Form handleGenerate(Object item, List<String> fields, List<String> forcedFields, Form form)
{ {
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
logger.debug("Generating form for: " + item); logger.debug("Generating form for: " + item);
@@ -174,7 +173,7 @@ public class NodeHandler extends AbstractHandler
NodeRef nodeRef = (NodeRef)item; NodeRef nodeRef = (NodeRef)item;
// generate the form for the node // generate the form for the node
generateNode(nodeRef, form); generateNode(nodeRef, fields, forcedFields, form);
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
logger.debug("Returning form: " + form); logger.debug("Returning form: " + form);
@@ -182,201 +181,384 @@ public class NodeHandler extends AbstractHandler
return form; return form;
} }
/*
* @see org.alfresco.repo.forms.processor.FormProcessorHandler#handlePersist(java.lang.Object, org.alfresco.repo.forms.FormData)
*/
public void handlePersist(Object item, FormData data)
{
if (logger.isDebugEnabled())
logger.debug("Persisting form for: " + item);
// cast to the expected NodeRef representation
NodeRef nodeRef = (NodeRef)item;
// persist the node
persistNode(nodeRef, data);
}
/** /**
* Sets up the Form object for the given NodeRef * Sets up the Form object for the given NodeRef
* *
* @param nodeRef The NodeRef to generate a Form for * @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 * @param form The Form instance to populate
*/ */
protected void generateNode(NodeRef nodeRef, Form form) protected void generateNode(NodeRef nodeRef, List<String> fields, List<String> forcedFields, Form form)
{ {
// set the type // set the type
QName type = this.nodeService.getType(nodeRef); QName type = this.nodeService.getType(nodeRef);
form.setType(type.toPrefixString(this.namespaceService)); form.setType(type.toPrefixString(this.namespaceService));
// setup field definitions and data if (fields != null && fields.size() > 0)
FormData formData = new FormData(); {
generatePropertyFields(nodeRef, form, formData); generateSelectedFields(nodeRef, fields, forcedFields, form);
generateAssociationFields(nodeRef, form, formData); }
generateChildAssociationFields(nodeRef, form, formData); else
generateTransientFields(nodeRef, form, formData); {
form.setFormData(formData); // setup field definitions and data
generateAllPropertyFields(nodeRef, form);
generateAllAssociationFields(nodeRef, form);
generateAllChildAssociationFields(nodeRef, form);
generateTransientFields(nodeRef, form);
}
} }
/** /**
* Persists the given FormData on the given NodeRef * 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
* definition for those fields is made so they can be included.
* *
* @param nodeRef The NodeRef to persist the form data on * @param nodeRef The NodeRef of the node being setup
* @param data The FormData to persist * @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 persistNode(NodeRef nodeRef, FormData data) protected void generateSelectedFields(NodeRef nodeRef, List<String> fields, List<String> forcedFields, Form form)
{ {
// get the property definitions for the type of node being persisted if (logger.isDebugEnabled())
logger.debug("Generating selected fields: " + fields + " and forcing: " + forcedFields);
// get data dictionary definition for node
QName type = this.nodeService.getType(nodeRef); QName type = this.nodeService.getType(nodeRef);
Collection<QName> aspects = this.getAspectsToInclude(nodeRef); TypeDefinition typeDef = this.dictionaryService.getAnonymousType(type,
TypeDefinition typeDef = this.dictionaryService.getAnonymousType(type, aspects); this.nodeService.getAspects(nodeRef));
Map<QName, PropertyDefinition> propDefs = typeDef.getProperties();
Map<QName, AssociationDefinition> assocDefs = typeDef.getAssociations(); Map<QName, AssociationDefinition> assocDefs = typeDef.getAssociations();
Map<QName, ChildAssociationDefinition> childAssocDefs = typeDef.getChildAssociations(); Map<QName, ChildAssociationDefinition> childAssocDefs = typeDef.getChildAssociations();
Map<QName, PropertyDefinition> propDefs = typeDef.getProperties(); Map<QName, Serializable> propValues = this.nodeService.getProperties(nodeRef);
Map<QName, Serializable> propsToPersist = new HashMap<QName, Serializable>(data.getData().size()); for (String fieldName : fields)
List<AbstractAssocCommand> assocsToPersist = new ArrayList<AbstractAssocCommand>();
for (String dataKey : data.getData().keySet())
{ {
FieldData fieldData = data.getData().get(dataKey); // try and split the field name
// NOTE: ignore file fields for now, not supported yet! String[] parts = fieldName.split(":");
if (fieldData.isFile() == false) if (parts.length == 2)
{ {
String fieldName = fieldData.getName(); // create qname of field name
String qNamePrefix = parts[0];
if (fieldName.startsWith(PROP_PREFIX)) String localName = parts[1];
QName fullQName = QName.createQName(qNamePrefix, localName, namespaceService);
// lookup property def on node
PropertyDefinition propDef = propDefs.get(fullQName);
if (propDef != null)
{ {
processPropertyPersist(nodeRef, propDefs, fieldData, propsToPersist); // generate the property field
generatePropertyField(propDef, propValues.get(propDef.getName()), form);
} }
else if (fieldName.startsWith(ASSOC_PREFIX)) else
{ {
processAssociationPersist(nodeRef, assocDefs, childAssocDefs, fieldData, assocsToPersist); // look for association defined for the type
AssociationDefinition assocDef = assocDefs.get(fullQName);
if (assocDef != null)
{
// generate the association field
generateAssociationField(assocDef,
this.nodeService.getTargetAssocs(nodeRef, fullQName), form);
}
else
{
ChildAssociationDefinition childAssocDef = childAssocDefs.get(fullQName);
if (childAssocDef != null)
{
// generate the association field
// TODO: see if we can get just the specific child assoc data
generateAssociationField(childAssocDef,
this.nodeService.getChildAssocs(nodeRef), form);
}
else
{
// still not found the field, is it a force'd field?
if (forcedFields != null && forcedFields.size() > 0 &&
forcedFields.contains(fieldName))
{
generateForcedField(fullQName, form);
}
else if (logger.isWarnEnabled())
{
logger.warn("Ignoring field \"" + fieldName +
"\" as it is not defined for the current node 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 = (ContentData)this.nodeService.getProperty(nodeRef, ContentModel.PROP_CONTENT);
if (content != null)
{
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 (logger.isWarnEnabled()) else if (logger.isWarnEnabled())
{ {
logger.warn("Ignoring unrecognised field '" + fieldName + "'"); logger.warn("Ignoring unrecognised field \"" + fieldName + "\"");
} }
} }
} }
// persist the properties using addProperties as this changes the repo values of
// those properties included in the Map, but leaves any other property values unchanged,
// whereas setProperties causes the deletion of properties that are not included in the Map.
this.nodeService.addProperties(nodeRef, propsToPersist);
for (AbstractAssocCommand cmd : assocsToPersist)
{
//TODO If there is an attempt to add and remove the same assoc in one request,
// we could drop each request and do nothing.
cmd.updateAssociations(nodeService);
}
} }
/** /**
* Sets up the field definitions for the node's properties. * Generates a field definition for the given field that is being forced
* to show
*
* @param fieldName QName of the field to force
* @param form The Form instance to populated
*/
protected void generateForcedField(QName fieldName, Form form)
{
if (logger.isDebugEnabled())
logger.debug("Attempting to force the inclusion of field \"" +
fieldName.toPrefixString(this.namespaceService) + "\"");
// lookup the field as a property in the model
PropertyDefinition propDef = this.dictionaryService.getProperty(fieldName);
if (propDef != null)
{
// generate the property field
generatePropertyField(propDef, null, form);
}
else
{
// lookup the field as an association in the model
AssociationDefinition assocDef = this.dictionaryService.getAssociation(fieldName);
if (assocDef != null)
{
// generate the association field
generateAssociationField(assocDef, null, form);
}
else if (logger.isWarnEnabled())
{
logger.warn("Ignoring field \"" + fieldName.toPrefixString(this.namespaceService) +
"\" as it is not defined for the current node and can not be found in any model");
}
}
}
/**
* Sets up the field definitions for all the node's properties.
* *
* @param nodeRef The NodeRef of the node being setup * @param nodeRef The NodeRef of the node being setup
* @param form The Form instance to populate * @param form The Form instance to populate
* @param formData The FormData instance to populate
*/ */
@SuppressWarnings("unchecked") protected void generateAllPropertyFields(NodeRef nodeRef, Form form)
protected void generatePropertyFields(NodeRef nodeRef, Form form, FormData formData)
{ {
// get data dictionary definition for node // get data dictionary definition for node
QName type = this.nodeService.getType(nodeRef); QName type = this.nodeService.getType(nodeRef);
Collection<QName> aspects = this.getAspectsToInclude(nodeRef); TypeDefinition typeDef = this.dictionaryService.getAnonymousType(type,
TypeDefinition typeDef = this.dictionaryService.getAnonymousType(type, aspects); this.nodeService.getAspects(nodeRef));
// iterate round the property definitions, create the equivalent // iterate round the property definitions for the node and create
// field definition and setup the data for the property // the equivalent field definition and setup the data for the property
Map<QName, PropertyDefinition> propDefs = typeDef.getProperties(); Map<QName, PropertyDefinition> propDefs = typeDef.getProperties();
Map<QName, Serializable> propValues = this.nodeService.getProperties(nodeRef); Map<QName, Serializable> propValues = this.nodeService.getProperties(nodeRef);
for (PropertyDefinition propDef : propDefs.values()) for (PropertyDefinition propDef : propDefs.values())
{ {
String propName = propDef.getName().toPrefixString(this.namespaceService); generatePropertyField(propDef, propValues.get(propDef.getName()), form);
PropertyFieldDefinition fieldDef = new PropertyFieldDefinition( }
propName, propDef.getDataType().getName().toPrefixString( }
this.namespaceService));
/**
* Sets up a field definition for the given property
*
* @param propDef The PropertyDefinition of the field to generate
* @param propValue The value of the property field
* @param form The Form instance to populate
*/
@SuppressWarnings("unchecked")
protected void generatePropertyField(PropertyDefinition propDef, Serializable propValue, Form form)
{
String propName = propDef.getName().toPrefixString(this.namespaceService);
PropertyFieldDefinition fieldDef = new PropertyFieldDefinition(
propName, propDef.getDataType().getName().toPrefixString(
this.namespaceService));
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());
// setup constraints for the property
List<ConstraintDefinition> constraints = propDef.getConstraints();
if (constraints != null && constraints.size() > 0)
{
List<FieldConstraint> fieldConstraints =
new ArrayList<FieldConstraint>(constraints.size());
String title = propDef.getTitle(); for (ConstraintDefinition constraintDef : constraints)
if (title == null)
{ {
title = propName; Constraint constraint = constraintDef.getConstraint();
} Map<String, String> fieldConstraintParams = null;
fieldDef.setLabel(title); Map<String, Object> constraintParams = constraint.getParameters();
fieldDef.setDefaultValue(propDef.getDefaultValue()); if (constraintParams != null)
fieldDef.setDescription(propDef.getDescription());
fieldDef.setMandatory(propDef.isMandatory());
fieldDef.setProtectedField(propDef.isProtected());
fieldDef.setRepeating(propDef.isMultiValued());
// setup constraints for the property
List<ConstraintDefinition> constraints = propDef.getConstraints();
if (constraints != null && constraints.size() > 0)
{
List<FieldConstraint> fieldConstraints =
new ArrayList<FieldConstraint>(constraints.size());
for (ConstraintDefinition constraintDef : constraints)
{ {
Constraint constraint = constraintDef.getConstraint(); // TODO: Just return the param value object, don't convert to String
Map<String, String> fieldConstraintParams = null; fieldConstraintParams = new HashMap<String, String>(constraintParams.size());
Map<String, Object> constraintParams = constraint.getParameters(); for (String name : constraintParams.keySet())
if (constraintParams != null)
{ {
// TODO: Just return the param value object, don't convert to String Object paramValue = constraintParams.get(name);
fieldConstraintParams = new HashMap<String, String>(constraintParams.size());
for (String name : constraintParams.keySet()) if (paramValue instanceof List)
{ {
Object paramValue = constraintParams.get(name); paramValue = makeListString((List)paramValue);
if (paramValue instanceof List)
{
paramValue = makeListString((List)paramValue);
}
fieldConstraintParams.put(name, paramValue.toString());
} }
fieldConstraintParams.put(name, paramValue.toString());
} }
FieldConstraint fieldConstraint = fieldDef.new FieldConstraint(
constraint.getType(), fieldConstraintParams);
fieldConstraints.add(fieldConstraint);
} }
FieldConstraint fieldConstraint = fieldDef.new FieldConstraint(
fieldDef.setConstraints(fieldConstraints); constraint.getType(), fieldConstraintParams);
fieldConstraints.add(fieldConstraint);
} }
form.addFieldDefinition(fieldDef); fieldDef.setConstraints(fieldConstraints);
}
// get the field value and add to the form data object
Serializable fieldData = propValues.get(propDef.getName()); form.addFieldDefinition(fieldDef);
if (fieldData != null)
// add the property value to the form
if (propValue != null)
{
if (propValue instanceof List)
{ {
if (fieldData instanceof List) // temporarily add repeating field data as a comma
{ // separated list, this will be changed to using
// temporarily add repeating field data as a comma // a separate field for each value once we have full
// separated list, this will be changed to using // UI support in place.
// a separate field for each value once we have full propValue = makeListString((List)propValue);
// UI support in place. }
fieldData = makeListString((List)fieldData);
} form.addData(PROP_PREFIX + fieldDef.getName(), propValue);
}
}
/**
* 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)
{
// setup mimetype field
generateMimetypePropertyField(content, form);
formData.addData(PROP_PREFIX + fieldDef.getName(), fieldData); // setup encoding field
generateEncodingPropertyField(content, form);
// setup size field
generateSizePropertyField(content, form);
} }
} }
} }
/** /**
* Sets up the field definitions for the node's associations. * Generates the field definition for the transient mimetype property
*
* @param content The ContentData object to generate the field from
* @param form The Form instance to populate
*/
protected void generateMimetypePropertyField(ContentData content, Form form)
{
PropertyFieldDefinition mimetypeField = new PropertyFieldDefinition(
TRANSIENT_MIMETYPE, DataTypeDefinition.TEXT.toPrefixString(
this.namespaceService));
mimetypeField.setLabel(I18NUtil.getMessage(MSG_MIMETYPE_LABEL));
mimetypeField.setDescription(I18NUtil.getMessage(MSG_MIMETYPE_DESC));
form.addFieldDefinition(mimetypeField);
form.addData(PROP_PREFIX + TRANSIENT_MIMETYPE, content.getMimetype());
}
/**
* Generates the field definition for the transient encoding property
*
* @param content The ContentData object to generate the field from
* @param form The Form instance to populate
*/
protected void generateEncodingPropertyField(ContentData content, Form form)
{
PropertyFieldDefinition encodingField = new PropertyFieldDefinition(
TRANSIENT_ENCODING, DataTypeDefinition.TEXT.toPrefixString(
this.namespaceService));
encodingField.setLabel(I18NUtil.getMessage(MSG_ENCODING_LABEL));
encodingField.setDescription(I18NUtil.getMessage(MSG_ENCODING_DESC));
form.addFieldDefinition(encodingField);
form.addData(PROP_PREFIX + TRANSIENT_ENCODING, content.getEncoding());
}
/**
* Generates the field definition for the transient size property
*
* @param content The ContentData object to generate the field from
* @param form The Form instance to populate
*/
protected void generateSizePropertyField(ContentData content, Form form)
{
PropertyFieldDefinition sizeField = new PropertyFieldDefinition(
TRANSIENT_SIZE, DataTypeDefinition.LONG.toPrefixString(
this.namespaceService));
sizeField.setLabel(I18NUtil.getMessage(MSG_SIZE_LABEL));
sizeField.setDescription(I18NUtil.getMessage(MSG_SIZE_DESC));
sizeField.setProtectedField(true);
form.addFieldDefinition(sizeField);
form.addData(PROP_PREFIX + TRANSIENT_SIZE, new Long(content.getSize()));
}
/**
* Sets up the field definitions for all the node's associations.
* *
* @param nodeRef The NodeRef of the node being setup * @param nodeRef The NodeRef of the node being setup
* @param form The Form instance to populate * @param form The Form instance to populate
* @param formData The FormData instance to populate
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected void generateAssociationFields(NodeRef nodeRef, Form form, FormData formData) protected void generateAllAssociationFields(NodeRef nodeRef, Form form)
{ {
// ********************************************************
// re-factor this to gen. all assocs defs and not values
// ********************************************************
// add target association data // add target association data
List<AssociationRef> associations = this.nodeService.getTargetAssocs(nodeRef, List<AssociationRef> associations = this.nodeService.getTargetAssocs(nodeRef,
RegexQNamePattern.MATCH_ALL); RegexQNamePattern.MATCH_ALL);
@@ -431,11 +613,11 @@ public class NodeHandler extends AbstractHandler
// add the value as a List (or add to the list if the form data // add the value as a List (or add to the list if the form data
// is already present) // is already present)
FieldData fieldData = formData.getData().get(prefixedAssocName); FieldData fieldData = form.getFormData().getData().get(prefixedAssocName);
if (fieldData == null) if (fieldData == null)
{ {
targets = new ArrayList<String>(4); targets = new ArrayList<String>(4);
formData.addData(prefixedAssocName, targets); form.addData(prefixedAssocName, targets);
} }
else else
{ {
@@ -448,7 +630,7 @@ public class NodeHandler extends AbstractHandler
else else
{ {
// there should only be one value // there should only be one value
formData.addData(prefixedAssocName, assocValue); form.addData(prefixedAssocName, assocValue);
} }
} }
} }
@@ -459,9 +641,8 @@ public class NodeHandler extends AbstractHandler
* *
* @param nodeRef The NodeRef of the node being setup * @param nodeRef The NodeRef of the node being setup
* @param form The Form instance to populate * @param form The Form instance to populate
* @param formData The FormData instance to populate
*/ */
protected void generateChildAssociationFields(NodeRef nodeRef, Form form, FormData formData) protected void generateAllChildAssociationFields(NodeRef nodeRef, Form form)
{ {
List<ChildAssociationRef> childAssocs = this.nodeService.getChildAssocs(nodeRef); List<ChildAssociationRef> childAssocs = this.nodeService.getChildAssocs(nodeRef);
if (childAssocs.isEmpty()) if (childAssocs.isEmpty())
@@ -527,7 +708,7 @@ public class NodeHandler extends AbstractHandler
// We don't want the whitespace or enclosing square brackets that come // We don't want the whitespace or enclosing square brackets that come
// with java.util.List.toString(), hence the custom String construction. // with java.util.List.toString(), hence the custom String construction.
StringBuilder assocValue = new StringBuilder(); /*StringBuilder assocValue = new StringBuilder();
for (Iterator<ChildAssociationRef> iter = values.iterator(); iter.hasNext(); ) for (Iterator<ChildAssociationRef> iter = values.iterator(); iter.hasNext(); )
{ {
ChildAssociationRef nextChild = iter.next(); ChildAssociationRef nextChild = iter.next();
@@ -537,55 +718,142 @@ public class NodeHandler extends AbstractHandler
assocValue.append(","); assocValue.append(",");
} }
} }
formData.addData(ASSOC_PREFIX + associationName, assocValue.toString()); form.addData(ASSOC_PREFIX + associationName, assocValue.toString());*/
List<String> nodeRefs = new ArrayList<String>(4);
for (Iterator<ChildAssociationRef> iter = values.iterator(); iter.hasNext(); )
{
ChildAssociationRef nextChild = iter.next();
nodeRefs.add(nextChild.getChildRef().toString());
}
form.addData(ASSOC_PREFIX + associationName, nodeRefs);
} }
} }
/** /**
* Sets up the field definitions for any transient fields that may be * Sets up a field definition for the given association
* useful, for example, 'mimetype', 'size' and 'encoding'.
* *
* @param nodeRef The NodeRef of the node being setup * @param assocDef The AssociationDefinition of the field to generate
* @param assocValues The values of the association field
* @param form The Form instance to populate * @param form The Form instance to populate
* @param formData The FormData instance to populate
*/ */
protected void generateTransientFields(NodeRef nodeRef, Form form, FormData formData) @SuppressWarnings("unchecked")
protected void generateAssociationField(AssociationDefinition assocDef,
List assocValues, Form form)
{ {
// if the node is content add the 'mimetype', 'size' and 'encoding' fields. String assocName = assocDef.getName().toPrefixString(this.namespaceService);
QName type = this.nodeService.getType(nodeRef); AssociationFieldDefinition fieldDef = new AssociationFieldDefinition(assocName,
if (this.dictionaryService.isSubClass(type, ContentModel.TYPE_CONTENT)) assocDef.getTargetClass().getName().toPrefixString(
this.namespaceService), Direction.TARGET);
String title = assocDef.getTitle();
if (title == null)
{ {
ContentData content = (ContentData)this.nodeService.getProperty(nodeRef, ContentModel.PROP_CONTENT); title = assocName;
if (content != null) }
fieldDef.setLabel(title);
fieldDef.setDescription(assocDef.getDescription());
fieldDef.setProtectedField(assocDef.isProtected());
fieldDef.setEndpointMandatory(assocDef.isTargetMandatory());
fieldDef.setEndpointMany(assocDef.isTargetMany());
// add definition to the form
form.addFieldDefinition(fieldDef);
// add the association value to the form
String prefixedAssocName = ASSOC_PREFIX + assocName;
// determine the type of association values data and extract accordingly
List<String> values = new ArrayList<String>(4);
for (Object value : assocValues)
{
if (value instanceof ChildAssociationRef)
{ {
// setup mimetype field values.add(((ChildAssociationRef)value).getChildRef().toString());
PropertyFieldDefinition mimetypeField = new PropertyFieldDefinition(
TRANSIENT_MIMETYPE, DataTypeDefinition.TEXT.toPrefixString(
this.namespaceService));
mimetypeField.setLabel(I18NUtil.getMessage(MSG_MIMETYPE_LABEL));
mimetypeField.setDescription(I18NUtil.getMessage(MSG_MIMETYPE_DESC));
form.addFieldDefinition(mimetypeField);
formData.addData(PROP_PREFIX + TRANSIENT_MIMETYPE, content.getMimetype());
// setup encoding field
PropertyFieldDefinition encodingField = new PropertyFieldDefinition(
TRANSIENT_ENCODING, DataTypeDefinition.TEXT.toPrefixString(
this.namespaceService));
encodingField.setLabel(I18NUtil.getMessage(MSG_ENCODING_LABEL));
encodingField.setDescription(I18NUtil.getMessage(MSG_ENCODING_DESC));
form.addFieldDefinition(encodingField);
formData.addData(PROP_PREFIX + TRANSIENT_ENCODING, content.getEncoding());
// setup size field
PropertyFieldDefinition sizeField = new PropertyFieldDefinition(
TRANSIENT_SIZE, DataTypeDefinition.LONG.toPrefixString(
this.namespaceService));
sizeField.setLabel(I18NUtil.getMessage(MSG_SIZE_LABEL));
sizeField.setDescription(I18NUtil.getMessage(MSG_SIZE_DESC));
sizeField.setProtectedField(true);
form.addFieldDefinition(sizeField);
formData.addData(PROP_PREFIX + TRANSIENT_SIZE, new Long(content.getSize()));
} }
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.
// TODO: Do we also return a well known named list of association names
// for each noderef so that clients do not have extra work to do
// to display the current values to the user
form.addData(prefixedAssocName, values);
}
/*
* @see org.alfresco.repo.forms.processor.FormProcessorHandler#handlePersist(java.lang.Object, org.alfresco.repo.forms.FormData)
*/
public void handlePersist(Object item, FormData data)
{
if (logger.isDebugEnabled())
logger.debug("Persisting form for: " + item);
// cast to the expected NodeRef representation
NodeRef nodeRef = (NodeRef)item;
// persist the node
persistNode(nodeRef, data);
}
/**
* Persists the given FormData on the given NodeRef
*
* @param nodeRef The NodeRef to persist the form data on
* @param data The FormData to persist
*/
protected void persistNode(NodeRef nodeRef, FormData data)
{
// get the property definitions for the type of node being persisted
QName type = this.nodeService.getType(nodeRef);
TypeDefinition typeDef = this.dictionaryService.getAnonymousType(type,
this.nodeService.getAspects(nodeRef));
Map<QName, AssociationDefinition> assocDefs = typeDef.getAssociations();
Map<QName, ChildAssociationDefinition> childAssocDefs = typeDef.getChildAssociations();
Map<QName, PropertyDefinition> propDefs = typeDef.getProperties();
Map<QName, Serializable> propsToPersist = new HashMap<QName, Serializable>(data.getData().size());
List<AbstractAssocCommand> assocsToPersist = new ArrayList<AbstractAssocCommand>();
for (String dataKey : data.getData().keySet())
{
FieldData fieldData = data.getData().get(dataKey);
// NOTE: ignore file fields for now, not supported yet!
if (fieldData.isFile() == false)
{
String fieldName = fieldData.getName();
if (fieldName.startsWith(PROP_PREFIX))
{
processPropertyPersist(nodeRef, propDefs, fieldData, propsToPersist);
}
else if (fieldName.startsWith(ASSOC_PREFIX))
{
processAssociationPersist(nodeRef, assocDefs, childAssocDefs, fieldData, assocsToPersist);
}
else if (logger.isWarnEnabled())
{
logger.warn("Ignoring unrecognised field '" + fieldName + "'");
}
}
}
// persist the properties using addProperties as this changes the repo values of
// those properties included in the Map, but leaves any other property values unchanged,
// whereas setProperties causes the deletion of properties that are not included in the Map.
this.nodeService.addProperties(nodeRef, propsToPersist);
for (AbstractAssocCommand cmd : assocsToPersist)
{
//TODO If there is an attempt to add and remove the same assoc in one request,
// we could drop each request and do nothing.
cmd.updateAssociations(nodeService);
} }
} }
@@ -946,33 +1214,6 @@ public class NodeHandler extends AbstractHandler
} }
} }
/**
* Returns a Collection of aspects for the given noderef to use
* for generating and persisting the form.
*
* @param nodeRef The NodeRef of the node to get aspects for
* @return The Collection of aspects
*/
protected Collection<QName> getAspectsToInclude(NodeRef nodeRef)
{
List<QName> currentAspects = new ArrayList<QName>();
currentAspects.addAll(this.nodeService.getAspects(nodeRef));
// TODO: make the list of aspects to ensure are present configurable
// make sure the titled and author aspects are present
if (currentAspects.contains(ContentModel.ASPECT_TITLED) == false)
{
currentAspects.add(ContentModel.ASPECT_TITLED);
}
if (currentAspects.contains(ContentModel.ASPECT_AUTHOR) == false)
{
currentAspects.add(ContentModel.ASPECT_AUTHOR);
}
return currentAspects;
}
/** /**
* Returns a comma separated list string of the given list object. * Returns a comma separated list string of the given list object.
* *

View File

@@ -58,9 +58,14 @@ public class ScriptForm implements Serializable
} }
} }
public String getItem() public String getItemKind()
{ {
return form.getItem(); return form.getItem().getKind();
}
public String getItemId()
{
return form.getItem().getId();
} }
public String getType() public String getType()

View File

@@ -24,11 +24,14 @@
*/ */
package org.alfresco.repo.forms.script; package org.alfresco.repo.forms.script;
import java.util.Arrays;
import java.util.List;
import org.alfresco.repo.forms.Form; import org.alfresco.repo.forms.Form;
import org.alfresco.repo.forms.FormData; import org.alfresco.repo.forms.FormData;
import org.alfresco.repo.forms.FormService; import org.alfresco.repo.forms.FormService;
import org.alfresco.repo.forms.Item;
import org.alfresco.repo.jscript.BaseScopableProcessorExtension; import org.alfresco.repo.jscript.BaseScopableProcessorExtension;
import org.alfresco.service.ServiceRegistry;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
@@ -40,22 +43,10 @@ import org.apache.commons.logging.LogFactory;
public class ScriptFormService extends BaseScopableProcessorExtension public class ScriptFormService extends BaseScopableProcessorExtension
{ {
private static Log logger = LogFactory.getLog(ScriptFormService.class); private static Log logger = LogFactory.getLog(ScriptFormService.class);
/** Service Registry */
private ServiceRegistry serviceRegistry;
/** The site service */ /** The site service */
private FormService formService; private FormService formService;
/**
* Sets the Service Registry
*
* @param serviceRegistry
*/
public void setServiceRegistry(ServiceRegistry serviceRegistry)
{
this.serviceRegistry = serviceRegistry;
}
/** /**
* Set the form service * Set the form service
* *
@@ -68,25 +59,79 @@ public class ScriptFormService extends BaseScopableProcessorExtension
} }
/** /**
* Returns the form for the given item * Returns a form representation of the given item,
* all known fields for the item are included.
* *
* @param item The item to retrieve a form for * @param itemKind The kind of item to retrieve a form for
* @param itemId The identifier of the item to retrieve a form for
* @return The form * @return The form
*/ */
public ScriptForm getForm(String item) public ScriptForm getForm(String itemKind, String itemId)
{ {
Form result = formService.getForm(item); return getForm(itemKind, itemId, null);
}
/**
* Returns a form representation of the given item consisting
* only of the given fields.
*
* @param itemKind The kind of item to retrieve a form for
* @param itemId The identifier of the item to retrieve a form for
* @param fields String array of fields to include, null
* indicates all possible fields for the item
* should be included
* @return The form
*/
public ScriptForm getForm(String itemKind, String itemId, String[] fields)
{
return getForm(itemKind, itemId, fields, null);
}
/**
* Returns a form representation of the given item consisting
* only of the given fields.
*
* @param itemKind The kind of item to retrieve a form for
* @param itemId The identifier of the item to retrieve a form for
* @param fields String array of fields to include, null
* indicates all possible fields for the item
* should be included
* @param forcedFields List of field names from 'fields' list
* that should be forcibly included, it is
* up to the form processor implementation
* to determine how to enforce this
* @return The form
*/
public ScriptForm getForm(String itemKind, String itemId,
String[] fields, String[] forcedFields)
{
// create List<String> representations of field params if necessary
List<String> fieldsList = null;
List<String> forcedFieldsList = null;
if (fields != null)
{
fieldsList = Arrays.asList(fields);
}
if (forcedFields != null)
{
forcedFieldsList = Arrays.asList(forcedFields);
}
Form result = formService.getForm(new Item(itemKind, itemId), fieldsList, forcedFieldsList);
return result == null ? null : new ScriptForm(result); return result == null ? null : new ScriptForm(result);
} }
/** /**
* Persists the given data object for the item provided * Persists the given data object for the item provided
* *
* @param item The item to persist the data for * @param itemKind The kind of item to retrieve a form for
* @param itemId The identifier of the item to retrieve a form for
* @param postData The post data, this can be a Map of name value * @param postData The post data, this can be a Map of name value
* pairs, a webscript FormData object or a JSONObject * pairs, a webscript FormData object or a JSONObject
*/ */
public void saveForm(String item, Object postData) public void saveForm(String itemKind, String itemId, Object postData)
{ {
// A note on data conversion as passed in to this method: // A note on data conversion as passed in to this method:
// Each of the 3 submission methods (multipart/formdata, JSON Post and // Each of the 3 submission methods (multipart/formdata, JSON Post and
@@ -109,6 +154,6 @@ public class ScriptFormService extends BaseScopableProcessorExtension
return; return;
} }
formService.saveForm(item, dataForFormService); formService.saveForm(new Item(itemKind, itemId), dataForFormService);
} }
} }

View File

@@ -3,37 +3,48 @@ function testGetFormForNonExistentContentNode()
// Replace all the digits in the ID with an 'x'. // Replace all the digits in the ID with an 'x'.
// Surely that node will not exist... // Surely that node will not exist...
var corruptedTestDoc = testDoc.replace(/\d/g, "x"); var corruptedTestDoc = testDoc.replace(/\d/g, "x");
var form = formService.getForm(corruptedTestDoc); var form = null;
try
{
form = formService.getForm("node", corruptedTestDoc);
}
catch (e)
{
// expected
}
test.assertNull(form, "Form should have not been found: " + testDoc); test.assertNull(form, "Form should have not been found: " + testDoc);
} }
function testGetFormForContentNode() function testGetFormForContentNode()
{ {
// Get a known form and check its various attributes/properties. // Get a known form and check its various attributes/properties.
var form = formService.getForm(testDoc); var form = formService.getForm("node", testDoc);
test.assertNotNull(form, "Form should have been found: " + testDoc); test.assertNotNull(form, "Form should have been found for: " + testDoc);
test.assertEquals(testDoc, form.item); test.assertEquals("node", form.itemKind);
test.assertEquals('cm:content', form.type); test.assertEquals(testDoc, form.itemId);
test.assertEquals('cm:content', form.type);
test.assertNull(form.fieldGroups, "form.fieldGroups should be null.");
test.assertNull(form.fieldGroups, "form.fieldGroups should be null.");
var fieldDefs = form.fieldDefinitions;
test.assertNotNull(fieldDefs, "field definitions should not be null."); var fieldDefs = form.fieldDefinitions;
test.assertEquals(23, fieldDefs.length); test.assertNotNull(fieldDefs, "field definitions should not be null.");
test.assertEquals(22, fieldDefs.length);
// as we know there are no duplicates we can safely create a map of the
// field definitions for the purposes of this test // as we know there are no duplicates we can safely create a map of the
var fieldDefnDataHash = {}; // field definitions for the purposes of this test
var fieldDef = null; var fieldDefnDataHash = {};
for (var x = 0; x < fieldDefs.length; x++) var fieldDef = null;
{ for (var x = 0; x < fieldDefs.length; x++)
fieldDef = fieldDefs[x]; {
fieldDefnDataHash[fieldDef.name] = fieldDef; fieldDef = fieldDefs[x];
} fieldDefnDataHash[fieldDef.name] = fieldDef;
}
var nameField = fieldDefnDataHash['cm:name'];
var titleField = fieldDefnDataHash['cm:title']; var nameField = fieldDefnDataHash['cm:name'];
var titleField = fieldDefnDataHash['cm:title'];
var descField = fieldDefnDataHash['cm:description']; var descField = fieldDefnDataHash['cm:description'];
var originatorField = fieldDefnDataHash['cm:originator']; var originatorField = fieldDefnDataHash['cm:originator'];
var addresseeField = fieldDefnDataHash['cm:addressee']; var addresseeField = fieldDefnDataHash['cm:addressee'];
@@ -101,6 +112,7 @@ function testGetFormForContentNode()
test.assertNotNull(fieldData, "fieldData should not be null."); test.assertNotNull(fieldData, "fieldData should not be null.");
test.assertNotNull(fieldData.length, "fieldData.length should not be null."); test.assertNotNull(fieldData.length, "fieldData.length should not be null.");
test.assertEquals(testDocName, fieldData["prop:cm:name"].value);
test.assertEquals("This is the title for the test document", fieldData["prop:cm:title"].value); test.assertEquals("This is the title for the test document", fieldData["prop:cm:title"].value);
test.assertEquals("This is the description for the test document", fieldData["prop:cm:description"].value); test.assertEquals("This is the description for the test document", fieldData["prop:cm:description"].value);
test.assertEquals("fred@customer.com", fieldData["prop:cm:originator"].value); test.assertEquals("fred@customer.com", fieldData["prop:cm:originator"].value);
@@ -126,6 +138,195 @@ function testGetFormForContentNode()
test.assertEquals(testAssociatedDoc, targets); test.assertEquals(testAssociatedDoc, targets);
} }
function testGetFormForFolderNode()
{
// Get a known form and check its various attributes/properties.
var form = formService.getForm("node", folder);
test.assertNotNull(form, "Form should have been found for: " + testDoc);
test.assertEquals("node", form.itemKind);
test.assertEquals(folder, form.itemId);
test.assertEquals('cm:folder', form.type);
test.assertNull(form.fieldGroups, "form.fieldGroups should be null.");
var fieldDefs = form.fieldDefinitions;
test.assertNotNull(fieldDefs, "field definitions should not be null.");
test.assertEquals(11, fieldDefs.length);
// as we know there are no duplicates we can safely create a map of the
// field definitions for the purposes of this test
var fieldDefnDataHash = {};
var fieldDef = null;
for (var x = 0; x < fieldDefs.length; x++)
{
fieldDef = fieldDefs[x];
fieldDefnDataHash[fieldDef.name] = fieldDef;
}
var nameField = fieldDefnDataHash['cm:name'];
var containsField = fieldDefnDataHash['cm:contains'];
test.assertNotNull(nameField, "Expecting to find the cm:name field");
test.assertNotNull(containsField, "Expecting to find the cm:contains field");
// check the labels of all the fields
test.assertEquals("Name", nameField.label);
test.assertEquals("Contains", containsField.label);
// check details of name field
test.assertEquals("d:text", nameField.dataType);
test.assertTrue(nameField.mandatory);
// Expecting cm:name to be single-valued.
test.assertFalse(nameField.repeating, "nameField.repeating was not false.");
// compare the association details
test.assertEquals("TARGET", "" + containsField.endpointDirection);
test.assertFalse(containsField.endpointMandatory, "containsField.endpointMandatory was not false.");
test.assertTrue(containsField.endpointMany, "constainsField.endpointMany was not true.");
// check the form data
var formData = form.formData;
test.assertNotNull(formData, "formData should not be null.");
var fieldData = formData.data;
test.assertNotNull(fieldData, "fieldData should not be null.");
test.assertNotNull(fieldData.length, "fieldData.length should not be null.");
test.assertEquals(folderName, fieldData["prop:cm:name"].value);
var children = fieldData["assoc:cm:contains"].value;
test.assertNotNull(children, "children should not be null.");
var childrenArr = children.split(",");
test.assertTrue(childrenArr.length == 3, "Expecting there to be 3 children");
}
function testGetFormWithSelectedFields()
{
// Define list of fields to retrieve
var fields = [];
fields.push("cm:name");
fields.push("cm:title");
fields.push("mimetype");
fields.push("cm:modified");
// add field that will be missing
fields.push("cm:author");
// Get a a form for the given fields
var form = formService.getForm("node", testDoc, fields);
test.assertNotNull(form, "Form should have been found for: " + testDoc);
var fieldDefs = form.fieldDefinitions;
test.assertNotNull(fieldDefs, "field definitions should not be null.");
test.assertEquals(4, fieldDefs.length);
// as we know there are no duplicates we can safely create a map of the
// field definitions for the purposes of this test
var fieldDefnDataHash = {};
var fieldDef = null;
for (var x = 0; x < fieldDefs.length; x++)
{
fieldDef = fieldDefs[x];
fieldDefnDataHash[fieldDef.name] = fieldDef;
}
var nameField = fieldDefnDataHash['cm:name'];
var titleField = fieldDefnDataHash['cm:title'];
var mimetypeField = fieldDefnDataHash['mimetype'];
var modifiedField = fieldDefnDataHash['cm:modified'];
var authorField = fieldDefnDataHash['cm:author'];
test.assertNotNull(nameField, "Expecting to find the cm:name field");
test.assertNotNull(titleField, "Expecting to find the cm:title field");
test.assertNotNull(mimetypeField, "Expecting to find the mimetype field");
test.assertNotNull(modifiedField, "Expecting to find the cm:modified field");
test.assertTrue(authorField === undefined, "Expecting cm:author field to be missing");
// check the labels of all the fields
test.assertEquals("Name", nameField.label);
test.assertEquals("Title", titleField.label);
test.assertEquals("Mimetype", mimetypeField.label);
test.assertEquals("Modified Date", modifiedField.label);
// check the form data
var formData = form.formData;
test.assertNotNull(formData, "formData should not be null.");
var fieldData = formData.data;
test.assertNotNull(fieldData, "fieldData should not be null.");
test.assertNotNull(fieldData.length, "fieldData.length should not be null.");
test.assertEquals(testDocName, fieldData["prop:cm:name"].value);
test.assertEquals("This is the title for the test document", fieldData["prop:cm:title"].value);
}
function testGetFormWithForcedFields()
{
// Define list of fields to retrieve
var fields = [];
fields.push("cm:name");
fields.push("cm:title");
fields.push("mimetype");
fields.push("cm:modified");
fields.push("cm:author");
fields.push("cm:wrong");
// define a list of fields to force
var force = [];
force.push("cm:author");
force.push("cm:wrong");
// Get a a form for the given fields
var form = formService.getForm("node", testDoc, fields, force);
test.assertNotNull(form, "Form should have been found for: " + testDoc);
var fieldDefs = form.fieldDefinitions;
test.assertNotNull(fieldDefs, "field definitions should not be null.");
test.assertEquals(5, fieldDefs.length);
// as we know there are no duplicates we can safely create a map of the
// field definitions for the purposes of this test
var fieldDefnDataHash = {};
var fieldDef = null;
for (var x = 0; x < fieldDefs.length; x++)
{
fieldDef = fieldDefs[x];
fieldDefnDataHash[fieldDef.name] = fieldDef;
}
var nameField = fieldDefnDataHash['cm:name'];
var titleField = fieldDefnDataHash['cm:title'];
var mimetypeField = fieldDefnDataHash['mimetype'];
var modifiedField = fieldDefnDataHash['cm:modified'];
var authorField = fieldDefnDataHash['cm:author'];
var wrongField = fieldDefnDataHash['cm:wrong'];
test.assertNotNull(nameField, "Expecting to find the cm:name field");
test.assertNotNull(titleField, "Expecting to find the cm:title field");
test.assertNotNull(mimetypeField, "Expecting to find the mimetype field");
test.assertNotNull(modifiedField, "Expecting to find the cm:modified field");
test.assertNotNull(authorField, "Expecting to find the cm:author field");
test.assertTrue(wrongField === undefined, "Expecting cm:wrong field to be missing");
// check the labels of all the fields
test.assertEquals("Name", nameField.label);
test.assertEquals("Title", titleField.label);
test.assertEquals("Mimetype", mimetypeField.label);
test.assertEquals("Modified Date", modifiedField.label);
test.assertEquals("Author", authorField.label);
// check the form data
var formData = form.formData;
test.assertNotNull(formData, "formData should not be null.");
var fieldData = formData.data;
test.assertNotNull(fieldData, "fieldData should not be null.");
test.assertNotNull(fieldData.length, "fieldData.length should not be null.");
test.assertEquals(testDocName, fieldData["prop:cm:name"].value);
test.assertEquals("This is the title for the test document", fieldData["prop:cm:title"].value);
test.assertNull(fieldData["prop:cm:author"], "Expecting cm:author to be null");
}
// Execute tests // Execute tests
testGetFormForNonExistentContentNode(); testGetFormForNonExistentContentNode();
testGetFormForContentNode(); testGetFormForContentNode();
testGetFormForFolderNode();
testGetFormWithSelectedFields();
testGetFormWithForcedFields();