diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/action-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/action-context.xml
index dfc411ea07..cbdd79e0ec 100644
--- a/rm-server/config/alfresco/module/org_alfresco_module_rm/action-context.xml
+++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/action-context.xml
@@ -34,4 +34,12 @@
-->
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/messages/actions.properties b/rm-server/config/alfresco/module/org_alfresco_module_rm/messages/actions.properties
index c55856199e..ce4c5321a9 100644
--- a/rm-server/config/alfresco/module/org_alfresco_module_rm/messages/actions.properties
+++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/messages/actions.properties
@@ -34,4 +34,20 @@ rm.action.node-already-transfer=Node is already being transfered.
rm.action.node-not-transfer=Node is not a transfer object.
rm.action.undo-not-last=Can not undo cut off, because last disposition action was not cut off.
rm.action.records_only_undeclared=Only records can be undeclared.
-rm.action.event-not-undone=The event {0} can not be undone, because it is not defined on the disposition lifecycle.
\ No newline at end of file
+rm.action.event-not-undone=The event {0} can not be undone, because it is not defined on the disposition lifecycle.
+#
+# i18n for Rule Actions
+#
+# File record
+file-record.title=File record
+# FIXME!!!
+#file-record.description=The rule is applied to all items that...
+#file-record.destination-record-folder.display-label=File record
+# Create record
+create-record.title=Create record
+# FIXME!!!
+#create-record.description=The rule is applied to all items that...
+# Declare record
+declare-record.title=Declare record
+# FIXME!!!
+#declare-record.description=The rule is applied to all items that...
\ No newline at end of file
diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/dm/DeclareRecordAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/dm/DeclareRecordAction.java
new file mode 100644
index 0000000000..e63ac21907
--- /dev/null
+++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/dm/DeclareRecordAction.java
@@ -0,0 +1,256 @@
+package org.alfresco.module.org_alfresco_module_rm.action.dm;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.alfresco.error.AlfrescoRuntimeException;
+import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService;
+import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
+import org.alfresco.module.org_alfresco_module_rm.record.RecordService;
+import org.alfresco.repo.action.executer.ActionExecuterAbstractBase;
+import org.alfresco.repo.security.authentication.AuthenticationUtil;
+import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
+import org.alfresco.service.cmr.action.Action;
+import org.alfresco.service.cmr.action.ParameterDefinition;
+import org.alfresco.service.cmr.dictionary.AspectDefinition;
+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.repository.NodeRef;
+import org.alfresco.service.cmr.repository.NodeService;
+import org.alfresco.service.cmr.security.OwnableService;
+import org.alfresco.service.namespace.QName;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.extensions.surf.util.I18NUtil;
+
+public class DeclareRecordAction extends ActionExecuterAbstractBase implements RecordsManagementModel
+{
+ /** I18N */
+ private static final String MSG_NO_DECLARE_MAND_PROP = "rm.action.no-declare-mand-prop";
+
+ /** Logger */
+ private static Log logger = LogFactory.getLog(DeclareRecordAction.class);
+
+ /** Record service */
+ private RecordService recordService;
+
+ /** Record management service */
+ private RecordsManagementService recordsManagementService;
+
+ /** Node service */
+ private NodeService nodeService;
+
+ /** Ownable service **/
+ private OwnableService ownableService;
+
+ /** Dictionary service */
+ private DictionaryService dictionaryService;
+
+ /**
+ * @param recordService record service
+ */
+ public void setRecordService(RecordService recordService)
+ {
+ this.recordService = recordService;
+ }
+
+ /**
+ * @param recordsManagementService records management service
+ */
+ public void setRecordsManagementService(
+ RecordsManagementService recordsManagementService)
+ {
+ this.recordsManagementService = recordsManagementService;
+ }
+
+ /**
+ * @param nodeService node service
+ */
+ public void setNodeService(NodeService nodeService)
+ {
+ this.nodeService = nodeService;
+ }
+
+ /**
+ * @param ownableSerice ownable serice
+ */
+ public void setOwnableService(OwnableService ownableService)
+ {
+ this.ownableService = ownableService;
+ }
+
+ /**
+ * @param dictionaryService dictionary service
+ */
+ public void setDictionaryService(DictionaryService dictionaryService)
+ {
+ this.dictionaryService = dictionaryService;
+ }
+
+ @Override
+ protected void executeImpl(Action action, final NodeRef actionedUponNodeRef)
+ {
+ // skip everything if the actioned upon node reference is already a record
+ if (nodeService.hasAspect(actionedUponNodeRef, ASPECT_RECORD) == false)
+ {
+ // TODO we should use the file plan passed as a parameter
+ // grab the file plan
+ List filePlans = recordsManagementService.getFilePlans();
+ if (filePlans.size() == 1)
+ {
+ // TODO parameterise the action with the file plan
+ final NodeRef filePlan = filePlans.get(0);
+
+ // run record creation as system
+ AuthenticationUtil.runAsSystem(new RunAsWork()
+ {
+ @Override
+ public Void doWork() throws Exception
+ {
+ // create record from existing document
+ recordService.createRecordFromDocument(filePlan, actionedUponNodeRef);
+ return null;
+ }
+ });
+
+ // DEMO CODE
+ if (nodeService.getProperty(actionedUponNodeRef, PROP_ORIGINATOR) == null)
+ {
+ nodeService.setProperty(actionedUponNodeRef, PROP_ORIGINATOR, "Michelle Smith");
+ }
+ if (nodeService.getProperty(actionedUponNodeRef, PROP_ORIGINATING_ORGANIZATION) == null)
+ {
+ nodeService.setProperty(actionedUponNodeRef, PROP_ORIGINATING_ORGANIZATION, "Customer Service");
+ }
+ if (nodeService.getProperty(actionedUponNodeRef, PROP_PUBLICATION_DATE) == null)
+ {
+ nodeService.setProperty(actionedUponNodeRef, PROP_PUBLICATION_DATE, new Date());
+ }
+
+ if (recordService.isDeclared(actionedUponNodeRef) == false)
+ {
+ List missingProperties = new ArrayList(5);
+ // Aspect not already defined - check mandatory properties then add
+ if (mandatoryPropertiesSet(actionedUponNodeRef, missingProperties) == true)
+ {
+ // Add the declared aspect
+ Map declaredProps = new HashMap(2);
+ declaredProps.put(PROP_DECLARED_AT, new Date());
+ declaredProps.put(PROP_DECLARED_BY, AuthenticationUtil.getRunAsUser());
+ nodeService.addAspect(actionedUponNodeRef, ASPECT_DECLARED_RECORD, declaredProps);
+
+ // remove all owner related rights
+ ownableService.setOwner(actionedUponNodeRef, OwnableService.NO_OWNER);
+ }
+ else
+ {
+ throw new AlfrescoRuntimeException(buildMissingPropertiesErrorString(missingProperties));
+ }
+ }
+ }
+ else
+ {
+ throw new AlfrescoRuntimeException("Unable to find file plan.");
+ }
+ }
+ }
+
+ private String buildMissingPropertiesErrorString(List missingProperties)
+ {
+ StringBuilder builder = new StringBuilder(255);
+ builder.append(I18NUtil.getMessage(MSG_NO_DECLARE_MAND_PROP));
+ builder.append(" ");
+ for (String missingProperty : missingProperties)
+ {
+ builder.append(missingProperty)
+ .append(", ");
+ }
+ return builder.toString();
+ }
+
+ /**
+ * Helper method to check whether all the mandatory properties of the node have been set
+ *
+ * @param nodeRef
+ * node reference
+ * @return boolean true if all mandatory properties are set, false otherwise
+ */
+ private boolean mandatoryPropertiesSet(NodeRef nodeRef, List missingProperties)
+ {
+ boolean result = true;
+
+ Map nodeRefProps = nodeService.getProperties(nodeRef);
+
+ QName nodeRefType = nodeService.getType(nodeRef);
+
+ TypeDefinition typeDef = dictionaryService.getType(nodeRefType);
+ for (PropertyDefinition propDef : typeDef.getProperties().values())
+ {
+ if (propDef.isMandatory() == true)
+ {
+ if (nodeRefProps.get(propDef.getName()) == null)
+ {
+ logMissingProperty(propDef, missingProperties);
+
+ result = false;
+ break;
+ }
+ }
+ }
+
+ if (result != false)
+ {
+ Set aspects = this.nodeService.getAspects(nodeRef);
+ for (QName aspect : aspects)
+ {
+ AspectDefinition aspectDef = dictionaryService.getAspect(aspect);
+ for (PropertyDefinition propDef : aspectDef.getProperties().values())
+ {
+ if (propDef.isMandatory() == true)
+ {
+ if (nodeRefProps.get(propDef.getName()) == null)
+ {
+ logMissingProperty(propDef, missingProperties);
+
+ result = false;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Log information about missing properties.
+ *
+ * @param propDef property definition
+ * @param missingProperties missing properties
+ */
+ private void logMissingProperty(PropertyDefinition propDef, List missingProperties)
+ {
+ if (logger.isWarnEnabled())
+ {
+ StringBuilder msg = new StringBuilder();
+ msg.append("Mandatory property missing: ").append(propDef.getName());
+ logger.warn(msg.toString());
+ }
+ missingProperties.add(propDef.getName().toString());
+ }
+
+ @Override
+ protected void addParameterDefinitions(List paramList)
+ {
+ // TODO eventually we will need to pass in the file plan as a parameter
+ // TODO .. or the RM site
+ }
+
+}
\ No newline at end of file