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

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

View File

@@ -34,11 +34,15 @@ import java.util.List;
import java.util.Map;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.forms.AssociationFieldDefinition.Direction;
import org.alfresco.repo.forms.FormData.FieldData;
import org.alfresco.repo.jscript.ClasspathScriptLocation;
import org.alfresco.repo.forms.PropertyFieldDefinition.FieldConstraint;
import org.alfresco.repo.jscript.ClasspathScriptLocation;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.service.cmr.repository.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.ScriptLocation;
import org.alfresco.service.cmr.repository.ScriptService;
@@ -61,9 +65,11 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
private NamespaceService namespaceService;
private ScriptService scriptService;
private PersonService personService;
private ContentService contentService;
private NodeRef document;
private NodeRef associatedDoc;
private String documentName;
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";
@@ -72,11 +78,17 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
private static String VALUE_ADDRESSEES1 = "harry@example.com";
private static String VALUE_ADDRESSEES2 = "jane@example.com";
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 String LABEL_NAME = "Name";
private static String LABEL_TITLE = "Title";
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_ADDRESSEE = "Addressee";
private static String LABEL_ADDRESSEES = "Addressees";
@@ -100,6 +112,7 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
this.namespaceService = (NamespaceService)this.applicationContext.getBean("NamespaceService");
this.scriptService = (ScriptService)this.applicationContext.getBean("ScriptService");
this.personService = (PersonService)this.applicationContext.getBean("PersonService");
this.contentService = (ContentService)this.applicationContext.getBean("ContentService");
AuthenticationComponent authenticationComponent = (AuthenticationComponent) this.applicationContext
.getBean("authenticationComponent");
@@ -124,7 +137,8 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
// Create a node
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(
folder,
ContentModel.ASSOC_CONTAINS,
@@ -141,6 +155,12 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
ContentModel.TYPE_CONTENT,
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
Map<QName, Serializable> aspectProps = new HashMap<QName, Serializable>(2);
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.createAssociation(this.document, this.associatedDoc, ContentModel.ASSOC_REFERENCES);
setComplete();
endTransaction();
//setComplete();
//endTransaction();
}
private void createUser(String userName)
@@ -206,7 +226,7 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
// check the field definitions
Collection<FieldDefinition> fieldDefs = form.getFieldDefinitions();
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
// 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 titleField = (PropertyFieldDefinition)fieldDefMap.get("cm:title");
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 addresseeField = (PropertyFieldDefinition)fieldDefMap.get("cm:addressee");
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:title field", titleField);
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:addressee field", addresseeField);
assertNotNull("Expecting to find the cm:addressees field", addresseesField);
@@ -246,6 +272,12 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
LABEL_TITLE, titleField.getLabel());
assertEquals("Expecting cm:description label to be " + LABEL_DESCRIPTION,
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,
LABEL_ORIGINATOR, originatorField.getLabel());
assertEquals("Expecting cm:addressee label to be " + LABEL_ADDRESSEE,
@@ -299,11 +331,14 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
assertNotNull("Expecting field data", fieldData);
assertEquals(VALUE_TITLE, fieldData.get("prop:cm:title").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_ADDRESSEE, fieldData.get("prop:cm:addressee").getValue());
assertEquals(VALUE_ADDRESSEES1, fieldData.get("prop:cm:addressees_0").getValue());
assertEquals(VALUE_ADDRESSEES2, fieldData.get("prop:cm:addressees_1").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();
calTestValue.setTime(VALUE_SENT_DATE);
@@ -316,6 +351,98 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
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
{
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.Pattern;
import org.alfresco.i18n.I18NUtil;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.forms.AssociationFieldDefinition;
import org.alfresco.repo.forms.Form;
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.Constraint;
import org.alfresco.service.cmr.dictionary.ConstraintDefinition;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
import org.alfresco.service.cmr.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.ContentData;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
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 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 */
protected NodeService nodeService;
protected FileFolderService fileFolderService;
protected DictionaryService dictionaryService;
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>.
* 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
@@ -91,6 +124,16 @@ public class NodeHandler extends AbstractHandler
{
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
@@ -123,12 +166,8 @@ public class NodeHandler extends AbstractHandler
// cast to the expected NodeRef representation
NodeRef nodeRef = (NodeRef)item;
// set the type
QName type = this.nodeService.getType(nodeRef);
form.setType(type.toPrefixString(this.namespaceService));
// setup field definitions and data
setupFields(nodeRef, form);
// generate the form for the node
generateNode(nodeRef, form);
if (logger.isDebugEnabled())
logger.debug("Returning form: " + form);
@@ -141,70 +180,93 @@ public class NodeHandler extends AbstractHandler
*/
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;
Map<QName, Serializable> submittedProperties = extractSubmittedPropsFrom(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);
// persist the node
persistNode(nodeRef, data);
}
private Map<QName, Serializable> extractSubmittedPropsFrom(FormData data)
/**
* 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)
{
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;
// 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);
}
/**
* Sets up the field definitions for the form
* 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")
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
QName type = this.nodeService.getType(nodeRef);
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
List<AssociationRef> associations = this.nodeService.getTargetAssocs(nodeRef,
RegexQNamePattern.MATCH_ALL);
@@ -352,8 +425,294 @@ public class NodeHandler extends AbstractHandler
}
// TODO: Add source association definitions and data
}
/**
* 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");
// set the form data
form.setFormData(formData);
// 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;
test.assertNotNull(fieldDefs, "field definitions should not be null.");
test.assertEquals(19, fieldDefs.length);
test.assertEquals(22, fieldDefs.length);
var fieldDefnDataHash = form.fieldDefinitionData;