Added client and server side support for transient properties

- As long as a template for a control is configured fields without a definition can now be displayed
 - Added 3 well known transient properties; mimetype, encoding and size (these are similar to the propertyResolvers we had in the JSF client)
 - Added explicit persistence handling for the new transient properties, the name property and adds aspect if title/description and/or author property is present
 - Added saveForm test

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@13693 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Gavin Cornwell
2009-03-19 21:24:59 +00:00
parent 0e25e67967
commit 2e94b28d01
6 changed files with 573 additions and 71 deletions

View File

@@ -3,6 +3,14 @@
<beans> <beans>
<bean id="formResourceBundles" class="org.alfresco.i18n.ResourceBundleBootstrapComponent">
<property name="resourceBundles">
<list>
<value>alfresco.messages.form-service</value>
</list>
</property>
</bean>
<!-- form service bean --> <!-- form service bean -->
<bean id="FormService" class="org.springframework.aop.framework.ProxyFactoryBean"> <bean id="FormService" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces"> <property name="proxyInterfaces">
@@ -76,6 +84,7 @@
parent="baseFormHandler"> parent="baseFormHandler">
<property name="handlerRegistry" ref="nodeHandlerRegistry" /> <property name="handlerRegistry" ref="nodeHandlerRegistry" />
<property name="nodeService" ref="NodeService" /> <property name="nodeService" ref="NodeService" />
<property name="fileFolderService" ref="FileFolderService" />
<property name="dictionaryService" ref="DictionaryService" /> <property name="dictionaryService" ref="DictionaryService" />
<property name="namespaceService" ref="NamespaceService" /> <property name="namespaceService" ref="NamespaceService" />
</bean> </bean>
@@ -86,7 +95,6 @@
parent="baseFormHandler" /> parent="baseFormHandler" />
--> -->
<bean id="formServiceScript" parent="baseJavaScriptExtension" class="org.alfresco.repo.forms.script.ScriptFormService"> <bean id="formServiceScript" parent="baseJavaScriptExtension" class="org.alfresco.repo.forms.script.ScriptFormService">
<property name="extensionName"> <property name="extensionName">
<value>formService</value> <value>formService</value>

View File

@@ -0,0 +1,8 @@
# form service externalised display strings
form_service.mimetype.label=Mimetype
form_service.mimetype.description=Mimetype of the content
form_service.encoding.label=Encoding
form_service.encoding.description=Encoding of the content
form_service.size.label=Size
form_service.size.description=Size of the content in bytes

View File

