mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-07 17:49:17 +00:00
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:
@@ -3,6 +3,14 @@
|
||||
|
||||
<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 -->
|
||||
<bean id="FormService" class="org.springframework.aop.framework.ProxyFactoryBean">
|
||||
<property name="proxyInterfaces">
|
||||
@@ -76,6 +84,7 @@
|
||||
parent="baseFormHandler">
|
||||
<property name="handlerRegistry" ref="nodeHandlerRegistry" />
|
||||
<property name="nodeService" ref="NodeService" />
|
||||
<property name="fileFolderService" ref="FileFolderService" />
|
||||
<property name="dictionaryService" ref="DictionaryService" />
|
||||
<property name="namespaceService" ref="NamespaceService" />
|
||||
</bean>
|
||||
@@ -86,7 +95,6 @@
|
||||
parent="baseFormHandler" />
|
||||
-->
|
||||
|
||||
|
||||
<bean id="formServiceScript" parent="baseJavaScriptExtension" class="org.alfresco.repo.forms.script.ScriptFormService">
|
||||
<property name="extensionName">
|
||||
<value>formService</value>
|
||||
|
8
config/alfresco/messages/form-service.properties
Normal file
8
config/alfresco/messages/form-service.properties
Normal 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
|
@@ -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
|
||||
|
@@ -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.");
|
||||
|
@@ -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
|
||||
@@ -92,6 +125,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);
|
||||
}
|
||||
|
||||
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;
|
||||
// persist the node
|
||||
persistNode(nodeRef, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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")
|
||||
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
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
|
||||
|
Reference in New Issue
Block a user