@@ -37,7 +37,7 @@ public abstract class FieldDefinition
protected String binding; protected String binding;
protected String defaultValue; protected String defaultValue;
protected FieldGroup group; protected FieldGroup group;
protected boolean protectedField; protected boolean protectedField = false;
/** /**
* Default constructor * Default constructor

View File

@@ -34,11 +34,15 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import org.alfresco.model.ContentModel; import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.forms.AssociationFieldDefinition.Direction; import org.alfresco.repo.forms.AssociationFieldDefinition.Direction;
import org.alfresco.repo.forms.FormData.FieldData; import org.alfresco.repo.forms.FormData.FieldData;
import org.alfresco.repo.jscript.ClasspathScriptLocation;
import org.alfresco.repo.forms.PropertyFieldDefinition.FieldConstraint; import org.alfresco.repo.forms.PropertyFieldDefinition.FieldConstraint;
import org.alfresco.repo.jscript.ClasspathScriptLocation;
import org.alfresco.repo.security.authentication.AuthenticationComponent; import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.ScriptLocation; import org.alfresco.service.cmr.repository.ScriptLocation;
import org.alfresco.service.cmr.repository.ScriptService; import org.alfresco.service.cmr.repository.ScriptService;
@@ -61,9 +65,11 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
private NamespaceService namespaceService; private NamespaceService namespaceService;
private ScriptService scriptService; private ScriptService scriptService;
private PersonService personService; private PersonService personService;
private ContentService contentService;
private NodeRef document; private NodeRef document;
private NodeRef associatedDoc; private NodeRef associatedDoc;
private String documentName;
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";
@@ -72,11 +78,17 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
private static String VALUE_ADDRESSEES1 = "harry@example.com"; private static String VALUE_ADDRESSEES1 = "harry@example.com";
private static String VALUE_ADDRESSEES2 = "jane@example.com"; private static String VALUE_ADDRESSEES2 = "jane@example.com";
private static String VALUE_SUBJECT = "The subject is..."; private static String VALUE_SUBJECT = "The subject is...";
private static String VALUE_MIMETYPE = MimetypeMap.MIMETYPE_TEXT_PLAIN;
private static String VALUE_ENCODING = "UTF-8";
private static String VALUE_CONTENT = "This is the content for the test document";
private static Date VALUE_SENT_DATE = new Date(); private static Date VALUE_SENT_DATE = new Date();
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_MIMETYPE = "Mimetype";
private static String LABEL_ENCODING = "Encoding";
private static String LABEL_SIZE = "Size";
private static String LABEL_ORIGINATOR = "Originator"; private static String LABEL_ORIGINATOR = "Originator";
private static String LABEL_ADDRESSEE = "Addressee"; private static String LABEL_ADDRESSEE = "Addressee";
private static String LABEL_ADDRESSEES = "Addressees"; private static String LABEL_ADDRESSEES = "Addressees";
@@ -100,6 +112,7 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
this.namespaceService = (NamespaceService)this.applicationContext.getBean("NamespaceService"); this.namespaceService = (NamespaceService)this.applicationContext.getBean("NamespaceService");
this.scriptService = (ScriptService)this.applicationContext.getBean("ScriptService"); this.scriptService = (ScriptService)this.applicationContext.getBean("ScriptService");
this.personService = (PersonService)this.applicationContext.getBean("PersonService"); this.personService = (PersonService)this.applicationContext.getBean("PersonService");
this.contentService = (ContentService)this.applicationContext.getBean("ContentService");
AuthenticationComponent authenticationComponent = (AuthenticationComponent) this.applicationContext AuthenticationComponent authenticationComponent = (AuthenticationComponent) this.applicationContext
.getBean("authenticationComponent"); .getBean("authenticationComponent");
@@ -124,7 +137,8 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
// Create a node // Create a node
Map<QName, Serializable> docProps = new HashMap<QName, Serializable>(1); Map<QName, Serializable> docProps = new HashMap<QName, Serializable>(1);
docProps.put(ContentModel.PROP_NAME, "testDocument" + guid + ".txt"); this.documentName = "testDocument" + guid + ".txt";
docProps.put(ContentModel.PROP_NAME, this.documentName);
this.document = this.nodeService.createNode( this.document = this.nodeService.createNode(
folder, folder,
ContentModel.ASSOC_CONTAINS, ContentModel.ASSOC_CONTAINS,
@@ -141,6 +155,12 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
ContentModel.TYPE_CONTENT, ContentModel.TYPE_CONTENT,
docProps).getChildRef(); docProps).getChildRef();
// add some content to the node
ContentWriter writer = this.contentService.getWriter(this.document, ContentModel.PROP_CONTENT, true);
writer.setMimetype(VALUE_MIMETYPE);
writer.setEncoding(VALUE_ENCODING);
writer.putContent(VALUE_CONTENT);
// add standard titled aspect // add standard titled aspect
Map<QName, Serializable> aspectProps = new HashMap<QName, Serializable>(2); Map<QName, Serializable> aspectProps = new HashMap<QName, Serializable>(2);
aspectProps.put(ContentModel.PROP_TITLE, VALUE_TITLE); aspectProps.put(ContentModel.PROP_TITLE, VALUE_TITLE);
@@ -164,8 +184,8 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
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(); //setComplete();
endTransaction(); //endTransaction();
} }
private void createUser(String userName) private void createUser(String userName)
@@ -206,7 +226,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 19 fields", 19, 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
@@ -221,6 +241,9 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
PropertyFieldDefinition nameField = (PropertyFieldDefinition)fieldDefMap.get("cm:name"); PropertyFieldDefinition nameField = (PropertyFieldDefinition)fieldDefMap.get("cm:name");
PropertyFieldDefinition titleField = (PropertyFieldDefinition)fieldDefMap.get("cm:title"); PropertyFieldDefinition titleField = (PropertyFieldDefinition)fieldDefMap.get("cm:title");
PropertyFieldDefinition descField = (PropertyFieldDefinition)fieldDefMap.get("cm:description"); PropertyFieldDefinition descField = (PropertyFieldDefinition)fieldDefMap.get("cm:description");
PropertyFieldDefinition mimetypeField = (PropertyFieldDefinition)fieldDefMap.get("mimetype");
PropertyFieldDefinition encodingField = (PropertyFieldDefinition)fieldDefMap.get("encoding");
PropertyFieldDefinition sizeField = (PropertyFieldDefinition)fieldDefMap.get("size");
PropertyFieldDefinition originatorField = (PropertyFieldDefinition)fieldDefMap.get("cm:originator"); PropertyFieldDefinition originatorField = (PropertyFieldDefinition)fieldDefMap.get("cm:originator");
PropertyFieldDefinition addresseeField = (PropertyFieldDefinition)fieldDefMap.get("cm:addressee"); PropertyFieldDefinition addresseeField = (PropertyFieldDefinition)fieldDefMap.get("cm:addressee");
PropertyFieldDefinition addresseesField = (PropertyFieldDefinition)fieldDefMap.get("cm:addressees"); PropertyFieldDefinition addresseesField = (PropertyFieldDefinition)fieldDefMap.get("cm:addressees");
@@ -232,6 +255,9 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
assertNotNull("Expecting to find the cm:name field", nameField); assertNotNull("Expecting to find the cm:name field", nameField);
assertNotNull("Expecting to find the cm:title field", titleField); assertNotNull("Expecting to find the cm:title field", titleField);
assertNotNull("Expecting to find the cm:description field", descField); assertNotNull("Expecting to find the cm:description field", descField);
assertNotNull("Expecting to find the mimetype field", mimetypeField);
assertNotNull("Expecting to find the encoding field", encodingField);
assertNotNull("Expecting to find the size field", sizeField);
assertNotNull("Expecting to find the cm:originator field", originatorField); assertNotNull("Expecting to find the cm:originator field", originatorField);
assertNotNull("Expecting to find the cm:addressee field", addresseeField); assertNotNull("Expecting to find the cm:addressee field", addresseeField);
assertNotNull("Expecting to find the cm:addressees field", addresseesField); assertNotNull("Expecting to find the cm:addressees field", addresseesField);
@@ -246,6 +272,12 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
LABEL_TITLE, titleField.getLabel()); LABEL_TITLE, titleField.getLabel());
assertEquals("Expecting cm:description label to be " + LABEL_DESCRIPTION, assertEquals("Expecting cm:description label to be " + LABEL_DESCRIPTION,
LABEL_DESCRIPTION, descField.getLabel()); LABEL_DESCRIPTION, descField.getLabel());
assertEquals("Expecting mimetype label to be " + LABEL_MIMETYPE,
LABEL_MIMETYPE, mimetypeField.getLabel());
assertEquals("Expecting encoding label to be " + LABEL_ENCODING,
LABEL_ENCODING, encodingField.getLabel());
assertEquals("Expecting size label to be " + LABEL_SIZE,
LABEL_SIZE, sizeField.getLabel());
assertEquals("Expecting cm:originator label to be " + LABEL_ORIGINATOR, assertEquals("Expecting cm:originator label to be " + LABEL_ORIGINATOR,
LABEL_ORIGINATOR, originatorField.getLabel()); LABEL_ORIGINATOR, originatorField.getLabel());
assertEquals("Expecting cm:addressee label to be " + LABEL_ADDRESSEE, assertEquals("Expecting cm:addressee label to be " + LABEL_ADDRESSEE,
@@ -299,11 +331,14 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
assertNotNull("Expecting field data", fieldData); assertNotNull("Expecting field data", fieldData);
assertEquals(VALUE_TITLE, fieldData.get("prop:cm:title").getValue()); assertEquals(VALUE_TITLE, fieldData.get("prop:cm:title").getValue());
assertEquals(VALUE_DESCRIPTION, fieldData.get("prop:cm:description").getValue()); assertEquals(VALUE_DESCRIPTION, fieldData.get("prop:cm:description").getValue());
assertEquals(VALUE_MIMETYPE, fieldData.get("prop:mimetype").getValue());
assertEquals(VALUE_ENCODING, fieldData.get("prop:encoding").getValue());
assertEquals(VALUE_ORIGINATOR, fieldData.get("prop:cm:originator").getValue()); assertEquals(VALUE_ORIGINATOR, fieldData.get("prop:cm:originator").getValue());
assertEquals(VALUE_ADDRESSEE, fieldData.get("prop:cm:addressee").getValue()); assertEquals(VALUE_ADDRESSEE, fieldData.get("prop:cm:addressee").getValue());
assertEquals(VALUE_ADDRESSEES1, fieldData.get("prop:cm:addressees_0").getValue()); assertEquals(VALUE_ADDRESSEES1, fieldData.get("prop:cm:addressees_0").getValue());
assertEquals(VALUE_ADDRESSEES2, fieldData.get("prop:cm:addressees_1").getValue()); assertEquals(VALUE_ADDRESSEES2, fieldData.get("prop:cm:addressees_1").getValue());
assertEquals(VALUE_SUBJECT, fieldData.get("prop:cm:subjectline").getValue()); assertEquals(VALUE_SUBJECT, fieldData.get("prop:cm:subjectline").getValue());
assertTrue("Expecting size to be > 0", ((Long)fieldData.get("prop:size").getValue()).longValue() > 0);
Calendar calTestValue = Calendar.getInstance(); Calendar calTestValue = Calendar.getInstance();
calTestValue.setTime(VALUE_SENT_DATE); calTestValue.setTime(VALUE_SENT_DATE);
@@ -316,6 +351,98 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
assertEquals(this.associatedDoc.toString(), targets.get(0)); assertEquals(this.associatedDoc.toString(), targets.get(0));
} }
public void testSaveForm() throws Exception
{
// create FormData object containing the values to update
FormData data = new FormData();
// update the name
String newName = "new-" + this.documentName;
data.addData("prop:cm:name", newName);
// update the title property
String newTitle = "This is the new title property";
data.addData("prop:cm:title", newTitle);
// update the mimetype
String newMimetype = MimetypeMap.MIMETYPE_HTML;
data.addData("prop:mimetype", newMimetype);
// update the originator
String newOriginator = "jane@example.com";
data.addData("prop:cm:originator", newOriginator);
// set the date to null (using an empty string)
data.addData("prop:cm:sentdate", "");
// try and update non-existent properties (make sure there are no exceptions)
data.addData("prop:cm:wrong", "This should not be persisted");
data.addData("cm:wrong", "This should not be persisted");
// persist the data
this.formService.saveForm(this.document.toString(), data);
// retrieve the data directly from the node service to ensure its been changed
Map<QName, Serializable> updatedProps = this.nodeService.getProperties(this.document);
String updatedName = (String)updatedProps.get(ContentModel.PROP_NAME);
String updatedTitle = (String)updatedProps.get(ContentModel.PROP_TITLE);
String updatedOriginator = (String)updatedProps.get(ContentModel.PROP_ORIGINATOR);
String wrong = (String)updatedProps.get(QName.createQName("cm", "wrong", this.namespaceService));
Date sentDate = (Date)updatedProps.get(ContentModel.PROP_SENTDATE);
assertEquals(newName, updatedName);
assertEquals(newTitle, updatedTitle);
assertEquals(newOriginator, updatedOriginator);
assertNull("Expecting sentdate to be null", sentDate);
assertNull("Expecting my:wrong to be null", wrong);
// check mimetype was updated
ContentData contentData = (ContentData)updatedProps.get(ContentModel.PROP_CONTENT);
if (contentData != null)
{
String updatedMimetype = contentData.getMimetype();
assertEquals(MimetypeMap.MIMETYPE_HTML, updatedMimetype);
}
}
public void testNoForm() throws Exception
{
// test that a form can not be retrieved for a non-existent item
try
{
this.formService.getForm("Invalid Item");
fail("Expecting getForm for 'Invalid Item' to fail");
}
catch (Exception e)
{
// expected
}
String missingNode = this.document.toString().replace("-", "x");
Form form = this.formService.getForm(missingNode);
assertNull("Expecting getForm for a missing node to be null", form);
// test that a form can not be saved for a non-existent item
try
{
this.formService.saveForm("Invalid Item", new FormData());
fail("Expecting saveForm for 'Invalid Item' to fail");
}
catch (Exception e)
{
// expected
}
try
{
this.formService.saveForm(missingNode, new FormData());
fail("Expecting saveForm for a missing node to fail");
}
catch (Exception e)
{
// expected
}
}
public void off_testSaveUpdatedForm() throws Exception public void off_testSaveUpdatedForm() throws Exception
{ {
fail("Form persistence not yet impl'd."); fail("Form persistence not yet impl'd.");

View File

@@ -32,6 +32,8 @@ import java.util.Map;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.alfresco.i18n.I18NUtil;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.forms.AssociationFieldDefinition; import org.alfresco.repo.forms.AssociationFieldDefinition;
import org.alfresco.repo.forms.Form; import org.alfresco.repo.forms.Form;
import org.alfresco.repo.forms.FormData; import org.alfresco.repo.forms.FormData;
@@ -43,10 +45,15 @@ import org.alfresco.repo.forms.PropertyFieldDefinition.FieldConstraint;
import org.alfresco.service.cmr.dictionary.AssociationDefinition; import org.alfresco.service.cmr.dictionary.AssociationDefinition;
import org.alfresco.service.cmr.dictionary.Constraint; import org.alfresco.service.cmr.dictionary.Constraint;
import org.alfresco.service.cmr.dictionary.ConstraintDefinition; import org.alfresco.service.cmr.dictionary.ConstraintDefinition;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.dictionary.PropertyDefinition; import org.alfresco.service.cmr.dictionary.PropertyDefinition;
import org.alfresco.service.cmr.dictionary.TypeDefinition; import org.alfresco.service.cmr.dictionary.TypeDefinition;
import org.alfresco.service.cmr.model.FileExistsException;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileNotFoundException;
import org.alfresco.service.cmr.repository.AssociationRef; import org.alfresco.service.cmr.repository.AssociationRef;
import org.alfresco.service.cmr.repository.ContentData;
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.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.NamespaceService;
@@ -70,17 +77,43 @@ public class NodeHandler extends AbstractHandler
protected static final String PROP_PREFIX = "prop:"; protected static final String PROP_PREFIX = "prop:";
protected static final String ASSOC_PREFIX = "assoc:"; protected static final String ASSOC_PREFIX = "assoc:";
protected static final String TRANSIENT_MIMETYPE = "mimetype";
protected static final String TRANSIENT_SIZE = "size";
protected static final String TRANSIENT_ENCODING = "encoding";
protected static final String MSG_MIMETYPE_LABEL = "form_service.mimetype.label";
protected static final String MSG_MIMETYPE_DESC = "form_service.mimetype.description";
protected static final String MSG_ENCODING_LABEL = "form_service.encoding.label";
protected static final String MSG_ENCODING_DESC = "form_service.encoding.description";
protected static final String MSG_SIZE_LABEL = "form_service.size.label";
protected static final String MSG_SIZE_DESC = "form_service.size.description";
/** Services */ /** Services */
protected NodeService nodeService; protected NodeService nodeService;
protected FileFolderService fileFolderService;
protected DictionaryService dictionaryService; protected DictionaryService dictionaryService;
protected NamespaceService namespaceService; protected NamespaceService namespaceService;
/** /**
* A regular expression which can be used to match property/association names. * A regular expression which can be used to match property names.
* These names will look like <code>"prop:cm:name"</code>. * These names will look like <code>"prop:cm:name"</code>.
* The pattern can also be used to extract the "cm" and the "name" parts. * The pattern can also be used to extract the "cm" and the "name" parts.
*/ */
private Pattern propertyNamePattern = Pattern.compile("prop:(.*){1}?:(.*){1}?"); protected Pattern propertyNamePattern = Pattern.compile(PROP_PREFIX + "(.*){1}?:(.*){1}?");
/**
* A regular expression which can be used to match tranisent property names.
* These names will look like <code>"prop:name"</code>.
* The pattern can also be used to extract the "name" part.
*/
protected Pattern transientPropertyPattern = Pattern.compile(PROP_PREFIX + "(.*){1}?");
/**
* A regular expression which can be used to match association names.
* These names will look like <code>"assoc:cm:references"</code>.
* The pattern can also be used to extract the "cm" and the "name" parts.
*/
protected Pattern associationNamePattern = Pattern.compile(ASSOC_PREFIX + "(.*){1}?:(.*){1}?");
/** /**
* Sets the node service * Sets the node service
@@ -92,6 +125,16 @@ public class NodeHandler extends AbstractHandler
this.nodeService = nodeService; this.nodeService = nodeService;
} }
/**
* Sets the file folder service
*
* @param fileFolderService The FileFolderService instance
*/
public void setFileFolderService(FileFolderService fileFolderService)
{
this.fileFolderService = fileFolderService;
}
/** /**
* Sets the data dictionary service * Sets the data dictionary service
* *
@@ -123,12 +166,8 @@ public class NodeHandler extends AbstractHandler
// cast to the expected NodeRef representation // cast to the expected NodeRef representation
NodeRef nodeRef = (NodeRef)item; NodeRef nodeRef = (NodeRef)item;
// set the type // generate the form for the node
QName type = this.nodeService.getType(nodeRef); generateNode(nodeRef, form);
form.setType(type.toPrefixString(this.namespaceService));
// setup field definitions and data
setupFields(nodeRef, form);
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
logger.debug("Returning form: " + form); logger.debug("Returning form: " + form);
@@ -141,70 +180,93 @@ public class NodeHandler extends AbstractHandler
*/ */
public void handlePersist(Object item, FormData data) public void handlePersist(Object item, FormData data)
{ {
if (logger.isDebugEnabled())
logger.debug("Persisting form for: " + item);
// cast to the expected NodeRef representation // cast to the expected NodeRef representation
NodeRef nodeRef = (NodeRef)item; NodeRef nodeRef = (NodeRef)item;
Map<QName, Serializable> submittedProperties = extractSubmittedPropsFrom(data); // persist the node
persistNode(nodeRef, data);
// The call to addProperties changes the repo values of those properties
// included in the Map, but leaves any other property values unchanged.
//
// Compare setProperties(..), which causes the deletion of properties that
// are not included in the Map.
this.nodeService.addProperties(nodeRef, submittedProperties);
}
private Map<QName, Serializable> extractSubmittedPropsFrom(FormData data)
{
Map<QName, Serializable> result = new HashMap<QName, Serializable>();
for (String dataKey : data.getData().keySet())
{
FieldData nextFieldData = data.getData().get(dataKey);
if (nextFieldData.isFile())
{
//TODO Implement support for file-based submits.
}
else
{
String nextDataName = nextFieldData.getName();
Matcher m = this.propertyNamePattern.matcher(nextDataName);
// Only "prop:" properties are handled here
if (m.matches())
{
String qNamePrefix = m.group(1);
String localName = m.group(2);
QName fullQName = QName.createQName(qNamePrefix, localName, namespaceService);
// These values are all Strings. The repo does most of the data
// conversion work for us.
Object nextDataObject = nextFieldData.getValue();
// This cast should be safe as all dataObjects are Strings.
result.put(fullQName, (Serializable)nextDataObject);
}
//TODO Implement support for "assoc:" properties.
else
{
if (logger.isWarnEnabled())
{
StringBuilder msg = new StringBuilder();
msg.append("Field ").append(nextDataName).append(" has not been recognised.");
logger.warn(msg.toString());
}
}
}
}
return result;
} }
/** /**
* Sets up the field definitions for the form * Sets up the Form object for the given NodeRef
*
* @param nodeRef The NodeRef to generate a Form for
* @param form The Form instance to populate
*/
protected void generateNode(NodeRef nodeRef, Form form)
{
// set the type
QName type = this.nodeService.getType(nodeRef);
form.setType(type.toPrefixString(this.namespaceService));
// setup field definitions and data
FormData formData = new FormData();
generatePropertyFields(nodeRef, form, formData);
generateAssociationFields(nodeRef, form, formData);
generateTransientFields(nodeRef, form, formData);
form.setFormData(formData);
}
/**
* 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, PropertyDefinition> propDefs = typeDef.getProperties();
Map<QName, Serializable> propsToPersist = new HashMap<QName, Serializable>(data.getData().size());
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))
{
// TODO: process any associations present
}
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);
// TODO: persist the associations
}
/**
* Sets up the field definitions for the node's properties.
*
* @param nodeRef The NodeRef of the node being setup
* @param form The Form instance to populate
* @param formData The FormData instance to populate
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private void setupFields(NodeRef nodeRef, Form form) protected void generatePropertyFields(NodeRef nodeRef, Form form, FormData formData)
{ {
FormData formData = new 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);
TypeDefinition typeDef = this.dictionaryService.getAnonymousType( TypeDefinition typeDef = this.dictionaryService.getAnonymousType(
@@ -283,7 +345,18 @@ public class NodeHandler extends AbstractHandler
} }
} }
} }
}
/**
* Sets up the field definitions for the node's associations.
*
* @param nodeRef The NodeRef of the node being setup
* @param form The Form instance to populate
* @param formData The FormData instance to populate
*/
@SuppressWarnings("unchecked")
protected void generateAssociationFields(NodeRef nodeRef, Form form, FormData formData)
{
// 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);
@@ -352,8 +425,294 @@ public class NodeHandler extends AbstractHandler
} }
// TODO: Add source association definitions and data // TODO: Add source association definitions and data
}
// set the form data /**
form.setFormData(formData); * 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
* @param formData The FormData instance to populate
*/
protected void generateTransientFields(NodeRef nodeRef, Form form, FormData formData)
{
// 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
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()));
}
}
}
/**
* Processes the given field data for persistence as a property.
*
* @param nodeRef The NodeRef to persist the properties on
* @param propDefs Map of PropertyDefinition's for the node being persisted
* @param fieldData Data to persist for the property
* @param propsToPersist Map of properties to be persisted
*/
protected void processPropertyPersist(NodeRef nodeRef, Map<QName, PropertyDefinition> propDefs,
FieldData fieldData, Map<QName, Serializable> propsToPersist)
{
if (logger.isDebugEnabled())
logger.debug("Processing field " + fieldData + " for property persistence");
// match and extract the prefix and name parts
Matcher m = this.propertyNamePattern.matcher(fieldData.getName());
if (m.matches())
{
String qNamePrefix = m.group(1);
String localName = m.group(2);
QName fullQName = QName.createQName(qNamePrefix, localName, namespaceService);
// ensure that the property being persisted is defined in the model
PropertyDefinition propDef = propDefs.get(fullQName);
if (propDef != null)
{
// look for properties that have well known handling requirements
if (fullQName.equals(ContentModel.PROP_NAME))
{
processNamePropertyPersist(nodeRef, fieldData);
}
else if (fullQName.equals(ContentModel.PROP_TITLE))
{
processTitlePropertyPersist(nodeRef, fieldData, propsToPersist);
}
else if (fullQName.equals(ContentModel.PROP_DESCRIPTION))
{
processDescriptionPropertyPersist(nodeRef, fieldData, propsToPersist);
}
else if (fullQName.equals(ContentModel.PROP_AUTHOR))
{
processAuthorPropertyPersist(nodeRef, fieldData, propsToPersist);
}
else
{
Object value = fieldData.getValue();
// before persisting check data type of property, if it's numerical
// or a date ensure empty strings are changed to null and convert
// locale strings to locale objects
if ((value instanceof String) && ((String)value).length() == 0)
{
if (propDef.getDataType().getName().equals(DataTypeDefinition.DOUBLE) ||
propDef.getDataType().getName().equals(DataTypeDefinition.FLOAT) ||
propDef.getDataType().getName().equals(DataTypeDefinition.INT) ||
propDef.getDataType().getName().equals(DataTypeDefinition.LONG) ||
propDef.getDataType().getName().equals(DataTypeDefinition.DATE) ||
propDef.getDataType().getName().equals(DataTypeDefinition.DATETIME))
{
value = null;
}
}
else if (propDef.getDataType().getName().equals(DataTypeDefinition.LOCALE))
{
value = I18NUtil.parseLocale((String)value);
}
// add the property to the map
propsToPersist.put(fullQName, (Serializable)value);
}
}
else if (logger.isWarnEnabled())
{
logger.warn("Ignoring field '" + fieldData.getName() + "' as a property definition can not be found");
}
}
else
{
// the field is potentially a well know transient property
// check for the ones we know about, anything else is ignored
Matcher tppm = this.transientPropertyPattern.matcher(fieldData.getName());
if (tppm.matches())
{
String fieldName = tppm.group(1);
if (fieldName.equals(TRANSIENT_MIMETYPE))
{
processMimetypePropertyPersist(nodeRef, fieldData, propsToPersist);
}
else if (fieldName.equals(TRANSIENT_ENCODING))
{
processEncodingPropertyPersist(nodeRef, fieldData, propsToPersist);
}
else if (fieldName.equals(TRANSIENT_SIZE))
{
// the size property is well known but should never be persisted
// as it is calculated so this is intentionally ignored
}
else if (logger.isWarnEnabled())
{
logger.warn("Ignoring unrecognised field '" + fieldData.getName() + "'");
}
}
else if (logger.isWarnEnabled())
{
logger.warn("Ignoring unrecognised field '" + fieldData.getName() + "'");
}
}
}
/**
* Persists the given field data as the name property
*
* @param nodeRef The NodeRef to update the name for
* @param fieldData The data representing the new name value
*/
protected void processNamePropertyPersist(NodeRef nodeRef, FieldData fieldData)
{
try
{
// if the name property changes the rename method of the file folder
// service should be called rather than updating the property directly
this.fileFolderService.rename(nodeRef, (String)fieldData.getValue());
}
catch (FileExistsException fee)
{
throw new FormException("Failed to persist field '" + fieldData.getName() + "'", fee);
}
catch (FileNotFoundException fnne)
{
throw new FormException("Failed to persist field '" + fieldData.getName() + "'", fnne);
}
}
/**
* Persists the given field data as the title property
*
* @param nodeRef The NodeRef to update the title for
* @param fieldData The data representing the new title value
* @param propsToPersist Map of properties to be persisted
*/
protected void processTitlePropertyPersist(NodeRef nodeRef, FieldData fieldData,
Map<QName, Serializable> propsToPersist)
{
// if a title property is present ensure the 'titled' aspect is applied
if (this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_TITLED) == false)
{
this.nodeService.addAspect(nodeRef, ContentModel.ASPECT_TITLED, null);
}
propsToPersist.put(ContentModel.PROP_TITLE, (String)fieldData.getValue());
}
/**
* Persists the given field data as the description property
*
* @param nodeRef The NodeRef to update the description for
* @param fieldData The data representing the new description value
* @param propsToPersist Map of properties to be persisted
*/
protected void processDescriptionPropertyPersist(NodeRef nodeRef, FieldData fieldData,
Map<QName, Serializable> propsToPersist)
{
// if a description property is present ensure the 'titled' aspect is applied
if (this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_TITLED) == false)
{
this.nodeService.addAspect(nodeRef, ContentModel.ASPECT_TITLED, null);
}
propsToPersist.put(ContentModel.PROP_DESCRIPTION, (String)fieldData.getValue());
}
/**
* Persists the given field data as the author property
*
* @param nodeRef The NodeRef to update the author for
* @param fieldData The data representing the new author value
* @param propsToPersist Map of properties to be persisted
*/
protected void processAuthorPropertyPersist(NodeRef nodeRef, FieldData fieldData,
Map<QName, Serializable> propsToPersist)
{
// if an author property is present ensure the 'author' aspect is applied
if (this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_AUTHOR) == false)
{
this.nodeService.addAspect(nodeRef, ContentModel.ASPECT_AUTHOR, null);
}
propsToPersist.put(ContentModel.PROP_AUTHOR, (String)fieldData.getValue());
}
/**
* Persists the given field data as the mimetype property
*
* @param nodeRef The NodeRef to update the mimetype for
* @param fieldData The data representing the new mimetype value
* @param propsToPersist Map of properties to be persisted
*/
protected void processMimetypePropertyPersist(NodeRef nodeRef, FieldData fieldData,
Map<QName, Serializable> propsToPersist)
{
ContentData contentData = (ContentData)propsToPersist.get(ContentModel.PROP_CONTENT);
if (contentData == null)
{
// content data has not been persisted yet so get it from the node
contentData = (ContentData)this.nodeService.getProperty(nodeRef, ContentModel.PROP_CONTENT);
}
if (contentData != null)
{
// update content data if we found the property
contentData = ContentData.setMimetype(contentData, (String)fieldData.getValue());
propsToPersist.put(ContentModel.PROP_CONTENT, contentData);
}
}
/**
* Persists the given field data as the encoding property
*
* @param nodeRef The NodeRef to update the encoding for
* @param fieldData The data representing the new encoding value
* @param propsToPersist Map of properties to be persisted
*/
protected void processEncodingPropertyPersist(NodeRef nodeRef, FieldData fieldData,
Map<QName, Serializable> propsToPersist)
{
ContentData contentData = (ContentData)propsToPersist.get(ContentModel.PROP_CONTENT);
if (contentData == null)
{
// content data has not been persisted yet so get it from the node
contentData = (ContentData)this.nodeService.getProperty(nodeRef, ContentModel.PROP_CONTENT);
}
if (contentData != null)
{
// update content data if we found the property
contentData = ContentData.setEncoding(contentData, (String)fieldData.getValue());
propsToPersist.put(ContentModel.PROP_CONTENT, contentData);
}
} }
} }

View File

@@ -22,7 +22,7 @@ function testGetFormForContentNode()
var fieldDefs = form.fieldDefinitions; var fieldDefs = form.fieldDefinitions;
test.assertNotNull(fieldDefs, "field definitions should not be null."); test.assertNotNull(fieldDefs, "field definitions should not be null.");
test.assertEquals(19, fieldDefs.length); test.assertEquals(22, fieldDefs.length);
var fieldDefnDataHash = form.fieldDefinitionData; var fieldDefnDataHash = form.fieldDefinitionData;