From f288e47d8a4b4b75d8bf83c6858fa6c58807bb9b Mon Sep 17 00:00:00 2001 From: Roy Wetherall Date: Thu, 30 Aug 2012 07:07:00 +0000 Subject: [PATCH 1/9] Creating new branch from https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/HEAD git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/DEV/INPLACE@41025 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 From 7b9912626c3f5be0146d12785580c1799f297083 Mon Sep 17 00:00:00 2001 From: Roy Wetherall Date: Tue, 18 Sep 2012 02:15:16 +0000 Subject: [PATCH 2/9] RM: Transparent records management prototype WIP * Create Record service and refactor * Add 'createRecord' method that preserves origional location(s) of the content * Add content readers information to record on extended security aspect * Experimental dynamic authority * DM action to 'create' record * Behaviour and methods to create and get new record container git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/DEV/INPLACE@41707 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../org_alfresco_module_rm/action-context.xml | 28 + .../rm-capabilities-condition-context.xml | 1 + .../messages/actions.properties | 37 + .../model/recordsModel.xml | 29 + .../org_alfresco_module_rm/module-context.xml | 34 +- .../rm-action-context.xml | 2 + .../rm-model-context.xml | 21 +- .../rm-service-context.xml | 64 +- .../rm-webscript-context.xml | 835 +-- .../RecordsManagementService.java | 31 +- .../RecordsManagementServiceImpl.java | 71 +- .../RecordsManagementServiceRegistry.java | 8 + .../RecordsManagementServiceRegistryImpl.java | 11 +- .../action/RMActionExecuterAbstractBase.java | 9 + ...DispositionActionExecuterAbstractBase.java | 4 +- .../action/dm/CreateRecordAction.java | 148 + .../action/impl/DeclareRecordAction.java | 4 +- .../action/impl/SplitEmailAction.java | 4 +- .../action/impl/UndeclareRecordAction.java | 4 +- .../capability/RMSecurityCommon.java | 8 +- .../AbstractCapabilityCondition.java | 10 + .../declarative/DeclarativeCapability.java | 3 + .../DeclaredCapabilityCondition.java | 2 +- .../capability/impl/CreateCapability.java | 4 +- .../forms/RecordsManagementFormFilter.java | 10 + .../RecordsManagementNodeFormFilter.java | 4 +- .../identifier/IdentifierServiceImpl.java | 2 +- .../jscript/app/BaseEvaluator.java | 12 + .../jscript/app/JSONConversionComponent.java | 16 +- .../evaluator/SplitEmailActionEvaluator.java | 2 +- .../model/RecordsManagementModel.java | 8 + .../FilePlanComponentAspect.java | 3 +- .../model/behaviour/FilePlanType.java | 115 + .../RecordComponentIdentifierAspect.java | 3 +- .../{ => behaviour}/RecordContainerType.java | 3 +- .../{ => behaviour}/RecordCopyBehaviours.java | 3 +- .../RecordsManagementSearchBehaviour.java | 43 +- .../model/{ => behaviour}/RmSiteType.java | 3 +- .../{ => behaviour}/ScheduledAspect.java | 3 +- .../permission/OtherImpl.java | 370 -- .../RecordReadersDynamicAuthority.java | 130 + .../record/RecordService.java | 59 + .../record/RecordServiceImpl.java | 201 + .../script/BootstrapTestDataGet.java | 4 +- .../script/ExportPost.java | 12 +- .../script/RecordMetaDataAspectsGet.java | 15 +- .../script/TransferGet.java | 6 +- .../slingshot/RMSearchPropertiesGet.java | 34 +- .../RecordsManagementServiceImplTest.java | 42 +- .../service/VitalRecordServiceImplTest.java | 2 +- .../test/system/DOD5015SystemTest.java | 4582 ----------------- ...ecordsManagementServiceImplSystemTest.java | 812 --- .../test/util/BaseRMTestCase.java | 30 +- .../test/util/BaseRMWebScriptTestCase.java | 21 +- .../test/util/TestUtilities.java | 2 +- 55 files changed, 1632 insertions(+), 6292 deletions(-) create mode 100644 rm-server/config/alfresco/module/org_alfresco_module_rm/action-context.xml create mode 100644 rm-server/config/alfresco/module/org_alfresco_module_rm/messages/actions.properties create mode 100644 rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/dm/CreateRecordAction.java rename rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/{ => behaviour}/FilePlanComponentAspect.java (95%) create mode 100644 rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/behaviour/FilePlanType.java rename rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/{ => behaviour}/RecordComponentIdentifierAspect.java (95%) rename rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/{ => behaviour}/RecordContainerType.java (95%) rename rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/{ => behaviour}/RecordCopyBehaviours.java (95%) rename rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/{ => behaviour}/RecordsManagementSearchBehaviour.java (93%) rename rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/{ => behaviour}/RmSiteType.java (94%) rename rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/{ => behaviour}/ScheduledAspect.java (92%) delete mode 100644 rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/permission/OtherImpl.java create mode 100644 rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/permission/RecordReadersDynamicAuthority.java create mode 100644 rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordService.java create mode 100644 rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImpl.java delete mode 100644 rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/system/DOD5015SystemTest.java delete mode 100644 rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/system/RecordsManagementServiceImplSystemTest.java 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 new file mode 100644 index 0000000000..b308c58434 --- /dev/null +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/action-context.xml @@ -0,0 +1,28 @@ + + + + + + + + + + alfresco.module.org_alfresco_module_rm.messages.actions + + + + + + + + + + + + + {http://www.alfresco.org/model/content/1.0}content + + + + + \ No newline at end of file diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/capability/rm-capabilities-condition-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/capability/rm-capabilities-condition-context.xml index 3c8fc8f4e9..34b247c5f1 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/capability/rm-capabilities-condition-context.xml +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/capability/rm-capabilities-condition-context.xml @@ -8,6 +8,7 @@ + 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 new file mode 100644 index 0000000000..c55856199e --- /dev/null +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/messages/actions.properties @@ -0,0 +1,37 @@ +rm.action.not-defined=The records management action {0} has not been defined. +rm.action.no-implicit-noderef=Unable to execute the records management action, because the action {0} implementation does not provide an implicit nodeRef. +rm.action.record-not-declared=Unable to execute disposition action {0}, because the record is not declared. (actionedUponNodeRef={1}) +rm.action.expected-record-level=Unable to execute disposition action {0}, because disposition is expected at the record level and this node is not a record. (actionedUponNodeRef={1}) +rm.action.not-all-records-declared=Unable to execute disposition action {0}, because not all the records in the record are declared. (actionedUponNodeRef={1}) +rm.action.not-eligible=Unable to execute disposition action {0}, because the next disposition action on the record or record folder is not eligible. (actionedUponNodeRef={1}) +rm.action.no-disposition-instructions=Unable to find disposition instructions for node. Can not execute disposition action {0}. (nodeRef={1}) +rm.action.no-disposition-lisfecycle-set=Unable to execute disposition action {0}, because node does not have a disposition life-cycle set. (nodeRef={1}) +rm.action.next-disp-not-set=Unable to execute disposition action {0}, because the next disposition action is not set. (nodeRef={1}) +rm.action.not-next-disp=Unable to execute disposition action {0}, because this is not the next disposition action for this record or record folder. (nodeRef={1}) +rm.action.not-record-folder=Unable to execute disposition action {0}, because disposition is expected at the record folder level and this node is not a record folder. (nodeRef={1}) +rm.action.actioned-upon-not-record=Can not execute action {0}, because the actioned upon node is not a Record. (filePlanComponet={1}) +rm.action.custom-aspect-not-recognised=The custom type can not be applied, because is it not recognised. (customAspect={0}) +rm.action.close-record-folder-not-folder=Unable to close record folder, because the node is not a record folder. (nodeRef={0}) +rm.action.event-no-disp-lc=The event {0} can not be completed, because it is not defined on the disposition lifecycle. +rm.action.undeclared-only-records=Only records can be undeclared. (nodeRef={0}) +rm.action.no-declare-mand-prop=Can not declare record, because not all the records mandatory properties have been set. +rm.action.ghosted-prop-update=The content properties of a previously destroyed record can not be updated. +rm.action.valid-date-disp-asof=A valid date must be specified when setting the disposition action as of date. +rm.action.disp-asof-lifecycle-applied=It is invalid to edit the disposition as of date of a record or record folder which has a lifecycle applied. +rm.action.hold-edit-reason-none=Can not edit hold reason, because no reason has been given. +rm.action.hold-edit-type=Can not edit hold reason, because actioned upon node is not of type {0}. (nodeRef={1}) +rm.action.specify-avlid-date=Must specify a valid date when setting the review as of date. +rm.action.review-details-only=Can only edit the review details of vital records. +rm.action.freeze-no-reason=Can not freeze a record without a reason. +rm.action.freeze-only-records-folders=Can only freeze records or record folders. +rm.action.no-open-record-folder=Unable to open record folder, because node is not a record folder. (actionedUponNodeRef={0}) +rm.action.not-hold-type=Can not relinquish hold, because node is not of type {0}. (actionedUponNodeRef={1}) +rm.action.no-read-mime-message=Unable to read mime message, because {0}. +rm.action.email-declared=Can not split email, because record has already been declared. (actionedUponNodeRef={0}) +rm.action.email-not-record=Can no split email, because node is not a record. (actionedUponNodeRef={0}) +rm.action.email-create-child-assoc=Unable to create custom child association. +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 diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/model/recordsModel.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/model/recordsModel.xml index 0be4c0dee8..754d382efd 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/model/recordsModel.xml +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/model/recordsModel.xml @@ -88,6 +88,11 @@ rma:recordsManagementRoot + + + + New Records Container + rma:recordsManagementContainer @@ -618,6 +623,19 @@ true + + + New Records + + true + false + + + rma:newRecordsContainer + true + true + + @@ -824,6 +842,17 @@ rma:filePlanComponent + + + + + + + + d:text + true + + diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/module-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/module-context.xml index 8aabd4625d..139c502bb0 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/module-context.xml +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/module-context.xml @@ -46,10 +46,13 @@ + + + + @@ -222,26 +226,28 @@ - - - - - - - - - - - - + + + + + + + + + + + + diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-action-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-action-context.xml index 7ff02d14d0..391f3c6711 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-action-context.xml +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-action-context.xml @@ -24,6 +24,7 @@ + @@ -764,5 +765,6 @@ + \ No newline at end of file diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-model-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-model-context.xml index a882c2d8d1..337668a427 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-model-context.xml +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-model-context.xml @@ -39,20 +39,20 @@ @@ -60,7 +60,7 @@ @@ -68,14 +68,14 @@ @@ -83,7 +83,7 @@ @@ -100,5 +100,12 @@ + + + + + \ No newline at end of file diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml index 2711f6edbd..4a921877a2 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml @@ -89,9 +89,9 @@ org.alfresco.module.org_alfresco_module_rm.RecordsManagementService.isRecordFolderClosed=RM.Read.0 org.alfresco.module.org_alfresco_module_rm.RecordsManagementService.createRecordFolder=RM.Write.0 org.alfresco.module.org_alfresco_module_rm.RecordsManagementService.getRecords=RM.Read.0,AFTER_RM.FilterNode + org.alfresco.module.org_alfresco_module_rm.RecordsManagementService.getRecordFolders=RM.Read.0,AFTER_RM.FilterNode org.alfresco.module.org_alfresco_module_rm.RecordsManagementService.getRecordMetaDataAspects=RM_ALLOW - org.alfresco.module.org_alfresco_module_rm.RecordsManagementService.getRecordFolders=RM.Read.0,AFTER_RM.FilterNode - org.alfresco.module.org_alfresco_module_rm.RecordsManagementService.isRecordDeclared=RM.Read.0 + org.alfresco.module.org_alfresco_module_rm.RecordsManagementService.isRecordDeclared=RM.Read.0 org.alfresco.module.org_alfresco_module_rm.RecordsManagementService.*=RM_DENY ]]> @@ -787,6 +787,66 @@ + + + + + + + + + + + + + org.alfresco.module.org_alfresco_module_rm.record.RecordService + + + + + + + + + + + + + + + + + + + + ${server.transaction.mode.default} + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - + + + + + - - - - - - - - - - + + + + + + + - - - - + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + - - - - + + + + - - - - - - - - - + + + + - - - - + + + + - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementService.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementService.java index 7776375050..cf6fe257c5 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementService.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementService.java @@ -23,6 +23,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import org.alfresco.module.org_alfresco_module_rm.record.RecordService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.QName; @@ -446,15 +447,6 @@ public interface RecordsManagementService // TODO move? copy? link? - /********** Record methods **********/ - - /** - * Get a list of all the record meta-data aspects - * - * @return {@link Set}<{@link QName}> list of record meta-data aspects - */ - Set getRecordMetaDataAspects(); - /** * Get all the record folders that a record is filed into. * @@ -463,12 +455,27 @@ public interface RecordsManagementService */ // TODO rename to List getParentRecordFolders(NodeRef record); List getRecordFolders(NodeRef record); - + + /********** Deprecated **********/ + + /** + * Get a list of all the record meta-data aspects + * + * @return {@link Set}<{@link QName}> list of record meta-data aspects + * + * @deprecated As of 2.1, replaced by {@link RecordService#getRecordMetaDataAspects()} + */ + @Deprecated + Set getRecordMetaDataAspects(); + /** * Indicates whether the record is declared - * + * * @param nodeRef node reference (record) * @return boolean true if record is declared, false otherwise + * + * @deprecated As of 2.1, replaced by {@link RecordsService#isDeclared()} */ - boolean isRecordDeclared(NodeRef nodeRef); + @Deprecated + boolean isRecordDeclared(NodeRef nodeRef); } diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementServiceImpl.java index 8a25859fdd..6c09f8bea4 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementServiceImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementServiceImpl.java @@ -20,7 +20,6 @@ package org.alfresco.module.org_alfresco_module_rm; import java.io.Serializable; import java.util.ArrayList; -import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; @@ -38,7 +37,6 @@ import org.alfresco.repo.domain.node.NodeDAO; import org.alfresco.repo.policy.JavaBehaviour; import org.alfresco.repo.policy.PolicyComponent; import org.alfresco.repo.policy.Behaviour.NotificationFrequency; -import org.alfresco.service.cmr.dictionary.AspectDefinition; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; @@ -77,7 +75,6 @@ public class RecordsManagementServiceImpl implements RecordsManagementService, private final static String MSG_PARENT_RECORD_FOLDER_ROOT = "rm.service.parent-record-folder-root"; private final static String MSG_PARENT_RECORD_FOLDER_TYPE = "rm.service.parent-record-folder-type"; private final static String MSG_RECORD_FOLDER_TYPE = "rm.service.record-folder-type"; - private final static String MSG_NOT_RECORD = "rm.service.not-record"; /** Store that the RM roots are contained within */ @SuppressWarnings("unused") @@ -105,9 +102,6 @@ public class RecordsManagementServiceImpl implements RecordsManagementService, /** Well-known location of the scripts folder. */ private NodeRef scriptsFolderNodeRef = new NodeRef("workspace", "SpacesStore", "rm_scripts"); - /** List of available record meta-data aspects */ - private Set recordMetaDataAspects; - /** Java behaviour */ private JavaBehaviour onChangeToDispositionActionDefinition; @@ -253,7 +247,7 @@ public class RecordsManagementServiceImpl implements RecordsManagementService, throw new AlfrescoRuntimeException("Unable to complete operation, because only content can be filed within a record folder."); } } - } + } /** * On add content to container @@ -1001,7 +995,7 @@ public class RecordsManagementServiceImpl implements RecordsManagementService, List records = getRecords(recordFolder); for (NodeRef record : records) { - if (isRecordDeclared(record) == false) + if (serviceRegistry.getRecordService().isDeclared(record) == false) { result = false; break; @@ -1120,31 +1114,6 @@ public class RecordsManagementServiceImpl implements RecordsManagementService, return createRecordFolder(parent, name, type, null); } - /** - * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementService#getRecordMetaDataAspects() - */ - public Set getRecordMetaDataAspects() - { - if (recordMetaDataAspects == null) - { - recordMetaDataAspects = new HashSet(7); - Collection aspects = dictionaryService.getAllAspects(); - for (QName aspect : aspects) - { - AspectDefinition def = dictionaryService.getAspect(aspect); - if (def != null) - { - QName parent = def.getParentName(); - if (parent != null && ASPECT_RECORD_META_DATA.equals(parent) == true) - { - recordMetaDataAspects.add(aspect); - } - } - } - } - return recordMetaDataAspects; - } - /** * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementService#getRecords(org.alfresco.service.cmr.repository.NodeRef) */ @@ -1164,21 +1133,7 @@ public class RecordsManagementServiceImpl implements RecordsManagementService, } } return result; - } - - /** - * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementService#isRecord(org.alfresco.service.cmr.repository.NodeRef, boolean) - */ - public boolean isRecordDeclared(NodeRef record) - { - if (isRecord(record) == false) - { - throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_NOT_RECORD, record.toString())); - } - return (this.nodeService.hasAspect(record, ASPECT_DECLARED_RECORD)); - } - - + } /** * This method examines the old and new property sets and for those properties which @@ -1271,4 +1226,24 @@ public class RecordsManagementServiceImpl implements RecordsManagementService, return result; } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementService#getRecordMetaDataAspects() + */ + @Override + @Deprecated + public Set getRecordMetaDataAspects() + { + return serviceRegistry.getRecordService().getRecordMetaDataAspects(); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementService#isRecordDeclared(org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + @Deprecated + public boolean isRecordDeclared(NodeRef nodeRef) + { + return serviceRegistry.getRecordService().isDeclared(nodeRef); + } } diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementServiceRegistry.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementServiceRegistry.java index ae6ba63928..551d1d406c 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementServiceRegistry.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementServiceRegistry.java @@ -23,6 +23,7 @@ import org.alfresco.module.org_alfresco_module_rm.audit.RecordsManagementAuditSe import org.alfresco.module.org_alfresco_module_rm.capability.CapabilityService; import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService; import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEventService; +import org.alfresco.module.org_alfresco_module_rm.record.RecordService; import org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService; import org.alfresco.service.NotAuditable; import org.alfresco.service.ServiceRegistry; @@ -45,6 +46,7 @@ public interface RecordsManagementServiceRegistry extends ServiceRegistry static final QName RECORDS_MANAGEMENT_SECURITY_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "RecordsManagementSecurityService"); static final QName RECORDS_MANAGEMENT_AUDIT_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "RecordsManagementAuditService"); static final QName CAPABILITY_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "CapabilityService"); + static final QName RECORD_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "RecordService"); /** * @return records management service @@ -52,6 +54,12 @@ public interface RecordsManagementServiceRegistry extends ServiceRegistry @NotAuditable RecordsManagementService getRecordsManagementService(); + /** + * @return record service + */ + @NotAuditable + RecordService getRecordService(); + /** * @return disposition service */ diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementServiceRegistryImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementServiceRegistryImpl.java index b27d6c86fc..d340b0190f 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementServiceRegistryImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementServiceRegistryImpl.java @@ -23,6 +23,7 @@ import org.alfresco.module.org_alfresco_module_rm.audit.RecordsManagementAuditSe import org.alfresco.module.org_alfresco_module_rm.capability.CapabilityService; import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService; import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEventService; +import org.alfresco.module.org_alfresco_module_rm.record.RecordService; import org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService; import org.alfresco.repo.service.ServiceDescriptorRegistry; @@ -65,6 +66,14 @@ public class RecordsManagementServiceRegistryImpl extends ServiceDescriptorRegis { return (RecordsManagementService)getService(RECORDS_MANAGEMENT_SERVICE); } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementServiceRegistry#getRecordService() + */ + public RecordService getRecordService() + { + return (RecordService)getService(RECORD_SERVICE); + } /** * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementServiceRegistry#getRecordsManagementSecurityService() @@ -74,7 +83,7 @@ public class RecordsManagementServiceRegistryImpl extends ServiceDescriptorRegis return (RecordsManagementSecurityService)getService(RECORDS_MANAGEMENT_SECURITY_SERVICE); } - /* + /** * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementServiceRegistry#getRecordsManagementAuditService() */ public RecordsManagementAuditService getRecordsManagementAuditService() diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/RMActionExecuterAbstractBase.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/RMActionExecuterAbstractBase.java index 6623bf9dc0..c76ca0a65e 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/RMActionExecuterAbstractBase.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/RMActionExecuterAbstractBase.java @@ -41,6 +41,7 @@ import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEvent; import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEventService; import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEventType; import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.module.org_alfresco_module_rm.record.RecordService; import org.alfresco.module.org_alfresco_module_rm.vital.VitalRecordService; import org.alfresco.repo.action.executer.ActionExecuterAbstractBase; import org.alfresco.service.cmr.action.Action; @@ -99,6 +100,9 @@ public abstract class RMActionExecuterAbstractBase extends ActionExecuterAbstra /** Records management service */ protected RecordsManagementService recordsManagementService; + /** Record service */ + protected RecordService recordService; + /** Disposition service */ protected DispositionService dispositionService; @@ -193,6 +197,11 @@ public abstract class RMActionExecuterAbstractBase extends ActionExecuterAbstra this.recordsManagementService = recordsManagementService; } + public void setRecordService(RecordService recordService) + { + this.recordService = recordService; + } + /** * Set the disposition service */ diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/RMDispositionActionExecuterAbstractBase.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/RMDispositionActionExecuterAbstractBase.java index 72bd5a7861..228d0e6f84 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/RMDispositionActionExecuterAbstractBase.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/RMDispositionActionExecuterAbstractBase.java @@ -100,7 +100,7 @@ public abstract class RMDispositionActionExecuterAbstractBase extends RMActionEx if (this.recordsManagementService.isRecord(actionedUponNodeRef) == true) { // Can only execute disposition action on record if declared - if (this.recordsManagementService.isRecordDeclared(actionedUponNodeRef) == true) + if (recordService.isDeclared(actionedUponNodeRef) == true) { // Indicate that the disposition action is underway this.nodeService.setProperty(nextDispositionActionNodeRef, PROP_DISPOSITION_ACTION_STARTED_AT, new Date()); @@ -315,7 +315,7 @@ public abstract class RMDispositionActionExecuterAbstractBase extends RMActionEx if (this.recordsManagementService.isRecord(filePlanComponent) == true) { // Can only execute disposition action on record if declared - if (this.recordsManagementService.isRecordDeclared(filePlanComponent) == true) + if (recordService.isDeclared(filePlanComponent) == true) { return true; } diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/dm/CreateRecordAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/dm/CreateRecordAction.java new file mode 100644 index 0000000000..be3d6570f9 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/dm/CreateRecordAction.java @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.action.dm; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; +import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.module.org_alfresco_module_rm.permission.RecordReadersDynamicAuthority; +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.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.namespace.QName; + +/** + * Creates a new record from an existing content object. + * + * Note: This is a 'normal' dm action, rather than a records management action. + * + * @author Roy Wetherall + */ +public class CreateRecordAction extends ActionExecuterAbstractBase + implements RecordsManagementModel +{ + private RecordsManagementService recordsManagementService; + + private RecordService recordService; + + private PermissionService permissionService; + + private NodeService nodeService; + + public void setRecordsManagementService(RecordsManagementService recordsManagementService) + { + this.recordsManagementService = recordsManagementService; + } + + public void setRecordService(RecordService recordService) + { + this.recordService = recordService; + } + + public void setPermissionService(PermissionService permissionService) + { + this.permissionService = permissionService; + } + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + @Override + protected void executeImpl(Action action, final NodeRef actionedUponNodeRef) + { + // TODO we should use the file plan passed as a parameter + // grab the file plan + List filePlans = recordsManagementService.getFilePlans(); + if (filePlans.size() == 1) + { + final NodeRef filePlan = filePlans.get(0); + + AuthenticationUtil.runAsSystem(new RunAsWork() + { + @Override + public Void doWork() throws Exception + { + // get the documents readers + Long aclId = nodeService.getNodeAclId(actionedUponNodeRef); + Set readers = permissionService.getReaders(aclId); + + // get the documents primary parent assoc + ChildAssociationRef parentAssoc = nodeService.getPrimaryParent(actionedUponNodeRef); + + /// get the new record container for the file plan + NodeRef newRecordContainer = recordService.getNewRecordContainer(filePlan); + if (newRecordContainer == null) + { + throw new AlfrescoRuntimeException("Unable to create record, because new record container could not be found."); + } + + // move the document into the file plan + nodeService.moveNode(actionedUponNodeRef, newRecordContainer, ContentModel.ASSOC_CONTAINS, parentAssoc.getQName()); + + // maintain the original primary location + nodeService.addChild(parentAssoc.getParentRef(), actionedUponNodeRef, parentAssoc.getTypeQName(), parentAssoc.getQName()); + + // add extended security information to the record + Map props = new HashMap(1); + props.put(PROP_READERS, (Serializable)readers); + nodeService.addAspect(actionedUponNodeRef, ASPECT_EXTENDED_RECORD_SECURITY, props); + + // add permission so readers can still 'see' the new record + // Note: using the regular permission service as we don't want to reflect this permission up (and down) the + // hierarchy + permissionService.setPermission(actionedUponNodeRef, + RecordReadersDynamicAuthority.RECORD_READERS, + RMPermissionModel.READ_RECORDS, + true); + + return null; + } + }); + } + else + { + throw new AlfrescoRuntimeException("Unable to file file plan."); + } + } + + @Override + protected void addParameterDefinitions(List params) + { + // TODO eventually we will need to pass in the file plan as a parameter + // TODO .. or the RM site + } + +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/DeclareRecordAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/DeclareRecordAction.java index d37078ad1c..81595e2e61 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/DeclareRecordAction.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/DeclareRecordAction.java @@ -64,7 +64,7 @@ public class DeclareRecordAction extends RMActionExecuterAbstractBase { if (recordsManagementService.isRecord(actionedUponNodeRef) == true) { - if (recordsManagementService.isRecordDeclared(actionedUponNodeRef) == false) + if (recordService.isDeclared(actionedUponNodeRef) == false) { List missingProperties = new ArrayList(5); // Aspect not already defined - check mandatory properties then add @@ -195,7 +195,7 @@ public class DeclareRecordAction extends RMActionExecuterAbstractBase { if (recordsManagementService.isRecord(filePlanComponent) == true) { - if (recordsManagementService.isRecordDeclared(filePlanComponent) == false) + if (recordService.isDeclared(filePlanComponent) == false) { // Aspect not already defined - check mandatory properties then add List missingProperties = new ArrayList(10); diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/SplitEmailAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/SplitEmailAction.java index 9523e21558..75152018ac 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/SplitEmailAction.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/SplitEmailAction.java @@ -114,7 +114,7 @@ public class SplitEmailAction extends RMActionExecuterAbstractBase if (recordsManagementService.isRecord(actionedUponNodeRef) == true) { - if (recordsManagementService.isRecordDeclared(actionedUponNodeRef) == false) + if (recordService.isDeclared(actionedUponNodeRef) == false) { ChildAssociationRef parent = nodeService.getPrimaryParent(actionedUponNodeRef); @@ -175,7 +175,7 @@ public class SplitEmailAction extends RMActionExecuterAbstractBase { if (recordsManagementService.isRecord(filePlanComponent) == true) { - if (recordsManagementService.isRecordDeclared(filePlanComponent)) + if (recordService.isDeclared(filePlanComponent)) { if (throwException) { diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/UndeclareRecordAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/UndeclareRecordAction.java index a2a926f22d..bbf53afd2b 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/UndeclareRecordAction.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/UndeclareRecordAction.java @@ -48,7 +48,7 @@ public class UndeclareRecordAction extends RMActionExecuterAbstractBase { if (recordsManagementService.isRecord(actionedUponNodeRef) == true) { - if (recordsManagementService.isRecordDeclared(actionedUponNodeRef) == true) + if (recordService.isDeclared(actionedUponNodeRef) == true) { // Remove the declared aspect this.nodeService.removeAspect(actionedUponNodeRef, ASPECT_DECLARED_RECORD); @@ -73,7 +73,7 @@ public class UndeclareRecordAction extends RMActionExecuterAbstractBase { if (recordsManagementService.isRecord(filePlanComponent) == true) { - if (recordsManagementService.isRecordDeclared(filePlanComponent) == true) + if (recordService.isDeclared(filePlanComponent) == true) { return true; } diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMSecurityCommon.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMSecurityCommon.java index c80837debf..d2dde825dc 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMSecurityCommon.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMSecurityCommon.java @@ -22,7 +22,7 @@ import net.sf.acegisecurity.vote.AccessDecisionVoter; import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; import org.alfresco.module.org_alfresco_module_rm.caveat.RMCaveatConfigComponent; -import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.module.org_alfresco_module_rm.record.RecordService; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.transaction.AlfrescoTransactionSupport; import org.alfresco.service.cmr.repository.NodeRef; @@ -45,6 +45,7 @@ public class RMSecurityCommon protected NodeService nodeService; protected PermissionService permissionService; protected RecordsManagementService rmService; + protected RecordService recordService; protected RMCaveatConfigComponent caveatConfigComponent; public void setNodeService(NodeService nodeService) @@ -62,6 +63,11 @@ public class RMSecurityCommon this.rmService = rmService; } + public void setRecordService(RecordService recordService) + { + this.recordService = recordService; + } + public void setCaveatConfigComponent(RMCaveatConfigComponent caveatConfigComponent) { this.caveatConfigComponent = caveatConfigComponent; diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/AbstractCapabilityCondition.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/AbstractCapabilityCondition.java index f09769a026..e2bfc95777 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/AbstractCapabilityCondition.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/AbstractCapabilityCondition.java @@ -20,6 +20,7 @@ package org.alfresco.module.org_alfresco_module_rm.capability.declarative; 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.service.cmr.repository.NodeService; import org.alfresco.service.cmr.security.PermissionService; import org.springframework.beans.factory.BeanNameAware; @@ -38,6 +39,7 @@ public abstract class AbstractCapabilityCondition implements CapabilityCondition /** Services */ protected RecordsManagementService rmService; + protected RecordService recordService; protected PermissionService permissionService; protected NodeService nodeService; @@ -49,6 +51,14 @@ public abstract class AbstractCapabilityCondition implements CapabilityCondition this.rmService = rmService; } + /** + * @param recordService record service + */ + public void setRecordService(RecordService recordService) + { + this.recordService = recordService; + } + /** * @param permissionService permission service */ diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/DeclarativeCapability.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/DeclarativeCapability.java index f57f054423..e26954d6ca 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/DeclarativeCapability.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/DeclarativeCapability.java @@ -128,6 +128,9 @@ public class DeclarativeCapability extends AbstractCapability this.isUndetermined = isUndetermined; } + /** + * @return + */ public boolean isUndetermined() { return isUndetermined; diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/DeclaredCapabilityCondition.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/DeclaredCapabilityCondition.java index b57801aab4..f3f479095c 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/DeclaredCapabilityCondition.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/declarative/condition/DeclaredCapabilityCondition.java @@ -33,7 +33,7 @@ public class DeclaredCapabilityCondition extends AbstractCapabilityCondition boolean result = false; if (FilePlanComponentKind.RECORD.equals(rmService.getFilePlanComponentKind(nodeRef)) == true) { - result = rmService.isRecordDeclared(nodeRef); + result = recordService.isDeclared(nodeRef); } return result; } diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/impl/CreateCapability.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/impl/CreateCapability.java index a54bda2968..e4acc7b194 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/impl/CreateCapability.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/impl/CreateCapability.java @@ -70,7 +70,7 @@ public class CreateCapability extends DeclarativeCapability { if(linkee == null) { - if(rmService.isRecord(destination) && rmService.isRecordDeclared(destination) == false) + if(rmService.isRecord(destination) && recordService.isDeclared(destination) == false) { if (permissionService.hasPermission(destination, RMPermissionModel.FILE_RECORDS) == AccessStatus.ALLOWED) { @@ -80,7 +80,7 @@ public class CreateCapability extends DeclarativeCapability } else { - if(rmService.isRecord(linkee) && rmService.isRecord(destination) && rmService.isRecordDeclared(destination) == false) + if(rmService.isRecord(linkee) && rmService.isRecord(destination) && recordService.isDeclared(destination) == false) { if (permissionService.hasPermission(destination, RMPermissionModel.FILE_RECORDS) == AccessStatus.ALLOWED) { diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/forms/RecordsManagementFormFilter.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/forms/RecordsManagementFormFilter.java index 7b6579f5d8..5768888b3c 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/forms/RecordsManagementFormFilter.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/forms/RecordsManagementFormFilter.java @@ -25,6 +25,7 @@ import java.util.Map; import org.alfresco.module.org_alfresco_module_rm.RecordsManagementAdminService; import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; import org.alfresco.module.org_alfresco_module_rm.RecordsManagementServiceRegistry; +import org.alfresco.module.org_alfresco_module_rm.record.RecordService; import org.alfresco.repo.forms.Field; import org.alfresco.repo.forms.FieldGroup; import org.alfresco.repo.forms.Form; @@ -58,6 +59,7 @@ public abstract class RecordsManagementFormFilter extends AbstractFilt protected RecordsManagementServiceRegistry rmServiceRegistry; protected RecordsManagementService rmService; protected RecordsManagementAdminService rmAdminService; + protected RecordService recordService; /** * Sets the NamespaceService instance @@ -109,6 +111,14 @@ public abstract class RecordsManagementFormFilter extends AbstractFilt this.rmAdminService = rmAdminService; } + /** + * @param recordService record service + */ + public void setRecordService(RecordService recordService) + { + this.recordService = recordService; + } + /** * Add property fields to group * diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/forms/RecordsManagementNodeFormFilter.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/forms/RecordsManagementNodeFormFilter.java index 43a0d08512..8573483c49 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/forms/RecordsManagementNodeFormFilter.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/forms/RecordsManagementNodeFormFilter.java @@ -166,7 +166,7 @@ public class RecordsManagementNodeFormFilter extends RecordsManagementFormFilter protected void addRecordMetadataPropertyFieldsToGroup(Form form, NodeRef nodeRef) { - Set aspects = rmService.getRecordMetaDataAspects(); + Set aspects = recordService.getRecordMetaDataAspects(); for (QName aspect : aspects) { @@ -214,7 +214,7 @@ public class RecordsManagementNodeFormFilter extends RecordsManagementFormFilter { if (rmService.isRecord(nodeRef) == true) { - addTransientPropertyField(form, TRANSIENT_DECLARED, DataTypeDefinition.BOOLEAN, rmService.isRecordDeclared(nodeRef)); + addTransientPropertyField(form, TRANSIENT_DECLARED, DataTypeDefinition.BOOLEAN, recordService.isDeclared(nodeRef)); } DispositionSchedule ds = dispositionService.getDispositionSchedule(nodeRef); diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/identifier/IdentifierServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/identifier/IdentifierServiceImpl.java index e9d99efd5f..148865d15b 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/identifier/IdentifierServiceImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/identifier/IdentifierServiceImpl.java @@ -120,7 +120,7 @@ public class IdentifierServiceImpl implements IdentifierService } /** - * Generate an identifier for a given type of object with the acompanying context. + * Generate an identifier for a given type of object with the accompanying context. * * @param type content type * @param context context diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/BaseEvaluator.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/BaseEvaluator.java index 014f4ad8e5..a5fc34068c 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/BaseEvaluator.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/BaseEvaluator.java @@ -28,6 +28,7 @@ import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; import org.alfresco.module.org_alfresco_module_rm.capability.Capability; import org.alfresco.module.org_alfresco_module_rm.capability.CapabilityService; import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.module.org_alfresco_module_rm.record.RecordService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.security.AccessStatus; @@ -49,6 +50,9 @@ public abstract class BaseEvaluator implements RecordsManagementModel /** Records management service */ protected RecordsManagementService recordsManagementService; + /** Record service */ + protected RecordService recordService; + /** Node service */ protected NodeService nodeService; @@ -80,6 +84,14 @@ public abstract class BaseEvaluator implements RecordsManagementModel this.recordsManagementService = recordsManagementService; } + /** + * @param recordService record service + */ + public void setRecordService(RecordService recordService) + { + this.recordService = recordService; + } + /** * @param nodeService node service */ diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/JSONConversionComponent.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/JSONConversionComponent.java index 29f526faa2..94b9e1bccf 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/JSONConversionComponent.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/JSONConversionComponent.java @@ -25,6 +25,7 @@ import org.alfresco.module.org_alfresco_module_rm.FilePlanComponentKind; import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; import org.alfresco.module.org_alfresco_module_rm.capability.CapabilityService; import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel; +import org.alfresco.module.org_alfresco_module_rm.record.RecordService; import org.alfresco.service.cmr.model.FileInfo; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.security.AccessStatus; @@ -41,6 +42,9 @@ public class JSONConversionComponent extends org.alfresco.repo.jscript.app.JSONC /** Records management service */ private RecordsManagementService recordsManagementService; + /** Record service */ + private RecordService recordService; + /** Capability service */ private CapabilityService capabilityService; @@ -56,7 +60,15 @@ public class JSONConversionComponent extends org.alfresco.repo.jscript.app.JSONC public void setRecordsManagementService(RecordsManagementService recordsManagementService) { this.recordsManagementService = recordsManagementService; - } + } + + /** + * @param recordService record service + */ + public void setRecordService(RecordService recordService) + { + this.recordService = recordService; + } /** * @param capabilityService capability service @@ -219,7 +231,7 @@ public class JSONConversionComponent extends org.alfresco.repo.jscript.app.JSONC } else { - if (recordsManagementService.isRecordDeclared(nodeRef) == true) + if (recordService.isDeclared(nodeRef) == true) { result = "record"; } diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/evaluator/SplitEmailActionEvaluator.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/evaluator/SplitEmailActionEvaluator.java index c28f1ae23b..574da9ed1e 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/evaluator/SplitEmailActionEvaluator.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/evaluator/SplitEmailActionEvaluator.java @@ -35,7 +35,7 @@ public class SplitEmailActionEvaluator extends BaseEvaluator protected boolean evaluateImpl(NodeRef nodeRef) { boolean result = false; - if (recordsManagementService.isRecordDeclared(nodeRef) == false) + if (recordService.isDeclared(nodeRef) == false) { ContentData contentData = (ContentData)nodeService.getProperty(nodeRef, ContentModel.PROP_CONTENT); if (contentData != null) diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/RecordsManagementModel.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/RecordsManagementModel.java index 5891cb5788..992059c02a 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/RecordsManagementModel.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/RecordsManagementModel.java @@ -57,6 +57,9 @@ public interface RecordsManagementModel extends RecordsManagementCustomModel // Records management root container public static final QName TYPE_FILE_PLAN = QName.createQName(RM_URI, "filePlan"); + // New records container + public static final QName TYPE_NEW_RECORDS_CONTAINER = QName.createQName(RM_URI, "newRecordsContainer"); + // Disposition instructions aspect public static final QName ASPECT_SCHEDULED = QName.createQName(RM_URI, "scheduled"); public static final QName ASSOC_DISPOSITION_SCHEDULE = QName.createQName(RM_URI, "dispositionSchedule"); @@ -167,6 +170,7 @@ public interface RecordsManagementModel extends RecordsManagementCustomModel public static final QName ASPECT_RECORDS_MANAGEMENT_ROOT = QName.createQName(RM_URI, "recordsManagementRoot"); public static final QName ASSOC_HOLDS = QName.createQName(RM_URI, "holds"); public static final QName ASSOC_TRANSFERS = QName.createQName(RM_URI, "transfers"); + public static final QName ASSOC_NEW_RECORDS = QName.createQName(RM_URI, "newRecords"); // Hold type public static final QName TYPE_HOLD = QName.createQName(RM_URI, "hold"); @@ -219,4 +223,8 @@ public interface RecordsManagementModel extends RecordsManagementCustomModel public static final QName PROP_RS_DISPOITION_INSTRUCTIONS = QName.createQName(RM_URI, "recordSearchDispositionInstructions"); public static final QName PROP_RS_DISPOITION_AUTHORITY = QName.createQName(RM_URI, "recordSearchDispositionAuthority"); public static final QName PROP_RS_HOLD_REASON = QName.createQName(RM_URI, "recordSearchHoldReason"); + + // Extended record security aspect + public static final QName ASPECT_EXTENDED_RECORD_SECURITY = QName.createQName(RM_URI, "extendedRecordSecurity"); + public static final QName PROP_READERS = QName.createQName(RM_URI, "readers"); } diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/FilePlanComponentAspect.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/behaviour/FilePlanComponentAspect.java similarity index 95% rename from rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/FilePlanComponentAspect.java rename to rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/behaviour/FilePlanComponentAspect.java index 637f0caa4d..dae35c2349 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/FilePlanComponentAspect.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/behaviour/FilePlanComponentAspect.java @@ -16,12 +16,13 @@ * You should have received a copy of the GNU Lesser General Public License * along with Alfresco. If not, see . */ -package org.alfresco.module.org_alfresco_module_rm.model; +package org.alfresco.module.org_alfresco_module_rm.model.behaviour; import java.io.Serializable; import java.util.Map; import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; import org.alfresco.repo.copy.AbstractCopyBehaviourCallback; import org.alfresco.repo.copy.CopyBehaviourCallback; import org.alfresco.repo.copy.CopyDetails; diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/behaviour/FilePlanType.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/behaviour/FilePlanType.java new file mode 100644 index 0000000000..5f961b6185 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/behaviour/FilePlanType.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.model.behaviour; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.repo.node.NodeServicePolicies; +import org.alfresco.repo.policy.JavaBehaviour; +import org.alfresco.repo.policy.PolicyComponent; +import org.alfresco.repo.policy.Behaviour.NotificationFrequency; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.namespace.QName; + +/** + * Behaviour associated with the file plan type + * + * @author Roy Wetherall + */ +public class FilePlanType implements RecordsManagementModel, + NodeServicePolicies.OnCreateNodePolicy +{ + /** Policy component */ + private PolicyComponent policyComponent; + + /** Node service */ + private NodeService nodeService; + + /** Permission service */ + private PermissionService permissionService; + + /** New record container name */ + private static final String NAME_NR_CONTAINER = "New Records"; + + /** + * Set the policy component + * @param policyComponent policy component + */ + public void setPolicyComponent(PolicyComponent policyComponent) + { + this.policyComponent = policyComponent; + } + + /** + * Set node service + * @param nodeService node service + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + public void setPermissionService(PermissionService permissionService) + { + this.permissionService = permissionService; + } + + /** + * Bean initialisation method + */ + public void init() + { + policyComponent.bindClassBehaviour( + NodeServicePolicies.OnCreateNodePolicy.QNAME, + TYPE_FILE_PLAN, + new JavaBehaviour(this, "onCreateNode", NotificationFrequency.TRANSACTION_COMMIT)); + } + + /** + * @see org.alfresco.repo.node.NodeServicePolicies.OnCreateNodePolicy#onCreateNode(org.alfresco.service.cmr.repository.ChildAssociationRef) + */ + @Override + public void onCreateNode(ChildAssociationRef assoc) + { + // grab the newly created file plan + NodeRef filePlan = assoc.getChildRef(); + + // create the properties map + Map properties = new HashMap(1); + properties.put(ContentModel.PROP_NAME, NAME_NR_CONTAINER); + + // create the 'new records' folder + NodeRef container = nodeService.createNode( + filePlan, + ASSOC_NEW_RECORDS, + QName.createQName(RM_URI, NAME_NR_CONTAINER), + TYPE_NEW_RECORDS_CONTAINER, + properties).getChildRef(); + + // set inheritance to false + permissionService.setInheritParentPermissions(container, false); + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/RecordComponentIdentifierAspect.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/behaviour/RecordComponentIdentifierAspect.java similarity index 95% rename from rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/RecordComponentIdentifierAspect.java rename to rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/behaviour/RecordComponentIdentifierAspect.java index 2a037bf81c..97e3b434a4 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/RecordComponentIdentifierAspect.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/behaviour/RecordComponentIdentifierAspect.java @@ -16,11 +16,12 @@ * You should have received a copy of the GNU Lesser General Public License * along with Alfresco. If not, see . */ -package org.alfresco.module.org_alfresco_module_rm.model; +package org.alfresco.module.org_alfresco_module_rm.model.behaviour; import java.io.Serializable; import java.util.Map; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; import org.alfresco.repo.node.NodeServicePolicies; import org.alfresco.repo.node.NodeServicePolicies.BeforeDeleteNodePolicy; import org.alfresco.repo.node.NodeServicePolicies.OnUpdatePropertiesPolicy; diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/RecordContainerType.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/behaviour/RecordContainerType.java similarity index 95% rename from rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/RecordContainerType.java rename to rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/behaviour/RecordContainerType.java index 5bd85b7f1e..d4117ea00b 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/RecordContainerType.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/behaviour/RecordContainerType.java @@ -16,11 +16,12 @@ * You should have received a copy of the GNU Lesser General Public License * along with Alfresco. If not, see . */ -package org.alfresco.module.org_alfresco_module_rm.model; +package org.alfresco.module.org_alfresco_module_rm.model.behaviour; import org.alfresco.model.ContentModel; import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionService; import org.alfresco.module.org_alfresco_module_rm.identifier.IdentifierService; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; import org.alfresco.repo.node.NodeServicePolicies; import org.alfresco.repo.policy.JavaBehaviour; import org.alfresco.repo.policy.PolicyComponent; diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/RecordCopyBehaviours.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/behaviour/RecordCopyBehaviours.java similarity index 95% rename from rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/RecordCopyBehaviours.java rename to rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/behaviour/RecordCopyBehaviours.java index 7c4377c9d0..c58cca1a05 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/RecordCopyBehaviours.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/behaviour/RecordCopyBehaviours.java @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Alfresco. If not, see . */ -package org.alfresco.module.org_alfresco_module_rm.model; +package org.alfresco.module.org_alfresco_module_rm.model.behaviour; import java.io.Serializable; import java.util.ArrayList; @@ -26,6 +26,7 @@ import java.util.Map; import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; import org.alfresco.module.org_alfresco_module_rm.RecordsManagementServiceRegistry; import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionService; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; import org.alfresco.repo.copy.AbstractCopyBehaviourCallback; import org.alfresco.repo.copy.CopyBehaviourCallback; import org.alfresco.repo.copy.CopyDetails; diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/RecordsManagementSearchBehaviour.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/behaviour/RecordsManagementSearchBehaviour.java similarity index 93% rename from rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/RecordsManagementSearchBehaviour.java rename to rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/behaviour/RecordsManagementSearchBehaviour.java index ea6e0ef6ca..84adf52828 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/RecordsManagementSearchBehaviour.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/behaviour/RecordsManagementSearchBehaviour.java @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Alfresco. If not, see . */ -package org.alfresco.module.org_alfresco_module_rm.model; +package org.alfresco.module.org_alfresco_module_rm.model.behaviour; import java.io.Serializable; import java.util.ArrayList; @@ -36,6 +36,7 @@ import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedul import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionScheduleImpl; import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService; import org.alfresco.module.org_alfresco_module_rm.event.EventCompletionDetails; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; import org.alfresco.module.org_alfresco_module_rm.vital.VitalRecordDefinition; import org.alfresco.module.org_alfresco_module_rm.vital.VitalRecordService; import org.alfresco.repo.policy.JavaBehaviour; @@ -315,13 +316,22 @@ public class RecordsManagementSearchBehaviour implements RecordsManagementModel } } - public void onAddRecordAspect(NodeRef nodeRef, QName aspectTypeQName) + public void onAddRecordAspect(final NodeRef nodeRef, final QName aspectTypeQName) { - if (nodeService.exists(nodeRef) == true) + AuthenticationUtil.runAsSystem(new AuthenticationUtil.RunAsWork() { - applySearchAspect(nodeRef); - setupDispositionScheduleProperties(nodeRef); - } + @Override + public Void doWork() throws Exception + { + if (nodeService.exists(nodeRef) == true) + { + applySearchAspect(nodeRef); + setupDispositionScheduleProperties(nodeRef); + } + + return null; + } + }); } public void recordFolderCreate(ChildAssociationRef childAssocRef) @@ -494,13 +504,22 @@ public class RecordsManagementSearchBehaviour implements RecordsManagementModel } } - public void rmSearchAspectAdd(NodeRef nodeRef, QName aspectTypeQName) - { - if (nodeService.exists(nodeRef) == true) + public void rmSearchAspectAdd(final NodeRef nodeRef, final QName aspectTypeQName) + { + AuthenticationUtil.runAsSystem(new AuthenticationUtil.RunAsWork() { - // Initialise the search parameteres as required - setVitalRecordDefintionDetails(nodeRef); - } + @Override + public Void doWork() throws Exception + { + if (nodeService.exists(nodeRef) == true) + { + // Initialise the search parameteres as required + setVitalRecordDefintionDetails(nodeRef); + } + + return null; + } + }); } public void vitalRecordDefintionAddAspect(NodeRef nodeRef, QName aspectTypeQName) diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/RmSiteType.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/behaviour/RmSiteType.java similarity index 94% rename from rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/RmSiteType.java rename to rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/behaviour/RmSiteType.java index 8224640d20..c66039c5a0 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/RmSiteType.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/behaviour/RmSiteType.java @@ -16,8 +16,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with Alfresco. If not, see . */ -package org.alfresco.module.org_alfresco_module_rm.model; +package org.alfresco.module.org_alfresco_module_rm.model.behaviour; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; import org.alfresco.module.org_alfresco_module_rm.search.RecordsManagementSearchService; import org.alfresco.repo.node.NodeServicePolicies; import org.alfresco.repo.policy.JavaBehaviour; diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/ScheduledAspect.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/behaviour/ScheduledAspect.java similarity index 92% rename from rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/ScheduledAspect.java rename to rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/behaviour/ScheduledAspect.java index 22169f0417..c4e1c004ed 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/ScheduledAspect.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/behaviour/ScheduledAspect.java @@ -16,9 +16,10 @@ * You should have received a copy of the GNU Lesser General Public License * along with Alfresco. If not, see . */ -package org.alfresco.module.org_alfresco_module_rm.model; +package org.alfresco.module.org_alfresco_module_rm.model.behaviour; import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; import org.alfresco.repo.node.NodeServicePolicies; import org.alfresco.repo.policy.JavaBehaviour; import org.alfresco.repo.policy.PolicyComponent; diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/permission/OtherImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/permission/OtherImpl.java deleted file mode 100644 index 958d67566a..0000000000 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/permission/OtherImpl.java +++ /dev/null @@ -1,370 +0,0 @@ -package org.alfresco.module.org_alfresco_module_rm.permission; - -import java.io.Serializable; -import java.util.Collections; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.Set; - -import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel; -import org.alfresco.repo.cache.SimpleCache; -import org.alfresco.repo.domain.permissions.AclDAO; -import org.alfresco.repo.security.permissions.AccessControlEntry; -import org.alfresco.repo.security.permissions.AccessControlList; -import org.alfresco.repo.security.permissions.PermissionEntry; -import org.alfresco.repo.security.permissions.PermissionReference; -import org.alfresco.repo.security.permissions.impl.ModelDAO; -import org.alfresco.repo.security.permissions.impl.PermissionServiceImpl; -import org.alfresco.repo.security.permissions.impl.RequiredPermission; -import org.alfresco.repo.security.permissions.impl.SimplePermissionReference; -import org.alfresco.service.cmr.security.AccessStatus; -import org.alfresco.service.cmr.security.PermissionService; -import org.alfresco.service.namespace.QName; -import org.alfresco.util.Pair; - -public class OtherImpl extends PermissionServiceImpl -{ - static SimplePermissionReference RM_OLD_ALL_PERMISSIONS_REFERENCE = SimplePermissionReference.getPermissionReference( - QName.createQName("", PermissionService.ALL_PERMISSIONS), - PermissionService.ALL_PERMISSIONS); - - private SimpleCache> rmReadersCache; - - private AclDAO rmAclDaoComponent; - - private ModelDAO rmModelDao; - - public void setRmReadersCache(SimpleCache> rmReadersCache) - { - this.rmReadersCache = rmReadersCache; - } - - public void setRmAclDAO(AclDAO rmAclDaoComponent) - { - this.rmAclDaoComponent = rmAclDaoComponent; - } - - public void setRmModelDAO(ModelDAO rmModelDao) - { - this.rmModelDao = rmModelDao; - } - - @Override - public void setAnyDenyDenies(boolean anyDenyDenies) - { - super.setAnyDenyDenies(anyDenyDenies); - rmReadersCache.clear(); - } - - @Override - public Set getReaders(Long aclId) - { - Set dmReaders = super.getReaders(aclId); - - Set rmReaders = rmReadersCache.get(aclId); - if (rmReaders == null) - { - rmReaders = buildRMReaders(aclId); - rmReadersCache.put(aclId, rmReaders); - } - - Set result = new HashSet(); - result.addAll(dmReaders); - result.addAll(rmReaders); - return result; - } - - private Set buildRMReaders(Long aclId) - { - AccessControlList acl = rmAclDaoComponent.getAccessControlList(aclId); - if (acl == null) - { - return Collections.emptySet(); - } - - HashSet assigned = new HashSet(); - HashSet readers = new HashSet(); - - for (AccessControlEntry ace : acl.getEntries()) - { - assigned.add(ace.getAuthority()); - } - - PermissionReference permissionRef = getPermissionReference(RMPermissionModel.READ_RECORDS); - - for (String authority : assigned) - { - RMUnconditionalAclTest rmTest = new RMUnconditionalAclTest(permissionRef); - if (rmTest.evaluate(authority, aclId)) - { - readers.add(authority); - } - } - - return Collections.unmodifiableSet(readers); - } - - /** - * Ignores type and aspect requirements on the node - * - */ - private class RMUnconditionalAclTest - { - /* - * The required permission. - */ - PermissionReference required; - - /* - * Granters of the permission - */ - Set granters; - - /* - * The additional permissions required at the node level. - */ - Set nodeRequirements = new HashSet(); - - /* - * Constructor just gets the additional requirements - */ - RMUnconditionalAclTest(PermissionReference required) - { - this.required = required; - - // Set the required node permissions - if (required.equals(getPermissionReference(ALL_PERMISSIONS))) - { - nodeRequirements = rmModelDao.getUnconditionalRequiredPermissions(getPermissionReference(PermissionService.FULL_CONTROL), RequiredPermission.On.NODE); - } - else - { - nodeRequirements = rmModelDao.getUnconditionalRequiredPermissions(required, RequiredPermission.On.NODE); - } - - if (rmModelDao.getUnconditionalRequiredPermissions(required, RequiredPermission.On.PARENT).size() > 0) - { - throw new IllegalStateException("Parent permissions can not be checked for an acl"); - } - - if (rmModelDao.getUnconditionalRequiredPermissions(required, RequiredPermission.On.CHILDREN).size() > 0) - { - throw new IllegalStateException("Child permissions can not be checked for an acl"); - } - - // Find all the permissions that grant the allowed permission - // All permissions are treated specially. - granters = new LinkedHashSet(128, 1.0f); - granters.addAll(rmModelDao.getGrantingPermissions(required)); - granters.add(getAllPermissionReference()); - granters.add(RM_OLD_ALL_PERMISSIONS_REFERENCE); - } - - /** - * Internal hook point for recursion - * - * @param authorisations - * @param nodeRef - * @param denied - * @param recursiveIn - * @return true if granted - */ - boolean evaluate(String authority, Long aclId) - { - // Start out true and "and" all other results - boolean success = true; - - // Check the required permissions but not for sets they rely on - // their underlying permissions - //if (modelDAO.checkPermission(required)) - //{ - - // We have to do the test as no parent will help us out - success &= hasSinglePermission(authority, aclId); - - if (!success) - { - return false; - } - //} - - // Check the other permissions required on the node - for (PermissionReference pr : nodeRequirements) - { - // Build a new test - RMUnconditionalAclTest nt = new RMUnconditionalAclTest(pr); - success &= nt.evaluate(authority, aclId); - if (!success) - { - return false; - } - } - - return success; - } - - boolean hasSinglePermission(String authority, Long aclId) - { - // Check global permission - - if (checkGlobalPermissions(authority)) - { - return true; - } - - if(aclId == null) - { - return false; - } - else - { - return checkRequired(authority, aclId); - } - - } - - /** - * Check if we have a global permission - * - * @param authorisations - * @return true if granted - */ - private boolean checkGlobalPermissions(String authority) - { - for (PermissionEntry pe : rmModelDao.getGlobalPermissionEntries()) - { - if (isGranted(pe, authority)) - { - return true; - } - } - return false; - } - - /** - * Check that a given authentication is available on a node - * - * @param authorisations - * @param nodeRef - * @param denied - * @return true if a check is required - */ - boolean checkRequired(String authority, Long aclId) - { - AccessControlList acl = rmAclDaoComponent.getAccessControlList(aclId); - - if (acl == null) - { - return false; - } - - Set> denied = new HashSet>(); - - // Check if each permission allows - the first wins. - // We could have other voting style mechanisms here - for (AccessControlEntry ace : acl.getEntries()) - { - if (isGranted(ace, authority, denied)) - { - return true; - } - } - return false; - } - - /** - * Is a permission granted - * - * @param pe - - * the permissions entry to consider - * @param granters - - * the set of granters - * @param authorisations - - * the set of authorities - * @param denied - - * the set of denied permissions/authority pais - * @return true if granted - */ - private boolean isGranted(AccessControlEntry ace, String authority, Set> denied) - { - // If the permission entry denies then we just deny - if (ace.getAccessStatus() == AccessStatus.DENIED) - { - denied.add(new Pair(ace.getAuthority(), ace.getPermission())); - - Set granters = rmModelDao.getGrantingPermissions(ace.getPermission()); - for (PermissionReference granter : granters) - { - denied.add(new Pair(ace.getAuthority(), granter)); - } - - // All the things granted by this permission must be - // denied - Set grantees = rmModelDao.getGranteePermissions(ace.getPermission()); - for (PermissionReference grantee : grantees) - { - denied.add(new Pair(ace.getAuthority(), grantee)); - } - - // All permission excludes all permissions available for - // the node. - if (ace.getPermission().equals(getAllPermissionReference()) || ace.getPermission().equals(RM_OLD_ALL_PERMISSIONS_REFERENCE)) - { - for (PermissionReference deny : rmModelDao.getAllPermissions()) - { - denied.add(new Pair(ace.getAuthority(), deny)); - } - } - - return false; - } - - // The permission is allowed but we deny it as it is in the denied - // set - - if (denied != null) - { - Pair specific = new Pair(ace.getAuthority(), required); - if (denied.contains(specific)) - { - return false; - } - } - - // If the permission has a match in both the authorities and - // granters list it is allowed - // It applies to the current user and it is granted - if (authority.equals(ace.getAuthority()) && granters.contains(ace.getPermission())) - { - { - return true; - } - } - - // Default deny - return false; - } - - private boolean isGranted(PermissionEntry pe, String authority) - { - // If the permission entry denies then we just deny - if (pe.isDenied()) - { - return false; - } - - // If the permission has a match in both the authorities and - // granters list it is allowed - // It applies to the current user and it is granted - if (granters.contains(pe.getPermissionReference()) && authority.equals(pe.getAuthority())) - { - { - return true; - } - } - - // Default deny - return false; - } - } -} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/permission/RecordReadersDynamicAuthority.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/permission/RecordReadersDynamicAuthority.java new file mode 100644 index 0000000000..d8d63a64e6 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/permission/RecordReadersDynamicAuthority.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.permission; + +import java.util.Set; + +import org.alfresco.module.org_alfresco_module_rm.FilePlanComponentKind; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.repo.security.permissions.DynamicAuthority; +import org.alfresco.repo.security.permissions.PermissionReference; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.security.AuthorityService; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; + +/** + * @author Roy Wetherall + * @since 2.1 + */ +public class RecordReadersDynamicAuthority implements DynamicAuthority, RecordsManagementModel, ApplicationContextAware +{ + public static final String RECORD_READERS = "ROLE_RECORD_READERS"; + + private RecordsManagementService recordsManagementService; + + private NodeService nodeService; + + private AuthorityService authorityService; + + private ApplicationContext applicationContext; + + private RecordsManagementService getRecordsManagementService() + { + if (recordsManagementService == null) + { + recordsManagementService = (RecordsManagementService)applicationContext.getBean("recordsManagementService"); + } + return recordsManagementService; + } + + private NodeService getNodeService() + { + if (nodeService == null) + { + nodeService = (NodeService)applicationContext.getBean("nodeService"); + } + return nodeService; + } + + private AuthorityService getAuthorityService() + { + if (authorityService == null) + { + authorityService = (AuthorityService)applicationContext.getBean("authorityService"); + } + return authorityService; + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException + { + this.applicationContext = applicationContext; + } + + /** + * @see org.alfresco.repo.security.permissions.DynamicAuthority#getAuthority() + */ + @Override + public String getAuthority() + { + return RECORD_READERS; + } + + /** + * @see org.alfresco.repo.security.permissions.DynamicAuthority#hasAuthority(org.alfresco.service.cmr.repository.NodeRef, java.lang.String) + */ + @SuppressWarnings("unchecked") + @Override + public boolean hasAuthority(NodeRef nodeRef, String userName) + { + boolean result = false; + + FilePlanComponentKind kind = getRecordsManagementService().getFilePlanComponentKind(nodeRef); + if (FilePlanComponentKind.RECORD.equals(kind) == true) + { + if (getNodeService().hasAspect(nodeRef, ASPECT_EXTENDED_RECORD_SECURITY) == true) + { + result = true; + + System.out.println("Setting hasAuthority to true! - " + userName + " - " + nodeRef.toString()); + +// Set readers = (Set)nodeService.getProperty(nodeRef, PROP_READERS); +// for (String reader : readers) +// { +// // check to see if the user is one of the readers or is contained there within +// } + } + } + + return result; + } + + /** + * @see org.alfresco.repo.security.permissions.DynamicAuthority#requiredFor() + */ + @Override + public Set requiredFor() + { + return null; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordService.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordService.java new file mode 100644 index 0000000000..e311a3071e --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordService.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +package org.alfresco.module.org_alfresco_module_rm.record; + +import java.util.Set; + +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; + +/** + * + * @author Roy Wetherall + * @since 2.1 + */ +public interface RecordService +{ + /** + * Get a list of all the record meta-data aspects + * + * @return {@link Set}<{@link QName}> list of record meta-data aspects + */ + Set getRecordMetaDataAspects(); + + /** + * Indicates whether the record is declared + * + * @param nodeRef node reference (record) + * @return boolean true if record is declared, false otherwise + */ + boolean isDeclared(NodeRef nodeRef); + + // TODO boolean isRecordFiled(NodeRef record); + // TODO boolean isRecordClassified(NodeRef record); + + NodeRef getNewRecordContainer(NodeRef filePlan); + + NodeRef createRecord(NodeRef filePlan, NodeRef document); + + // TODO NodeRef createAndFileRecord(NodeRef recordFolder, NodeRef document); + + // TODO void fileRecord(NodeRef recordFolder, NodeRef record); +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImpl.java new file mode 100644 index 0000000000..4f0425d8a2 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImpl.java @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.record; + +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; +import org.alfresco.module.org_alfresco_module_rm.identifier.IdentifierService; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.repo.policy.JavaBehaviour; +import org.alfresco.repo.policy.PolicyComponent; +import org.alfresco.repo.policy.Behaviour.NotificationFrequency; +import org.alfresco.service.cmr.dictionary.AspectDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.RegexQNamePattern; + +/** + * @author Roy Wetherall + * @since 2.1 + */ +public class RecordServiceImpl implements RecordService, RecordsManagementModel +{ + private NodeService nodeService; + + private IdentifierService identifierService; + + private RecordsManagementService recordsManagementService; + + private DictionaryService dictionaryService; + + private PolicyComponent policyComponent; + + /** List of available record meta-data aspects */ + private Set recordMetaDataAspects; + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + public void setIdentifierService(IdentifierService identifierService) + { + this.identifierService = identifierService; + } + + public void setRecordsManagementService(RecordsManagementService recordsManagementService) + { + this.recordsManagementService = recordsManagementService; + } + + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + public void setPolicyComponent(PolicyComponent policyComponent) + { + this.policyComponent = policyComponent; + } + + public void init() + { + policyComponent.bindAssociationBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "onCreateChildAssociation"), + TYPE_NEW_RECORDS_CONTAINER, + ContentModel.ASSOC_CONTAINS, + new JavaBehaviour(this, "onCreateNewRecord", NotificationFrequency.TRANSACTION_COMMIT)); + } + + public void onCreateNewRecord(ChildAssociationRef childAssocRef, boolean bNew) + { + NodeRef nodeRef = childAssocRef.getChildRef(); + if (nodeService.exists(nodeRef) == true) + { + QName type = nodeService.getType(nodeRef); + if (ContentModel.TYPE_CONTENT.equals(type) == true || + dictionaryService.isSubClass(type, ContentModel.TYPE_CONTENT) == true) + { + makeRecord(nodeRef); + } + else + { + throw new AlfrescoRuntimeException("Only content can be created as a record."); + } + } + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.record.RecordService#getRecordMetaDataAspects() + */ + public Set getRecordMetaDataAspects() + { + if (recordMetaDataAspects == null) + { + recordMetaDataAspects = new HashSet(7); + Collection aspects = dictionaryService.getAllAspects(); + for (QName aspect : aspects) + { + AspectDefinition def = dictionaryService.getAspect(aspect); + if (def != null) + { + QName parent = def.getParentName(); + if (parent != null && ASPECT_RECORD_META_DATA.equals(parent) == true) + { + recordMetaDataAspects.add(aspect); + } + } + } + } + return recordMetaDataAspects; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.record.RecordService#isDeclared(org.alfresco.service.cmr.repository.NodeRef) + */ + public boolean isDeclared(NodeRef record) + { + return (nodeService.hasAspect(record, ASPECT_DECLARED_RECORD)); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.record.RecordService#getNewRecordContainer(org.alfresco.service.cmr.repository.NodeRef) + */ + public NodeRef getNewRecordContainer(NodeRef filePlan) + { + NodeRef result = null; + + if (recordsManagementService.isFilePlan(filePlan) == true) + { + List assocs = nodeService.getChildAssocs(filePlan, ASSOC_NEW_RECORDS, RegexQNamePattern.MATCH_ALL); + if (assocs.size() != 1) + { + throw new AlfrescoRuntimeException("Error getting the new record container, because the container cannot be indentified."); + } + result = assocs.get(0).getChildRef(); + } + + return result; + } + + @Override + public NodeRef createRecord(NodeRef filePlan, NodeRef document) + { + // get the documents primary parent assoc + ChildAssociationRef parentAssoc = nodeService.getPrimaryParent(document); + + /// get the new record container for the file plan + NodeRef newRecordContainer = getNewRecordContainer(filePlan); + if (newRecordContainer == null) + { + throw new AlfrescoRuntimeException("Unable to create record, because new record container could not be found."); + } + + // move the document into the file plan + nodeService.moveNode(document, newRecordContainer, ContentModel.ASSOC_CONTAINS, parentAssoc.getQName()); + + // maintain the original primary location + nodeService.addChild(parentAssoc.getParentRef(), document, parentAssoc.getTypeQName(), parentAssoc.getQName()); + + return document; + } + + /** + * + * @param document + */ + private void makeRecord(NodeRef document) + { + nodeService.addAspect(document, RecordsManagementModel.ASPECT_RECORD, null); + + String recordId = identifierService.generateIdentifier(ASPECT_RECORD, nodeService.getPrimaryParent(document).getParentRef()); + nodeService.setProperty(document, PROP_IDENTIFIER, recordId); + } + +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/BootstrapTestDataGet.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/BootstrapTestDataGet.java index a0571bf0d5..7c0fb69c2d 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/BootstrapTestDataGet.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/BootstrapTestDataGet.java @@ -33,8 +33,8 @@ import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel; import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule; import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService; import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; -import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementSearchBehaviour; -import org.alfresco.module.org_alfresco_module_rm.model.RmSiteType; +import org.alfresco.module.org_alfresco_module_rm.model.behaviour.RecordsManagementSearchBehaviour; +import org.alfresco.module.org_alfresco_module_rm.model.behaviour.RmSiteType; import org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService; import org.alfresco.module.org_alfresco_module_rm.security.Role; import org.alfresco.repo.security.authentication.AuthenticationUtil; diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/ExportPost.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/ExportPost.java index b4b4c09303..38cb3daf17 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/ExportPost.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/ExportPost.java @@ -20,28 +20,28 @@ package org.alfresco.module.org_alfresco_module_rm.script; import java.io.File; import java.io.IOException; -import java.io.StringWriter; import java.io.PrintWriter; +import java.io.StringWriter; import org.alfresco.model.ContentModel; import org.alfresco.model.RenditionModel; import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; -import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementSearchBehaviour; +import org.alfresco.module.org_alfresco_module_rm.model.behaviour.RecordsManagementSearchBehaviour; import org.alfresco.repo.exporter.ACPExportPackageHandler; import org.alfresco.repo.web.scripts.content.StreamACP; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.view.ExporterCrawlerParameters; import org.alfresco.service.cmr.view.Location; import org.alfresco.service.namespace.QName; -import org.springframework.extensions.webscripts.Status; -import org.springframework.extensions.webscripts.WebScriptException; -import org.springframework.extensions.webscripts.WebScriptRequest; -import org.springframework.extensions.webscripts.WebScriptResponse; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.json.JSONException; import org.json.JSONObject; import org.json.JSONTokener; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; +import org.springframework.extensions.webscripts.WebScriptResponse; /** * Creates an RM specific ACP file of nodes to export then streams it back diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/RecordMetaDataAspectsGet.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/RecordMetaDataAspectsGet.java index a15ac40173..bd826287ca 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/RecordMetaDataAspectsGet.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/RecordMetaDataAspectsGet.java @@ -24,7 +24,7 @@ import java.util.List; import java.util.Map; import java.util.Set; -import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; +import org.alfresco.module.org_alfresco_module_rm.record.RecordService; import org.alfresco.service.cmr.dictionary.AspectDefinition; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.namespace.NamespaceService; @@ -41,7 +41,7 @@ public class RecordMetaDataAspectsGet extends DeclarativeWebScript { protected DictionaryService dictionaryService; protected NamespaceService namespaceService; - protected RecordsManagementService recordsManagementService; + protected RecordService recordService; /** * Set the dictionary service instance @@ -62,10 +62,13 @@ public class RecordMetaDataAspectsGet extends DeclarativeWebScript { this.namespaceService = namespaceService; } - - public void setRecordsManagementService(RecordsManagementService recordsManagementService) + + /** + * @param recordService record service + */ + public void setRecordService(RecordService recordService) { - this.recordsManagementService = recordsManagementService; + this.recordService = recordService; } /* @@ -75,7 +78,7 @@ public class RecordMetaDataAspectsGet extends DeclarativeWebScript protected Map executeImpl(WebScriptRequest req, Status status, Cache cache) { // Get the details of all the aspects - Set aspectQNames = recordsManagementService.getRecordMetaDataAspects(); + Set aspectQNames = recordService.getRecordMetaDataAspects(); List> aspects = new ArrayList>(aspectQNames.size()+1); for (QName aspectQName : aspectQNames) { diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/TransferGet.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/TransferGet.java index 022ad7bbbe..c5f2b38716 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/TransferGet.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/TransferGet.java @@ -24,17 +24,17 @@ import java.io.IOException; import org.alfresco.model.ContentModel; import org.alfresco.model.RenditionModel; import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; -import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementSearchBehaviour; +import org.alfresco.module.org_alfresco_module_rm.model.behaviour.RecordsManagementSearchBehaviour; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.view.ExporterCrawlerParameters; import org.alfresco.service.cmr.view.Location; import org.alfresco.service.namespace.QName; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.extensions.webscripts.Cache; import org.springframework.extensions.webscripts.Status; import org.springframework.extensions.webscripts.WebScriptRequest; import org.springframework.extensions.webscripts.WebScriptResponse; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; /** * Streams the nodes of a transfer object to the client in the form of an diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/slingshot/RMSearchPropertiesGet.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/slingshot/RMSearchPropertiesGet.java index 8948cb0895..d97cc1a53e 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/slingshot/RMSearchPropertiesGet.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/slingshot/RMSearchPropertiesGet.java @@ -25,7 +25,7 @@ import java.util.Map; import java.util.Set; import org.alfresco.module.org_alfresco_module_rm.RecordsManagementAdminService; -import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; +import org.alfresco.module.org_alfresco_module_rm.record.RecordService; import org.alfresco.service.cmr.dictionary.AspectDefinition; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.dictionary.PropertyDefinition; @@ -43,35 +43,45 @@ import org.springframework.extensions.webscripts.WebScriptRequest; */ public class RMSearchPropertiesGet extends DeclarativeWebScript { - private RecordsManagementAdminService adminService; - - private RecordsManagementService recordsManagementService; - - private DictionaryService dictionaryService; - + /** Services */ + private RecordsManagementAdminService adminService; + private RecordService recordService; + private DictionaryService dictionaryService; private NamespaceService namespaceService; - + + /** + * @param adminService records management admin service + */ public void setAdminService(RecordsManagementAdminService adminService) { this.adminService = adminService; } - public void setRecordsManagementService(RecordsManagementService recordsManagementService) + /** + * @param recordService record service + */ + public void setRecordService(RecordService recordService) { - this.recordsManagementService = recordsManagementService; + this.recordService = recordService; } + /** + * @param dictionaryService dictionary service + */ public void setDictionaryService(DictionaryService dictionaryService) { this.dictionaryService = dictionaryService; } + /** + * @param namespaceService namespace service + */ public void setNamespaceService(NamespaceService namespaceService) { this.namespaceService = namespaceService; } - /* + /** * @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.Status, org.alfresco.web.scripts.Cache) */ @Override @@ -81,7 +91,7 @@ public class RMSearchPropertiesGet extends DeclarativeWebScript List groups = new ArrayList(5); - Set aspects = recordsManagementService.getRecordMetaDataAspects(); + Set aspects = recordService.getRecordMetaDataAspects(); for (QName aspect : aspects) { Map properties = dictionaryService.getPropertyDefs(aspect); diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/RecordsManagementServiceImplTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/RecordsManagementServiceImplTest.java index f9d818bdaa..fad5ec9ac2 100644 --- a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/RecordsManagementServiceImplTest.java +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/RecordsManagementServiceImplTest.java @@ -21,6 +21,7 @@ package org.alfresco.module.org_alfresco_module_rm.test.service; import java.util.List; import java.util.Set; +import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.module.org_alfresco_module_rm.FilePlanComponentKind; import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; import org.alfresco.module.org_alfresco_module_rm.dod5015.DOD5015Model; @@ -591,7 +592,7 @@ public class RecordsManagementServiceImplTest extends BaseRMTestCase @Override public Void run() { - Set aspects = rmService.getRecordMetaDataAspects(); + Set aspects = recordService.getRecordMetaDataAspects(); assertNotNull(aspects); assertEquals(5, aspects.size()); assertTrue(aspects.containsAll( @@ -613,6 +614,45 @@ public class RecordsManagementServiceImplTest extends BaseRMTestCase // TODO void testIsRecordDeclared(NodeRef nodeRef); + public void testGetNewRecordsContainer() + { + doTestInTransaction(new Test() + { + @Override + public Void run() + { + NodeRef result1 = recordService.getNewRecordContainer(filePlan); + assertNotNull(result1); + assertEquals(TYPE_NEW_RECORDS_CONTAINER, nodeService.getType(result1)); + + assertNull(recordService.getNewRecordContainer(rmContainer)); + assertNull(recordService.getNewRecordContainer(rmFolder)); + + return null; + } + }); + + // Failure: File plan with no new record container + doTestInTransaction(new FailureTest + ( + "The newly created file plan shouldn't yet have a new record container.", + AlfrescoRuntimeException.class + ) + { + @Override + public void run() + { + NodeRef newFilePlan = rmService.createFilePlan(folder, GUID.generate()); + recordService.getNewRecordContainer(newFilePlan); + } + }); + } + + public void testCreateRecord() + { + + } + /********** RM2 - Multi-hierarchy record taxonomy's **********/ /** diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/VitalRecordServiceImplTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/VitalRecordServiceImplTest.java index ce07978310..a714452d41 100644 --- a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/VitalRecordServiceImplTest.java +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/VitalRecordServiceImplTest.java @@ -20,7 +20,7 @@ package org.alfresco.module.org_alfresco_module_rm.test.service; import java.util.Date; -import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementSearchBehaviour; +import org.alfresco.module.org_alfresco_module_rm.model.behaviour.RecordsManagementSearchBehaviour; import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase; import org.alfresco.module.org_alfresco_module_rm.vital.VitalRecordDefinition; import org.alfresco.repo.security.authentication.AuthenticationUtil; diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/system/DOD5015SystemTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/system/DOD5015SystemTest.java deleted file mode 100644 index 5d4ab0fcfe..0000000000 --- a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/system/DOD5015SystemTest.java +++ /dev/null @@ -1,4582 +0,0 @@ -/* - * Copyright (C) 2005-2011 Alfresco Software Limited. - * - * This file is part of Alfresco - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - */ -package org.alfresco.module.org_alfresco_module_rm.test.system; - -import java.io.File; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Collection; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.transaction.UserTransaction; - -import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.model.ContentModel; -import org.alfresco.module.org_alfresco_module_rm.RecordsManagementAdminService; -import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; -import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionResult; -import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionService; -import org.alfresco.module.org_alfresco_module_rm.action.impl.BroadcastDispositionActionDefinitionUpdateAction; -import org.alfresco.module.org_alfresco_module_rm.action.impl.CompleteEventAction; -import org.alfresco.module.org_alfresco_module_rm.action.impl.EditDispositionActionAsOfDateAction; -import org.alfresco.module.org_alfresco_module_rm.action.impl.EditReviewAsOfDateAction; -import org.alfresco.module.org_alfresco_module_rm.action.impl.FileAction; -import org.alfresco.module.org_alfresco_module_rm.action.impl.FreezeAction; -import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel; -import org.alfresco.module.org_alfresco_module_rm.caveat.RMCaveatConfigService; -import org.alfresco.module.org_alfresco_module_rm.caveat.RMListOfValuesConstraint.MatchLogic; -import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionAction; -import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionActionDefinition; -import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule; -import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService; -import org.alfresco.module.org_alfresco_module_rm.dod5015.DOD5015Model; -import org.alfresco.module.org_alfresco_module_rm.event.EventCompletionDetails; -import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementCustomModel; -import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; -import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementSearchBehaviour; -import org.alfresco.module.org_alfresco_module_rm.test.util.TestUtilities; -import org.alfresco.module.org_alfresco_module_rm.vital.VitalRecordDefinition; -import org.alfresco.module.org_alfresco_module_rm.vital.VitalRecordService; -import org.alfresco.repo.content.MimetypeMap; -import org.alfresco.repo.node.integrity.IntegrityException; -import org.alfresco.repo.search.impl.lucene.AbstractLuceneQueryParser; -import org.alfresco.repo.search.impl.lucene.fts.FullTextSearchIndexer; -import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; -import org.alfresco.repo.security.permissions.AccessDeniedException; -import org.alfresco.repo.transaction.RetryingTransactionHelper; -import org.alfresco.service.ServiceRegistry; -import org.alfresco.service.cmr.dictionary.DataTypeDefinition; -import org.alfresco.service.cmr.repository.ChildAssociationRef; -import org.alfresco.service.cmr.repository.ContentReader; -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.NodeService; -import org.alfresco.service.cmr.repository.Period; -import org.alfresco.service.cmr.repository.StoreRef; -import org.alfresco.service.cmr.search.ResultSet; -import org.alfresco.service.cmr.search.SearchService; -import org.alfresco.service.cmr.security.AccessStatus; -import org.alfresco.service.cmr.security.AuthorityService; -import org.alfresco.service.cmr.security.AuthorityType; -import org.alfresco.service.cmr.security.MutableAuthenticationService; -import org.alfresco.service.cmr.security.PermissionService; -import org.alfresco.service.cmr.security.PersonService; -import org.alfresco.service.cmr.security.PublicServiceAccessService; -import org.alfresco.service.cmr.site.SiteVisibility; -import org.alfresco.service.cmr.view.ImporterService; -import org.alfresco.service.namespace.NamespaceService; -import org.alfresco.service.namespace.QName; -import org.alfresco.service.namespace.RegexQNamePattern; -import org.alfresco.service.transaction.TransactionService; -import org.alfresco.util.BaseSpringTest; -import org.alfresco.util.GUID; -import org.alfresco.util.PropertyMap; - -/** - * DOD System Test - * - * @author Roy Wetherall, Neil McErlean - */ -public class DOD5015SystemTest extends BaseSpringTest implements RecordsManagementModel, DOD5015Model -{ - private static final Period weeklyReview = new Period("week|1"); - private static final Period dailyReview = new Period("day|1"); - public static final long TWENTY_FOUR_HOURS_IN_MS = 24 * 60 * 60 * 1000; // hours * minutes * seconds * millis - - protected static StoreRef SPACES_STORE = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore"); - - private NodeRef filePlan; - - private NodeService unprotectedNodeService; - private NodeService nodeService; - private SearchService searchService; - private ImporterService importService; - private ContentService contentService; - private RecordsManagementService rmService; - private RecordsManagementActionService rmActionService; - private ServiceRegistry serviceRegistry; - private TransactionService transactionService; - private RecordsManagementAdminService rmAdminService; - private RMCaveatConfigService caveatConfigService; - private DispositionService dispositionService; - private VitalRecordService vitalRecordService; - - private MutableAuthenticationService authenticationService; - private PersonService personService; - private AuthorityService authorityService; - private PermissionService permissionService; - private RetryingTransactionHelper transactionHelper; - - private PublicServiceAccessService publicServiceAccessService; - private FullTextSearchIndexer luceneFTS; - - // example base test data for supplemental markings list (see also recordsModel.xml) - protected final static String NOFORN = "NOFORN"; // Not Releasable to Foreign Nationals/Governments/Non-US Citizens - protected final static String NOCONTRACT = "NOCONTRACT"; // Not Releasable to Contractors or Contractor/Consultants - protected final static String FOUO = "FOUO"; // For Official Use Only - protected final static String FGI = "FGI"; // Foreign Government Information - - // example user-defined field - protected final static QName CONSTRAINT_CUSTOM_PRJLIST = QName.createQName(RM_CUSTOM_URI, "prjList"); - protected final static QName PROP_CUSTOM_PRJLIST = QName.createQName(RM_CUSTOM_URI, "projectNameList"); - - protected final static String PRJ_A = "Project A"; - protected final static String PRJ_B = "Project B"; - protected final static String PRJ_C = "Project C"; - - @Override - protected void onSetUpInTransaction() throws Exception - { - super.onSetUpInTransaction(); - - // Get the service required in the tests - this.unprotectedNodeService = (NodeService)applicationContext.getBean("nodeService"); - this.nodeService = (NodeService)this.applicationContext.getBean("NodeService"); // use upper 'N'odeService (to test access config interceptor) NodeService unprotectedNodeService = (NodeService)applicationContext.getBean("nodeService"); - this.authenticationService = (MutableAuthenticationService)this.applicationContext.getBean("AuthenticationService"); - this.personService = (PersonService)this.applicationContext.getBean("PersonService"); - this.authorityService = (AuthorityService)this.applicationContext.getBean("AuthorityService"); - this.permissionService = (PermissionService)this.applicationContext.getBean("PermissionService"); - this.searchService = (SearchService)this.applicationContext.getBean("SearchService"); // use upper 'S'earchService (to test access config interceptor) - this.importService = (ImporterService)this.applicationContext.getBean("importerComponent"); - this.contentService = (ContentService)this.applicationContext.getBean("ContentService"); - this.rmService = (RecordsManagementService)this.applicationContext.getBean("RecordsManagementService"); - this.rmActionService = (RecordsManagementActionService)this.applicationContext.getBean("RecordsManagementActionService"); - this.serviceRegistry = (ServiceRegistry)this.applicationContext.getBean("ServiceRegistry"); - this.transactionService = (TransactionService)this.applicationContext.getBean("TransactionService"); - this.rmAdminService = (RecordsManagementAdminService)this.applicationContext.getBean("RecordsManagementAdminService"); - this.caveatConfigService = (RMCaveatConfigService)this.applicationContext.getBean("caveatConfigService"); - this.publicServiceAccessService = (PublicServiceAccessService)this.applicationContext.getBean("PublicServiceAccessService"); - this.transactionHelper = (RetryingTransactionHelper)this.applicationContext.getBean("retryingTransactionHelper"); - this.dispositionService = (DispositionService)this.applicationContext.getBean("DispositionService"); - this.luceneFTS = (FullTextSearchIndexer)this.applicationContext.getBean("LuceneFullTextSearchIndexer"); - this.vitalRecordService = (VitalRecordService)applicationContext.getBean("VitalRecordService"); - - - // Set the current security context as admin - AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); - - // Get the test data - filePlan = TestUtilities.loadFilePlanData(applicationContext); - - File file = new File(System.getProperty("user.dir")+"/test-resources/testCaveatConfig1.json"); // from test-resources - assertTrue(file.exists()); - - caveatConfigService.updateOrCreateCaveatConfig(file); - - // set/reset allowed values (empty list by default) - List newValues = new ArrayList(4); - newValues.add(NOFORN); - newValues.add(NOCONTRACT); - newValues.add(FOUO); - newValues.add(FGI); - - rmAdminService.changeCustomConstraintValues(RecordsManagementCustomModel.CONSTRAINT_CUSTOM_SMLIST, newValues); - - // We pause FTS during this test, as it moves around records in intermediate places, and otherwise FTS may not - // finish clearing up its mess before each test finishes - this.luceneFTS.pause(); - } - - - - /* (non-Javadoc) - * @see org.springframework.test.AbstractTransactionalSpringContextTests#onTearDown() - */ - @Override - protected void onTearDown() throws Exception - { - super.onTearDown(); - - // Let FTS catch up again. - this.luceneFTS.resume(); - } - - - - /** - * Tests that the test data has been loaded correctly - */ - public void xtestTestData() throws Exception - { - // make sure the folders that should have disposition schedules do so - NodeRef janAuditRecordsFolder = TestUtilities.getRecordFolder(rmService, nodeService, "Reports", "AIS Audit Records", "January AIS Audit Records"); - assertNotNull(janAuditRecordsFolder); - - // ensure the folder has the disposition lifecycle aspect - assertTrue("Expected 'January AIS Audit Records' folder to have disposition lifecycle aspect applied", - nodeService.hasAspect(janAuditRecordsFolder, ASPECT_DISPOSITION_LIFECYCLE)); - - // ensure the folder has the correctly setup search aspect - checkSearchAspect(janAuditRecordsFolder); - - // check another folder that has events as part of the disposition schedule - NodeRef equalOppCoordFolder = TestUtilities.getRecordFolder(rmService, nodeService, "Military Files", "Personnel Security Program Records", "Equal Opportunity Coordinator"); - assertNotNull(equalOppCoordFolder); - assertTrue("Expected 'Equal Opportunity Coordinator' folder to have disposition lifecycle aspect applied", - nodeService.hasAspect(equalOppCoordFolder, ASPECT_DISPOSITION_LIFECYCLE)); - checkSearchAspect(equalOppCoordFolder); - } - - /** - * This test method creates a non-vital record and then moves it to a vital folder - * (triggering a refile) and then moves it a second time to another vital record - * having different metadata. - * - * Moving a Record within the FilePlan should trigger a "refile". Refiling a record - * will lead to the reconsideration of its disposition, vital and transfer/accession - * metadata, with potential changes therein. - */ - public void testMoveRefileRecord() throws Exception - { - // Commit in order to trigger the setUpRecordFolder behaviour - setComplete(); - endTransaction(); - - final NodeRef nonVitalFolder = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - // Create a record folder under a "non-vital" category - NodeRef nonVitalRecordCategory = TestUtilities.getRecordCategory(rmService, nodeService, "Reports", "Unit Manning Documents"); - assertNotNull(nonVitalRecordCategory); - - return createRecFolderNode(nonVitalRecordCategory); - } - }); - - final NodeRef recordUnderTest = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - // Create a (non-vital) record under the above folder - NodeRef recordUnderTest = createRecordNode(nonVitalFolder); - - rmActionService.executeRecordsManagementAction(recordUnderTest, "file"); - - TestUtilities.declareRecord(recordUnderTest, unprotectedNodeService, rmActionService); - - return recordUnderTest; - } - }); - - final NodeRef vitalFolder =transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - // No need to commit the transaction here as the record is non-vital and - // there is no metadata to copy down. - - NodeRef vitalFolder = retrieveJanuaryAISVitalFolder(); - - // Move the non-vital record under the vital folder. - serviceRegistry.getFileFolderService().move(recordUnderTest, vitalFolder, null); - - return vitalFolder; - } - }); - - final NodeRef secondVitalFolder = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - // At this point, the formerly nonVitalRecord is now actually vital. - assertTrue("Expected record.", rmService.isRecord(recordUnderTest)); - assertTrue("Expected declared.", rmService.isRecordDeclared(recordUnderTest)); - - final VitalRecordDefinition recordVrd = vitalRecordService.getVitalRecordDefinition(recordUnderTest); - assertNotNull("Moved record should now have a Vital Rec Defn", recordVrd); - assertEquals("Moved record had wrong review period", - vitalRecordService.getVitalRecordDefinition(vitalFolder).getReviewPeriod(), recordVrd.getReviewPeriod()); - assertNotNull("Moved record should now have a review-as-of date", nodeService.getProperty(recordUnderTest, PROP_REVIEW_AS_OF)); - - // Create another folder with different vital/disposition instructions - //TODO Change disposition instructions - NodeRef vitalRecordCategory = TestUtilities.getRecordCategory(rmService, nodeService, "Reports", "AIS Audit Records"); - assertNotNull(vitalRecordCategory); - return createRecFolderNode(vitalRecordCategory); - } - }); - - final Date reviewDate = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Date execute() throws Throwable - { - Map props = nodeService.getProperties(secondVitalFolder); - final Serializable secondVitalFolderReviewPeriod = props.get(PROP_REVIEW_PERIOD); - assertEquals("Unexpected review period.", weeklyReview, secondVitalFolderReviewPeriod); - - // We are changing the review period of this second record folder. - nodeService.setProperty(secondVitalFolder, PROP_REVIEW_PERIOD, dailyReview); - - Date reviewDate = (Date)nodeService.getProperty(recordUnderTest, PROP_REVIEW_AS_OF); - - // Move the newly vital record under the second vital folder. I expect the reviewPeriod - // for the record to be changed again. - serviceRegistry.getFileFolderService().move(recordUnderTest, secondVitalFolder, null); - - return reviewDate; - } - }); - - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - Period newReviewPeriod = vitalRecordService.getVitalRecordDefinition(recordUnderTest).getReviewPeriod(); - assertEquals("Unexpected review period.", dailyReview, newReviewPeriod); - - Date updatedReviewDate = (Date)nodeService.getProperty(recordUnderTest, PROP_REVIEW_AS_OF); - // The reviewAsOf date should have changed to "24 hours from now". - assertFalse("reviewAsOf date was unchanged", reviewDate.equals(updatedReviewDate)); - long millisecondsUntilNextReview = updatedReviewDate.getTime() - new Date().getTime(); - assertTrue("new reviewAsOf date was not within 24 hours of now.", - millisecondsUntilNextReview <= TWENTY_FOUR_HOURS_IN_MS); - - nodeService.deleteNode(recordUnderTest); - nodeService.deleteNode(nonVitalFolder); - nodeService.deleteNode(secondVitalFolder); - - return null; - } - }); - } - - public void off_testMoveRefileRecordFolder() throws Exception - { - //TODO Impl me - fail("Not yet impl'd."); - } - - public void off_testCopyRefileRecordFolder() throws Exception - { - //TODO Impl me - fail("Not yet impl'd."); - } - - public void off_testCopyRefileRecord() throws Exception - { - //TODO Impl me - fail("Not yet impl'd."); - } - - private NodeRef createRecordCategoryNode(NodeRef parentRecordSeries) - { - NodeRef newCategory = this.nodeService.createNode(parentRecordSeries, - ContentModel.ASSOC_CONTAINS, - QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "Test category " + System.currentTimeMillis()), - TYPE_RECORD_CATEGORY).getChildRef(); - - return newCategory; - } - - private NodeRef createRecFolderNode(NodeRef parentRecordCategory) - { - NodeRef newFolder = this.nodeService.createNode(parentRecordCategory, - ContentModel.ASSOC_CONTAINS, - QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "Test folder " + System.currentTimeMillis()), - TYPE_RECORD_FOLDER).getChildRef(); - return newFolder; - } - - private NodeRef createRecordNode(NodeRef parentFolder) - { - NodeRef newRecord = this.nodeService.createNode(parentFolder, - ContentModel.ASSOC_CONTAINS, - QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, - "Record" + System.currentTimeMillis() + ".txt"), - ContentModel.TYPE_CONTENT).getChildRef(); - ContentWriter writer = this.contentService.getWriter(newRecord, ContentModel.PROP_CONTENT, true); - writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); - writer.setEncoding("UTF-8"); - writer.putContent("Irrelevant content"); - return newRecord; - } - - private NodeRef retrieveJanuaryAISVitalFolder() - { - final List resultNodeRefs = retrieveJanuaryAISVitalFolders(); - final int folderCount = resultNodeRefs.size(); -// assertTrue("There should only be one 'January AIS Audit Records' folder. Were " + folderCount, folderCount == 1); - - // This nodeRef should have rma:VRI=true, rma:reviewPeriod=week|1, rma:isClosed=false - return resultNodeRefs.get(0); - } - - private List retrieveJanuaryAISVitalFolders() - { - String typeQuery = "TYPE:\"" + TYPE_RECORD_FOLDER + "\" AND @cm\\:name:\"January AIS Audit Records\""; - ResultSet types = this.searchService.query(SPACES_STORE, SearchService.LANGUAGE_LUCENE, typeQuery); - - final List resultNodeRefs = types.getNodeRefs(); - types.close(); - return resultNodeRefs; - } - - /** - * Test duplicate id's - */ - public void xxtestDuplicateIDs() - { - List roots = rmService.getFilePlans(); - final NodeRef root = roots.get(0); - setComplete(); - endTransaction(); - - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - String name1 = GUID.generate(); - Map props = new HashMap(2); - props.put(ContentModel.PROP_NAME, name1); - props.put(PROP_IDENTIFIER, "bob"); - ChildAssociationRef assoc = nodeService.createNode( - root, - ContentModel.ASSOC_CONTAINS, - QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, name1), - TYPE_RECORD_CATEGORY, - props); - - return assoc.getChildRef(); - } - }); - - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - try - { - String name1 = GUID.generate(); - Map props = new HashMap(2); - props.put(ContentModel.PROP_NAME, name1); - props.put(PROP_IDENTIFIER, "bob"); - ChildAssociationRef assoc = nodeService.createNode( - root, - ContentModel.ASSOC_CONTAINS, - QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, name1), - TYPE_RECORD_CATEGORY, - props); - fail("Cant duplicate series id"); - } - catch (Exception e) - { - // expected - } - - return null; - } - }); - } - - public void testDispositionLifecycle_0318_01_basictest() throws Exception - { - final NodeRef recordCategory = TestUtilities.getRecordCategory(rmService, nodeService, "Reports", "AIS Audit Records"); - setComplete(); - endTransaction(); - - final NodeRef recordFolder = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - - assertNotNull(recordCategory); - assertEquals("AIS Audit Records", nodeService.getProperty(recordCategory, ContentModel.PROP_NAME)); - - return createRecordFolder(recordCategory, "March AIS Audit Records"); - } - }); - - final NodeRef recordOne = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - // Check the folder to ensure everything has been inherited correctly - assertTrue(((Boolean)nodeService.getProperty(recordFolder, PROP_VITAL_RECORD_INDICATOR)).booleanValue()); - assertEquals(nodeService.getProperty(recordCategory, PROP_REVIEW_PERIOD), - nodeService.getProperty(recordFolder, PROP_REVIEW_PERIOD)); - - // Create the document - Map props = new HashMap(1); - props.put(ContentModel.PROP_NAME, "MyRecord.txt"); - NodeRef recordOne = nodeService.createNode(recordFolder, - ContentModel.ASSOC_CONTAINS, - QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "MyRecord.txt"), - ContentModel.TYPE_CONTENT).getChildRef(); - - // Set the content - ContentWriter writer = contentService.getWriter(recordOne, ContentModel.PROP_CONTENT, true); - writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); - writer.setEncoding("UTF-8"); - writer.putContent("There is some content in this record"); - - return recordOne; - } - }); - - // Checked that the document has been marked as incomplete - System.out.println("recordOne ..."); - - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - assertTrue(nodeService.hasAspect(recordOne, ASPECT_RECORD)); - assertNotNull(nodeService.getProperty(recordOne, PROP_IDENTIFIER)); - System.out.println("Record id: " + nodeService.getProperty(recordOne, PROP_IDENTIFIER)); - assertNotNull(nodeService.getProperty(recordOne, PROP_DATE_FILED)); - System.out.println("Date filed: " + nodeService.getProperty(recordOne, PROP_DATE_FILED)); - - // Check the review schedule - assertTrue(nodeService.hasAspect(recordOne, ASPECT_VITAL_RECORD)); - assertNotNull(nodeService.getProperty(recordOne, PROP_REVIEW_AS_OF)); - System.out.println("Review as of: " + nodeService.getProperty(recordOne, PROP_REVIEW_AS_OF)); - - // Change the review asOf date - Date nowDate = new Date(); - assertFalse(nowDate.equals(nodeService.getProperty(recordOne, PROP_REVIEW_AS_OF))); - Map reviewAsOfParams = new HashMap(1); - reviewAsOfParams.put(EditReviewAsOfDateAction.PARAM_AS_OF_DATE, nowDate); - rmActionService.executeRecordsManagementAction(recordOne, "editReviewAsOfDate", reviewAsOfParams); - assertTrue(nowDate.equals(nodeService.getProperty(recordOne, PROP_REVIEW_AS_OF))); - - // NOTE the disposition is being managed at a folder level ... - - // Check the disposition action - assertFalse(nodeService.hasAspect(recordOne, ASPECT_DISPOSITION_LIFECYCLE)); - assertTrue(nodeService.hasAspect(recordFolder, ASPECT_DISPOSITION_LIFECYCLE)); - - NodeRef ndNodeRef = nodeService.getChildAssocs(recordFolder, ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); - assertNotNull(ndNodeRef); - - assertNotNull(nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_ACTION_ID)); - System.out.println("Disposition action id: " + nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_ACTION_ID)); - assertEquals("cutoff", nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_ACTION)); - System.out.println("Disposition action: " + nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_ACTION)); - assertNotNull(nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF)); - System.out.println("Disposition as of: " + nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF)); - - // Check for the search properties having been populated - checkSearchAspect(recordFolder); - - // Test the declaration of a record by editing properties - Map propValues = new HashMap(); - propValues.put(RecordsManagementModel.PROP_PUBLICATION_DATE, new Date()); - List smList = new ArrayList(2); - smList.add(FOUO); - smList.add(NOFORN); - propValues.put(RecordsManagementModel.PROP_SUPPLEMENTAL_MARKING_LIST, (Serializable)smList); - propValues.put(RecordsManagementModel.PROP_MEDIA_TYPE, "mediaTypeValue"); - propValues.put(RecordsManagementModel.PROP_FORMAT, "formatValue"); - propValues.put(RecordsManagementModel.PROP_DATE_RECEIVED, new Date()); - nodeService.addProperties(recordOne, propValues); - - return null; - } - }); - - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - // Try and declare, expected failure - try - { - rmActionService.executeRecordsManagementAction(recordOne, "declareRecord"); - fail("Should not be able to declare a record that still has mandatory properties unset"); - } - catch (Exception e) - { - // Expected - } - - return null; - } - }); - - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - @SuppressWarnings("deprecation") - public Object execute() throws Throwable - { - assertTrue("Before test DECLARED aspect was set", - nodeService.hasAspect(recordOne, ASPECT_DECLARED_RECORD) == false); - - nodeService.setProperty(recordOne, RecordsManagementModel.PROP_ORIGINATOR, "origValue"); - nodeService.setProperty(recordOne, RecordsManagementModel.PROP_ORIGINATING_ORGANIZATION, "origOrgValue"); - nodeService.setProperty(recordOne, ContentModel.PROP_TITLE, "titleValue"); - - // Declare the record as we have set everything we should have - rmActionService.executeRecordsManagementAction(recordOne, "declareRecord"); - assertTrue(" the record is not declared", nodeService.hasAspect(recordOne, ASPECT_DECLARED_RECORD)); - - // check that the declaredAt and declaredBy properties are set - assertNotNull(nodeService.getProperty(recordOne, PROP_DECLARED_BY)); - assertEquals("admin", nodeService.getProperty(recordOne, PROP_DECLARED_BY)); - assertNotNull(nodeService.getProperty(recordOne, PROP_DECLARED_AT)); - Date dateNow = new Date(); - Date declaredDate = (Date)nodeService.getProperty(recordOne, PROP_DECLARED_AT); - assertEquals(declaredDate.getDate(), dateNow.getDate()); - assertEquals(declaredDate.getMonth(), dateNow.getMonth()); - assertEquals(declaredDate.getYear(), dateNow.getYear()); - - // Check that the history is empty - List history = dispositionService.getCompletedDispositionActions(recordFolder); - assertNotNull(history); - assertEquals(0, history.size()); - - return null; - } - }); - - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - // Execute the cutoff action (should fail because this is being done at the record level) - try - { - rmActionService.executeRecordsManagementAction(recordFolder, "cutoff", null); - fail(("Shouldn't have been able to execute cut off at the record level")); - } - catch (Exception e) - { - // expected - } - - // Execute the cutoff action (should fail becuase it is not yet eligiable) - try - { - rmActionService.executeRecordsManagementAction(recordFolder, "cutoff", null); - fail(("Shouldn't have been able to execute because it is not yet eligiable")); - } - catch (Exception e) - { - // expected - } - - return null; - } - }); - - final Calendar calendar = Calendar.getInstance(); - calendar.set(Calendar.HOUR, 0); - calendar.set(Calendar.MINUTE, 0); - calendar.set(Calendar.SECOND, 0); - - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - // Clock the asOf date back to ensure eligibility - NodeRef ndNodeRef = nodeService.getChildAssocs(recordFolder, ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); - Date nowDate = calendar.getTime(); - assertFalse(nowDate.equals(nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF))); - Map params = new HashMap(1); - params.put(EditDispositionActionAsOfDateAction.PARAM_AS_OF_DATE, nowDate); - rmActionService.executeRecordsManagementAction(recordFolder, "editDispositionActionAsOfDate", params); - assertTrue(nowDate.equals(nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF))); - - // Cut off - rmActionService.executeRecordsManagementAction(recordFolder, "cutoff", null); - - return null; - } - }); - - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - // Check the disposition action - assertFalse(nodeService.hasAspect(recordOne, ASPECT_DISPOSITION_LIFECYCLE)); - assertTrue(nodeService.hasAspect(recordFolder, ASPECT_DISPOSITION_LIFECYCLE)); - - NodeRef ndNodeRef = nodeService.getChildAssocs(recordFolder, ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); - assertNotNull(ndNodeRef); - - assertNotNull(nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_ACTION_ID)); - System.out.println("Disposition action id: " + nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_ACTION_ID)); - assertEquals("destroy", nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_ACTION)); - System.out.println("Disposition action: " + nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_ACTION)); - assertNotNull(nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF)); - System.out.println("Disposition as of: " + nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF)); - assertNull(nodeService.getProperty(recordFolder, RecordsManagementSearchBehaviour.PROP_RS_DISPOSITION_EVENTS)); - - // Check the previous action details - checkLastDispositionAction(recordFolder, "cutoff", 1); - - // Check for the search properties having been populated - checkSearchAspect(recordFolder); - - // Clock the asOf date back to ensure eligibility - ndNodeRef = nodeService.getChildAssocs(recordFolder, ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); - Date nowDate = calendar.getTime(); - assertFalse(nowDate.equals(nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF))); - Map params = new HashMap(1); - params.put(EditDispositionActionAsOfDateAction.PARAM_AS_OF_DATE, nowDate); - rmActionService.executeRecordsManagementAction(recordFolder, "editDispositionActionAsOfDate", params); - assertTrue(nowDate.equals(nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF))); - - rmActionService.executeRecordsManagementAction(recordFolder, "destroy", null); - - // Check that the node has been destroyed (ghosted) - //assertFalse(nodeService.exists(recordFolder)); - //assertFalse(nodeService.exists(recordOne)); - assertTrue(nodeService.hasAspect(recordFolder, ASPECT_GHOSTED)); - assertTrue(nodeService.hasAspect(recordOne, ASPECT_GHOSTED)); - - // Check the history - if (nodeService.exists(recordFolder) == true) - { - checkLastDispositionAction(recordFolder, "destroy", 2); - } - - return null; - } - }); - } - - /** - * Tests the re-scheduling of disposition lifecycles when the schedule changes - */ - public void testDispositionLifecycle_0318_reschedule_folderlevel() throws Exception - { - final NodeRef recordSeries = TestUtilities.getRecordSeries(rmService, nodeService, "Reports"); - setComplete(); - endTransaction(); - - // create a category - final NodeRef recordCategory = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - assertNotNull(recordSeries); - assertEquals("Reports", nodeService.getProperty(recordSeries, ContentModel.PROP_NAME)); - - return createRecordCategoryNode(recordSeries); - } - }); - - // define the disposition schedule for the category (Cut off monthly, hold 1 month, then destroy) - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - assertNotNull(recordCategory); - - DispositionSchedule schedule = dispositionService.getDispositionSchedule(recordCategory); - assertNotNull(schedule); - nodeService.setProperty(schedule.getNodeRef(), PROP_DISPOSITION_INSTRUCTIONS, "Cutoff after 1 month then destroy after 1 month"); - nodeService.setProperty(schedule.getNodeRef(), PROP_DISPOSITION_AUTHORITY, "Alfresco"); - - // define properties for both steps - Map step1 = new HashMap(); - step1.put(RecordsManagementModel.PROP_DISPOSITION_ACTION_NAME, "cutoff"); - step1.put(RecordsManagementModel.PROP_DISPOSITION_DESCRIPTION, "Cutoff after 1 month"); - step1.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD, "month|1"); - - Map step2 = new HashMap(); - step2.put(RecordsManagementModel.PROP_DISPOSITION_ACTION_NAME, "destroy"); - step2.put(RecordsManagementModel.PROP_DISPOSITION_DESCRIPTION, "Destroy after 1 month"); - step2.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD, "month|1"); - step2.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD_PROPERTY, PROP_CUT_OFF_DATE); - - // add the action definitions to the schedule - dispositionService.addDispositionActionDefinition(schedule, step1); - dispositionService.addDispositionActionDefinition(schedule, step2); - - return null; - } - }); - - // create a record folder - final NodeRef recordFolder = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - return createRecordFolder(recordCategory, "Folder1"); - } - }); - - // make sure the disposition lifecycle is present and correct - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - @SuppressWarnings("deprecation") - public Object execute() throws Throwable - { - assertNotNull(recordFolder); - - assertTrue(nodeService.hasAspect(recordFolder, ASPECT_DISPOSITION_LIFECYCLE)); - NodeRef ndNodeRef = nodeService.getChildAssocs(recordFolder, ASSOC_NEXT_DISPOSITION_ACTION, - RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); - assertNotNull(ndNodeRef); - - assertNotNull(nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_ACTION_ID)); - System.out.println("Disposition action id: " + nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_ACTION_ID)); - assertEquals("cutoff", nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_ACTION)); - System.out.println("Disposition action: " + nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_ACTION)); - assertNotNull(nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF)); - Date asOfDate = (Date)nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF); - System.out.println("Disposition as of: " + asOfDate); - - // make sure the as of date is a month in the future - Calendar cal = Calendar.getInstance(); - cal.add(Calendar.MONTH, 1); - int monthThen = cal.get(Calendar.MONTH); - assertEquals(asOfDate.getMonth(), monthThen);; - - // make sure there aren't any events - List events = nodeService.getChildAssocs(ndNodeRef, ASSOC_EVENT_EXECUTIONS, - RegexQNamePattern.MATCH_ALL); - assertEquals(0, events.size()); - - // Check for the search properties having been populated - checkSearchAspect(recordFolder); - - return null; - } - }); - - // change the period on the 1st step of the disposition schedule and make sure it perculates down - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - // define changes for schedule - Map changes = new HashMap(); - changes.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD, "month|3"); - - // update the second dispostion action definition - DispositionSchedule schedule = dispositionService.getDispositionSchedule(recordCategory); - assertNotNull(schedule); - List actionDefs = schedule.getDispositionActionDefinitions(); - assertEquals(2, actionDefs.size()); - System.out.println("Adding 3 months to period for 1st step: " + actionDefs.get(0).getName()); - updateDispositionActionDefinition(schedule, actionDefs.get(0), changes); - - return null; - } - }); - - // make sure the disposition lifecycle asOf date has been updated - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - @SuppressWarnings("deprecation") - public Object execute() throws Throwable - { - assertTrue(nodeService.hasAspect(recordFolder, ASPECT_DISPOSITION_LIFECYCLE)); - NodeRef ndNodeRef = nodeService.getChildAssocs(recordFolder, ASSOC_NEXT_DISPOSITION_ACTION, - RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); - assertNotNull(ndNodeRef); - - assertNotNull(nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF)); - Date asOfDate = (Date)nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF); - System.out.println("Disposition as of: " + asOfDate); - - // make sure the as of date is a month in the future - Calendar calendar = Calendar.getInstance(); - calendar.add(Calendar.MONTH, 3); - System.out.println("Test date: " + calendar.getTime()); - Calendar asOfCalendar = Calendar.getInstance(); - asOfCalendar.setTime(asOfDate); - assertEquals(calendar.get(Calendar.MONTH), asOfCalendar.get(Calendar.MONTH)); - - // Check for the search properties having been populated - checkSearchAspect(recordFolder); - - return null; - } - }); - - // change the period on the 2nd step of the disposition schedule and make sure it DOES NOT perculate down - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - // define changes for schedule - Map changes = new HashMap(); - changes.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD, "month|6"); - - // update the first dispostion action definition - DispositionSchedule schedule = dispositionService.getDispositionSchedule(recordCategory); - assertNotNull(schedule); - List actionDefs = schedule.getDispositionActionDefinitions(); - assertEquals(2, actionDefs.size()); - System.out.println("Adding 6 months to period for 2nd step: " + actionDefs.get(1).getName()); - updateDispositionActionDefinition(schedule, actionDefs.get(1), changes); - - return null; - } - }); - - // make sure the disposition lifecycle asOf date has NOT been updated as the period was - // changed for a step other than the current one - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - @SuppressWarnings("deprecation") - public Object execute() throws Throwable - { - assertTrue(nodeService.hasAspect(recordFolder, ASPECT_DISPOSITION_LIFECYCLE)); - NodeRef ndNodeRef = nodeService.getChildAssocs(recordFolder, ASSOC_NEXT_DISPOSITION_ACTION, - RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); - assertNotNull(ndNodeRef); - - assertNotNull(nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF)); - Date asOfDate = (Date)nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF); - System.out.println("Disposition as of: " + asOfDate); - - // make sure the as of date is a month in the future - Calendar calendar = Calendar.getInstance(); - calendar.add(Calendar.MONTH, 3); - assertEquals("Expecting the asOf date to be unchanged",asOfDate.getMonth(), calendar.get(Calendar.MONTH)); - - // Check for the search properties having been populated - checkSearchAspect(recordFolder); - - return null; - } - }); - - // change the disposition schedule to be event based rather than time based i.e. - // remove the period properties and supply 2 events in its place. - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - // define changes for schedule - Map changes = new HashMap(); - changes.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD, null); - List events = new ArrayList(2); - events.add("no_longer_needed"); - events.add("case_complete"); - changes.put(RecordsManagementModel.PROP_DISPOSITION_EVENT, (Serializable)events); - - // update the first dispostion action definition - DispositionSchedule schedule = dispositionService.getDispositionSchedule(recordCategory); - assertNotNull(schedule); - List actionDefs = schedule.getDispositionActionDefinitions(); - assertEquals(2, actionDefs.size()); - System.out.println("Removing period and adding no_longer_needed and case_complete to 1st step: " + - actionDefs.get(0).getName()); - updateDispositionActionDefinition(schedule, actionDefs.get(0), changes); - - return null; - } - }); - - // make sure the disposition lifecycle asOf date has been reset and there are now - // events hanging off the nextdispositionaction node - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - assertTrue(nodeService.hasAspect(recordFolder, ASPECT_DISPOSITION_LIFECYCLE)); - NodeRef ndNodeRef = nodeService.getChildAssocs(recordFolder, ASSOC_NEXT_DISPOSITION_ACTION, - RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); - assertNotNull(ndNodeRef); - - Date asOfDate = (Date)nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF); - System.out.println("New disposition as of: " + asOfDate); - assertNull("Expecting asOfDate to be null", asOfDate); - - // make sure the 2 events are present - List events = nodeService.getChildAssocs(ndNodeRef, ASSOC_EVENT_EXECUTIONS, - RegexQNamePattern.MATCH_ALL); - assertEquals(2, events.size()); - NodeRef event1 = events.get(0).getChildRef(); - assertEquals("no_longer_needed", nodeService.getProperty(event1, PROP_EVENT_EXECUTION_NAME)); - NodeRef event2 = events.get(1).getChildRef(); - assertEquals("case_complete", nodeService.getProperty(event2, PROP_EVENT_EXECUTION_NAME)); - - // Check for the search properties having been populated - checkSearchAspect(recordFolder, false); - - return null; - } - }); - - // remove one of the events just added - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - // define changes for schedule - Map changes = new HashMap(); - List events = new ArrayList(2); - events.add("case_complete"); - changes.put(RecordsManagementModel.PROP_DISPOSITION_EVENT, (Serializable)events); - - // update the first dispostion action definition - DispositionSchedule schedule = dispositionService.getDispositionSchedule(recordCategory); - assertNotNull(schedule); - List actionDefs = schedule.getDispositionActionDefinitions(); - assertEquals(2, actionDefs.size()); - System.out.println("Removing no_longer_needed event from 1st step: " + - actionDefs.get(0).getName()); - updateDispositionActionDefinition(schedule, actionDefs.get(0), changes); - - return null; - } - }); - - // make sure the disposition lifecycle asOf date is still null and ensure there is only one event - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - assertTrue(nodeService.hasAspect(recordFolder, ASPECT_DISPOSITION_LIFECYCLE)); - NodeRef ndNodeRef = nodeService.getChildAssocs(recordFolder, ASSOC_NEXT_DISPOSITION_ACTION, - RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); - assertNotNull(ndNodeRef); - - Date asOfDate = (Date)nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF); - assertNull("Expecting asOfDate to be null", asOfDate); - - // make sure only 1 event is present - List events = nodeService.getChildAssocs(ndNodeRef, ASSOC_EVENT_EXECUTIONS, - RegexQNamePattern.MATCH_ALL); - assertEquals(1, events.size()); - NodeRef event = events.get(0).getChildRef(); - assertEquals("case_complete", nodeService.getProperty(event, PROP_EVENT_EXECUTION_NAME)); - - // Check for the search properties having been populated - checkSearchAspect(recordFolder, false); - - return null; - } - }); - } - - /** - * Tests the re-scheduling of disposition lifecycles when the schedule changes - */ - public void testDispositionLifecycle_0318_reschedule_recordlevel() throws Exception - { - final NodeRef recordSeries = TestUtilities.getRecordSeries(rmService, nodeService, "Reports"); - setComplete(); - endTransaction(); - - // create a category - final NodeRef recordCategory = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - assertNotNull(recordSeries); - assertEquals("Reports", nodeService.getProperty(recordSeries, ContentModel.PROP_NAME)); - - return createRecordCategoryNode(recordSeries); - } - }); - - // define the disposition schedule for the category (Cut off monthly, hold 1 month, then destroy) - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - assertNotNull(recordCategory); - - // get the disposition schedule and turn on record level disposition - DispositionSchedule schedule = dispositionService.getDispositionSchedule(recordCategory); - assertNotNull(schedule); - nodeService.setProperty(schedule.getNodeRef(), PROP_DISPOSITION_INSTRUCTIONS, "Cutoff after 1 month then destroy after 1 month"); - nodeService.setProperty(schedule.getNodeRef(), PROP_DISPOSITION_AUTHORITY, "Alfresco"); - nodeService.setProperty(schedule.getNodeRef(), PROP_RECORD_LEVEL_DISPOSITION, true); - - // define properties for both steps - Map step1 = new HashMap(); - step1.put(RecordsManagementModel.PROP_DISPOSITION_ACTION_NAME, "cutoff"); - step1.put(RecordsManagementModel.PROP_DISPOSITION_DESCRIPTION, "Cutoff after 1 month"); - step1.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD, "month|1"); - - Map step2 = new HashMap(); - step2.put(RecordsManagementModel.PROP_DISPOSITION_ACTION_NAME, "destroy"); - step2.put(RecordsManagementModel.PROP_DISPOSITION_DESCRIPTION, "Destroy after 1 month"); - step2.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD, "month|1"); - step2.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD_PROPERTY, PROP_CUT_OFF_DATE); - - // add the action definitions to the schedule - dispositionService.addDispositionActionDefinition(schedule, step1); - dispositionService.addDispositionActionDefinition(schedule, step2); - - return null; - } - }); - - // create a record folder - final NodeRef recordFolder = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - return createRecordFolder(recordCategory, "Folder1"); - } - }); - - // create a record - final NodeRef record = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - assertNotNull(recordFolder); - - // Create the document - Map props = new HashMap(1); - props.put(ContentModel.PROP_NAME, "MyRecord.txt"); - NodeRef record = nodeService.createNode(recordFolder, - ContentModel.ASSOC_CONTAINS, - QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "MyRecord.txt"), - ContentModel.TYPE_CONTENT).getChildRef(); - - // Set the content - ContentWriter writer = contentService.getWriter(record, ContentModel.PROP_CONTENT, true); - writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); - writer.setEncoding("UTF-8"); - writer.putContent("There is some content in this record"); - - return record; - } - }); - - // make sure the disposition lifecycle is present and correct on the record and not on the folder - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - @SuppressWarnings("deprecation") - public Object execute() throws Throwable - { - assertNotNull(record); - - assertFalse(nodeService.hasAspect(recordFolder, ASPECT_DISPOSITION_LIFECYCLE)); - assertTrue(nodeService.hasAspect(record, ASPECT_DISPOSITION_LIFECYCLE)); - NodeRef ndNodeRef = nodeService.getChildAssocs(record, ASSOC_NEXT_DISPOSITION_ACTION, - RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); - assertNotNull(ndNodeRef); - - assertNotNull(nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_ACTION_ID)); - System.out.println("Disposition action id: " + nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_ACTION_ID)); - assertEquals("cutoff", nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_ACTION)); - System.out.println("Disposition action: " + nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_ACTION)); - assertNotNull(nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF)); - Date asOfDate = (Date)nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF); - System.out.println("Disposition as of: " + asOfDate); - - // make sure the as of date is a month in the future - Calendar cal = Calendar.getInstance(); - cal.add(Calendar.MONTH, 1); - int monthThen = cal.get(Calendar.MONTH); - assertEquals(asOfDate.getMonth(), monthThen); - - // make sure there aren't any events - List events = nodeService.getChildAssocs(ndNodeRef, ASSOC_EVENT_EXECUTIONS, - RegexQNamePattern.MATCH_ALL); - assertEquals(0, events.size()); - - // Check for the search properties having been populated - checkSearchAspect(record); - - return null; - } - }); - - // change the period on the 1st step of the disposition schedule and make sure it perculates down - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - // define changes for schedule - Map changes = new HashMap(); - changes.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD, "month|3"); - - // update the second dispostion action definition - DispositionSchedule schedule = dispositionService.getDispositionSchedule(recordCategory); - assertNotNull(schedule); - List actionDefs = schedule.getDispositionActionDefinitions(); - assertEquals(2, actionDefs.size()); - System.out.println("Adding 3 months to period for 1st step: " + actionDefs.get(0).getName()); - updateDispositionActionDefinition(schedule, actionDefs.get(0), changes); - - return null; - } - }); - - // make sure the disposition lifecycle asOf date has been updated - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - @SuppressWarnings("deprecation") - public Object execute() throws Throwable - { - assertTrue(nodeService.hasAspect(record, ASPECT_DISPOSITION_LIFECYCLE)); - NodeRef ndNodeRef = nodeService.getChildAssocs(record, ASSOC_NEXT_DISPOSITION_ACTION, - RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); - assertNotNull(ndNodeRef); - - assertNotNull(nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF)); - Date asOfDate = (Date)nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF); - System.out.println("Disposition as of: " + asOfDate); - - // make sure the as of date is a month in the future - Calendar calendar = Calendar.getInstance(); - calendar.add(Calendar.MONTH, 3); - assertEquals(asOfDate.getMonth(), calendar.get(Calendar.MONTH)); - - // Check for the search properties having been populated - checkSearchAspect(record); - - return null; - } - }); - - // change the period on the 2nd step of the disposition schedule and make sure it DOES NOT perculate down - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - // define changes for schedule - Map changes = new HashMap(); - changes.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD, "month|6"); - - // update the first dispostion action definition - DispositionSchedule schedule = dispositionService.getDispositionSchedule(recordCategory); - assertNotNull(schedule); - List actionDefs = schedule.getDispositionActionDefinitions(); - assertEquals(2, actionDefs.size()); - System.out.println("Adding 6 months to period for 2nd step: " + actionDefs.get(1).getName()); - updateDispositionActionDefinition(schedule, actionDefs.get(1), changes); - - return null; - } - }); - - // make sure the disposition lifecycle asOf date has NOT been updated as the period was - // changed for a step other than the current one - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - @SuppressWarnings("deprecation") - public Object execute() throws Throwable - { - assertTrue(nodeService.hasAspect(record, ASPECT_DISPOSITION_LIFECYCLE)); - NodeRef ndNodeRef = nodeService.getChildAssocs(record, ASSOC_NEXT_DISPOSITION_ACTION, - RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); - assertNotNull(ndNodeRef); - - assertNotNull(nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF)); - Date asOfDate = (Date)nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF); - System.out.println("Disposition as of: " + asOfDate); - - // make sure the as of date is a month in the future - Calendar calendar = Calendar.getInstance(); - calendar.add(Calendar.MONTH, 3); - assertEquals("Expecting the asOf date to be unchanged", asOfDate.getMonth(), calendar.get(Calendar.MONTH)); - - // Check for the search properties having been populated - checkSearchAspect(record); - - return null; - } - }); - - // change the disposition schedule to be event based rather than time based i.e. - // remove the period properties and supply 2 events in its place. - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - // define changes for schedule - Map changes = new HashMap(); - changes.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD, null); - List events = new ArrayList(2); - events.add("no_longer_needed"); - events.add("case_complete"); - changes.put(RecordsManagementModel.PROP_DISPOSITION_EVENT, (Serializable)events); - - // update the first dispostion action definition - DispositionSchedule schedule = dispositionService.getDispositionSchedule(recordCategory); - assertNotNull(schedule); - List actionDefs = schedule.getDispositionActionDefinitions(); - assertEquals(2, actionDefs.size()); - System.out.println("Removing period and adding no_longer_needed and case_complete to 1st step: " + - actionDefs.get(0).getName()); - updateDispositionActionDefinition(schedule, actionDefs.get(0), changes); - - return null; - } - }); - - // make sure the disposition lifecycle asOf date has been reset and there are now - // events hanging off the nextdispositionaction node - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - assertTrue(nodeService.hasAspect(record, ASPECT_DISPOSITION_LIFECYCLE)); - NodeRef ndNodeRef = nodeService.getChildAssocs(record, ASSOC_NEXT_DISPOSITION_ACTION, - RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); - assertNotNull(ndNodeRef); - - Date asOfDate = (Date)nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF); - System.out.println("New disposition as of: " + asOfDate); - assertNull("Expecting asOfDate to be null", asOfDate); - - // make sure the 2 events are present - List events = nodeService.getChildAssocs(ndNodeRef, ASSOC_EVENT_EXECUTIONS, - RegexQNamePattern.MATCH_ALL); - assertEquals(2, events.size()); - NodeRef event1 = events.get(0).getChildRef(); - assertEquals("no_longer_needed", nodeService.getProperty(event1, PROP_EVENT_EXECUTION_NAME)); - NodeRef event2 = events.get(1).getChildRef(); - assertEquals("case_complete", nodeService.getProperty(event2, PROP_EVENT_EXECUTION_NAME)); - - // Check for the search properties having been populated - checkSearchAspect(record, false); - - return null; - } - }); - - // remove one of the events just added - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - // define changes for schedule - Map changes = new HashMap(); - List events = new ArrayList(2); - events.add("case_complete"); - changes.put(RecordsManagementModel.PROP_DISPOSITION_EVENT, (Serializable)events); - - // update the first dispostion action definition - DispositionSchedule schedule = dispositionService.getDispositionSchedule(recordCategory); - assertNotNull(schedule); - List actionDefs = schedule.getDispositionActionDefinitions(); - assertEquals(2, actionDefs.size()); - System.out.println("Removing no_longer_needed event from 1st step: " + - actionDefs.get(0).getName()); - updateDispositionActionDefinition(schedule, actionDefs.get(0), changes); - - return null; - } - }); - - // make sure the disposition lifecycle asOf date is still null and ensure there is only one event - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - assertTrue(nodeService.hasAspect(record, ASPECT_DISPOSITION_LIFECYCLE)); - NodeRef ndNodeRef = nodeService.getChildAssocs(record, ASSOC_NEXT_DISPOSITION_ACTION, - RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); - assertNotNull(ndNodeRef); - - Date asOfDate = (Date)nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF); - assertNull("Expecting asOfDate to be null", asOfDate); - - // make sure only 1 event is present - List events = nodeService.getChildAssocs(ndNodeRef, ASSOC_EVENT_EXECUTIONS, - RegexQNamePattern.MATCH_ALL); - assertEquals(1, events.size()); - NodeRef event = events.get(0).getChildRef(); - assertEquals("case_complete", nodeService.getProperty(event, PROP_EVENT_EXECUTION_NAME)); - - // Check for the search properties having been populated - checkSearchAspect(record, false); - - return null; - } - }); - - // change the action on the first step from 'cutoff' to 'retain' - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - // define changes for schedule - Map changes = new HashMap(); - changes.put(RecordsManagementModel.PROP_DISPOSITION_ACTION_NAME, "retain"); - - // update the first dispostion action definition - DispositionSchedule schedule = dispositionService.getDispositionSchedule(recordCategory); - assertNotNull(schedule); - List actionDefs = schedule.getDispositionActionDefinitions(); - assertEquals(2, actionDefs.size()); - System.out.println("Changing action of 1st step from '" + - actionDefs.get(0).getName() + "' to 'retain'"); - updateDispositionActionDefinition(schedule, actionDefs.get(0), changes); - - return null; - } - }); - - // make sure the disposition lifecycle asOf date is still null, ensure there is still only one event - // and most importantly that the action name is now 'retain' - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - assertTrue(nodeService.hasAspect(record, ASPECT_DISPOSITION_LIFECYCLE)); - NodeRef ndNodeRef = nodeService.getChildAssocs(record, ASSOC_NEXT_DISPOSITION_ACTION, - RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); - assertNotNull(ndNodeRef); - - Date asOfDate = (Date)nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF); - assertNull("Expecting asOfDate to be null", asOfDate); - - // make sure only 1 event is present - List events = nodeService.getChildAssocs(ndNodeRef, ASSOC_EVENT_EXECUTIONS, - RegexQNamePattern.MATCH_ALL); - assertEquals(1, events.size()); - NodeRef event = events.get(0).getChildRef(); - assertEquals("case_complete", nodeService.getProperty(event, PROP_EVENT_EXECUTION_NAME)); - - String actionName = (String)nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_ACTION); - assertEquals("retain", actionName); - - // Check for the search properties having been populated - checkSearchAspect(record, false); - - return null; - } - }); - } - - private void updateDispositionActionDefinition(DispositionSchedule schedule, DispositionActionDefinition actionDefinition, Map actionDefinitionParams) - { - NodeRef nodeRef = actionDefinition.getNodeRef(); - Map before = nodeService.getProperties(nodeRef); - nodeService.addProperties(nodeRef, actionDefinitionParams); - Map after = nodeService.getProperties(nodeRef); - List updatedProps = determineChangedProps(before, after); - - refreshDispositionActionDefinition(nodeRef, updatedProps); - } - - private void refreshDispositionActionDefinition(NodeRef nodeRef, List updatedProps) - { - if (updatedProps != null) - { - Map params = new HashMap(); - params.put(BroadcastDispositionActionDefinitionUpdateAction.CHANGED_PROPERTIES, (Serializable)updatedProps); - rmActionService.executeRecordsManagementAction(nodeRef, BroadcastDispositionActionDefinitionUpdateAction.NAME, params); - } - - // Remove the unpublished update aspect - nodeService.removeAspect(nodeRef, ASPECT_UNPUBLISHED_UPDATE); - } - - private List determineChangedProps(Map oldProps, Map newProps) - { - List result = new ArrayList(); - for (QName qn : oldProps.keySet()) - { - if (newProps.get(qn) == null || - newProps.get(qn).equals(oldProps.get(qn)) == false) - { - result.add(qn); - } - } - for (QName qn : newProps.keySet()) - { - if (oldProps.get(qn) == null) - { - result.add(qn); - } - } - - return result; - } - - /** - * Tests the re-scheduling of disposition lifecycles when steps from the schedule are deleted - * (when using folder level disposition) - */ - public void testDispositionLifecycle_0318_reschedule_deletion_folderlevel() throws Exception - { - final NodeRef recordSeries = TestUtilities.getRecordSeries(rmService, nodeService, "Reports"); - setComplete(); - endTransaction(); - - // create a category - final NodeRef recordCategory = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - assertNotNull(recordSeries); - assertEquals("Reports", nodeService.getProperty(recordSeries, ContentModel.PROP_NAME)); - - return createRecordCategoryNode(recordSeries); - } - }); - - // define the disposition schedule for the category with several steps - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - assertNotNull(recordCategory); - - // define properties for both steps - Map step1 = new HashMap(); - step1.put(RecordsManagementModel.PROP_DISPOSITION_ACTION_NAME, "cutoff"); - step1.put(RecordsManagementModel.PROP_DISPOSITION_DESCRIPTION, "Cutoff when no longer needed"); - step1.put(RecordsManagementModel.PROP_DISPOSITION_EVENT, "no_longer_needed"); - - Map step2 = new HashMap(); - step2.put(RecordsManagementModel.PROP_DISPOSITION_ACTION_NAME, "transfer"); - step2.put(RecordsManagementModel.PROP_DISPOSITION_DESCRIPTION, "Transfer after 1 month"); - step2.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD, "month|1"); - step2.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD_PROPERTY, PROP_DISPOSITION_AS_OF); - - Map step3 = new HashMap(); - step3.put(RecordsManagementModel.PROP_DISPOSITION_ACTION_NAME, "destroy"); - step3.put(RecordsManagementModel.PROP_DISPOSITION_DESCRIPTION, "Destroy after 1 year"); - step3.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD, "year|1"); - step3.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD_PROPERTY, PROP_DISPOSITION_AS_OF); - - // add the action definitions to the schedule - DispositionSchedule schedule = dispositionService.getDispositionSchedule(recordCategory); - assertNotNull(schedule); - dispositionService.addDispositionActionDefinition(schedule, step1); - dispositionService.addDispositionActionDefinition(schedule, step2); - dispositionService.addDispositionActionDefinition(schedule, step3); - - return null; - } - }); - - // create first record folder - final NodeRef recordFolder1 = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - return createRecordFolder(recordCategory, "Folder1"); - } - }); - - // create second record folder - final NodeRef recordFolder2 = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - return createRecordFolder(recordCategory, "Folder2"); - } - }); - - // make sure the disposition lifecycle is present and correct - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - assertNotNull(recordFolder1); - assertNotNull(recordFolder2); - - assertTrue(nodeService.hasAspect(recordFolder1, ASPECT_DISPOSITION_LIFECYCLE)); - assertTrue(nodeService.hasAspect(recordFolder2, ASPECT_DISPOSITION_LIFECYCLE)); - NodeRef folder1NextAction = nodeService.getChildAssocs(recordFolder1, ASSOC_NEXT_DISPOSITION_ACTION, - RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); - assertNotNull(folder1NextAction); - NodeRef folder2NextAction = nodeService.getChildAssocs(recordFolder2, ASSOC_NEXT_DISPOSITION_ACTION, - RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); - assertNotNull(folder2NextAction); - - // make sure both folders are on the cutoff step - assertEquals("cutoff", nodeService.getProperty(folder1NextAction, PROP_DISPOSITION_ACTION)); - assertEquals("cutoff", nodeService.getProperty(folder2NextAction, PROP_DISPOSITION_ACTION)); - - // make sure both folders have 1 event - assertEquals(1, nodeService.getChildAssocs(folder1NextAction, ASSOC_EVENT_EXECUTIONS, - RegexQNamePattern.MATCH_ALL).size()); - assertEquals(1, nodeService.getChildAssocs(folder2NextAction, ASSOC_EVENT_EXECUTIONS, - RegexQNamePattern.MATCH_ALL).size()); - - // move folder 2 onto next step - Map params = new HashMap(3); - params.put(CompleteEventAction.PARAM_EVENT_NAME, "no_longer_needed"); - params.put(CompleteEventAction.PARAM_EVENT_COMPLETED_AT, new Date()); - params.put(CompleteEventAction.PARAM_EVENT_COMPLETED_BY, "gavinc"); - - AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); - rmActionService.executeRecordsManagementAction(recordFolder2, "completeEvent", params); - rmActionService.executeRecordsManagementAction(recordFolder2, "cutoff"); - AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); - - return null; - } - }); - - // check the second folder is at step 2 and then attempt to remove a step from the disposition schedule - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - NodeRef folder2NextAction = nodeService.getChildAssocs(recordFolder2, ASSOC_NEXT_DISPOSITION_ACTION, - RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); - assertNotNull(folder2NextAction); - assertEquals("transfer", (String)nodeService.getProperty(folder2NextAction, PROP_DISPOSITION_ACTION)); - - // check there are 3 steps to the schedule - DispositionSchedule schedule = dispositionService.getDispositionSchedule(recordCategory); - assertNotNull(schedule); - List actionDefs = schedule.getDispositionActionDefinitions(); - assertEquals(3, actionDefs.size()); - - // attempt to remove step 1 from the schedule - try - { - dispositionService.removeDispositionActionDefinition(schedule, actionDefs.get(0)); - fail("Expecting the step deletion to be unsuccessful as record folders are present"); - } - catch (AlfrescoRuntimeException are) - { - // expected as steps are present, deletion not allowed - } - - return null; - } - }); - - // remove both record folders - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - // remove record folders - nodeService.removeChild(recordCategory, recordFolder1); - nodeService.removeChild(recordCategory, recordFolder2); - return null; - } - }); - - // try removing last schedule step - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - // make sure there are 3 steps - DispositionSchedule schedule = dispositionService.getDispositionSchedule(recordCategory); - assertNotNull(schedule); - List actionDefs = schedule.getDispositionActionDefinitions(); - assertEquals(3, actionDefs.size()); - - // remove last step, should be successful this time - dispositionService.removeDispositionActionDefinition(schedule, actionDefs.get(2)); - - // make sure there are now 2 steps - schedule = dispositionService.getDispositionSchedule(recordCategory); - actionDefs = schedule.getDispositionActionDefinitions(); - assertEquals(2, actionDefs.size()); - - return null; - } - }); - - // *** NOTE: The commented out code below is potential tests for the step deletion behaviour *** - // *** we also need to add tests for deleting the step in the process where records or *** - // *** folders are on the last step i.e. what state should they be in if the last step *** - // *** is removed? *** - - /* - // check the second folder is at step 2 and remove the first step from the schedule - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - NodeRef folder2NextAction = nodeService.getChildAssocs(recordFolder2, ASSOC_NEXT_DISPOSITION_ACTION, - RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); - assertNotNull(folder2NextAction); - assertEquals("transfer", (String)nodeService.getProperty(folder2NextAction, PROP_DISPOSITION_ACTION)); - - // remove step 1 from the schedule - DispositionSchedule schedule = rmService.getDispositionSchedule(recordCategory); - assertNotNull(schedule); - List actionDefs = schedule.getDispositionActionDefinitions(); - assertEquals(3, actionDefs.size()); - System.out.println("Removing schedule step 1 named: " + actionDefs.get(0).getName()); - rmService.removeDispositionActionDefinition(schedule, actionDefs.get(0)); - - return null; - } - }); - - // make sure the next action for folder 1 has moved on and folder 2 is unchanged, then delete last step - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - NodeRef folder1NextAction = nodeService.getChildAssocs(recordFolder1, ASSOC_NEXT_DISPOSITION_ACTION, - RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); - assertNotNull(folder1NextAction); - NodeRef folder2NextAction = nodeService.getChildAssocs(recordFolder2, ASSOC_NEXT_DISPOSITION_ACTION, - RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); - assertNotNull(folder2NextAction); - - // make sure both folders are on the cutoff step - assertEquals("transfer", nodeService.getProperty(folder1NextAction, PROP_DISPOSITION_ACTION)); - assertEquals("transfer", nodeService.getProperty(folder2NextAction, PROP_DISPOSITION_ACTION)); - - // Check for the search properties having been populated - checkSearchAspect(folder1NextAction); - checkSearchAspect(folder2NextAction); - - // remove the step in the last position from the schedule - DispositionSchedule schedule = rmService.getDispositionSchedule(recordCategory); - assertNotNull(schedule); - List actionDefs = schedule.getDispositionActionDefinitions(); - assertEquals(2, actionDefs.size()); - System.out.println("Removing schedule last step named: " + actionDefs.get(1).getName()); - rmService.removeDispositionActionDefinition(schedule, actionDefs.get(1)); - - return null; - } - }); - - // check there were no changes, then remove the only remaining step - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - NodeRef folder1NextAction = nodeService.getChildAssocs(recordFolder1, ASSOC_NEXT_DISPOSITION_ACTION, - RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); - assertNotNull(folder1NextAction); - assertEquals("transfer", (String)nodeService.getProperty(folder1NextAction, PROP_DISPOSITION_ACTION)); - - NodeRef folder2NextAction = nodeService.getChildAssocs(recordFolder2, ASSOC_NEXT_DISPOSITION_ACTION, - RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); - assertNotNull(folder2NextAction); - assertEquals("transfer", (String)nodeService.getProperty(folder2NextAction, PROP_DISPOSITION_ACTION)); - - // remove last remaining step from the schedule - DispositionSchedule schedule = rmService.getDispositionSchedule(recordCategory); - assertNotNull(schedule); - List actionDefs = schedule.getDispositionActionDefinitions(); - assertEquals(1, actionDefs.size()); - System.out.println("Removing last remaining schedule step named: " + actionDefs.get(0).getName()); - rmService.removeDispositionActionDefinition(schedule, actionDefs.get(0)); - - return null; - } - }); - - // check there are no schedule steps left and that both folders no longer have the disposition lifecycle aspect, - // then add a new step - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - assertEquals(0, nodeService.getChildAssocs(recordFolder1, ASSOC_NEXT_DISPOSITION_ACTION, - RegexQNamePattern.MATCH_ALL).size()); - assertEquals(0, nodeService.getChildAssocs(recordFolder2, ASSOC_NEXT_DISPOSITION_ACTION, - RegexQNamePattern.MATCH_ALL).size()); - assertFalse(nodeService.hasAspect(recordFolder1, ASPECT_DISPOSITION_LIFECYCLE)); - assertFalse(nodeService.hasAspect(recordFolder2, ASPECT_DISPOSITION_LIFECYCLE)); - - // ensure schedule is empty - DispositionSchedule schedule = rmService.getDispositionSchedule(recordCategory); - assertNotNull(schedule); - List actionDefs = schedule.getDispositionActionDefinitions(); - assertEquals(0, actionDefs.size()); - - // add a new step - Map step1 = new HashMap(); - step1.put(RecordsManagementModel.PROP_DISPOSITION_ACTION_NAME, "retain"); - step1.put(RecordsManagementModel.PROP_DISPOSITION_DESCRIPTION, "Retain for 25 years"); - step1.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD, "year|25"); - rmService.addDispositionActionDefinition(schedule, step1); - - return null; - } - }); - - // check both folders now have the retain action - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - NodeRef folder1NextAction = nodeService.getChildAssocs(recordFolder1, ASSOC_NEXT_DISPOSITION_ACTION, - RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); - assertNotNull(folder1NextAction); - assertEquals("retain", (String)nodeService.getProperty(folder1NextAction, PROP_DISPOSITION_ACTION)); - assertNotNull(nodeService.getProperty(folder1NextAction, PROP_DISPOSITION_AS_OF)); - - NodeRef folder2NextAction = nodeService.getChildAssocs(recordFolder2, ASSOC_NEXT_DISPOSITION_ACTION, - RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); - assertNotNull(folder2NextAction); - assertEquals("retain", (String)nodeService.getProperty(folder2NextAction, PROP_DISPOSITION_ACTION)); - assertNotNull(nodeService.getProperty(folder2NextAction, PROP_DISPOSITION_AS_OF)); - - return null; - } - }); - */ - } - - /** - * Tests the re-scheduling of disposition lifecycles when steps from the schedule are deleted - * (when using record level disposition) - */ - public void testDispositionLifecycle_0318_reschedule_deletion_recordlevel() throws Exception - { - final NodeRef recordSeries = TestUtilities.getRecordSeries(rmService, nodeService, "Reports"); - setComplete(); - endTransaction(); - - // create a category - final NodeRef recordCategory = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - assertNotNull(recordSeries); - assertEquals("Reports", nodeService.getProperty(recordSeries, ContentModel.PROP_NAME)); - - return createRecordCategoryNode(recordSeries); - } - }); - - // define the disposition schedule for the category with several steps - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - assertNotNull(recordCategory); - - // get the disposition schedule and turn on record level disposition - DispositionSchedule schedule = dispositionService.getDispositionSchedule(recordCategory); - assertNotNull(schedule); - nodeService.setProperty(schedule.getNodeRef(), PROP_RECORD_LEVEL_DISPOSITION, true); - - // define properties for both steps - Map step1 = new HashMap(); - step1.put(RecordsManagementModel.PROP_DISPOSITION_ACTION_NAME, "cutoff"); - step1.put(RecordsManagementModel.PROP_DISPOSITION_DESCRIPTION, "Cutoff when no longer needed"); - step1.put(RecordsManagementModel.PROP_DISPOSITION_EVENT, "no_longer_needed"); - - Map step2 = new HashMap(); - step2.put(RecordsManagementModel.PROP_DISPOSITION_ACTION_NAME, "transfer"); - step2.put(RecordsManagementModel.PROP_DISPOSITION_DESCRIPTION, "Transfer after 1 month"); - step2.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD, "month|1"); - step2.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD_PROPERTY, PROP_DISPOSITION_AS_OF); - - Map step3 = new HashMap(); - step3.put(RecordsManagementModel.PROP_DISPOSITION_ACTION_NAME, "destroy"); - step3.put(RecordsManagementModel.PROP_DISPOSITION_DESCRIPTION, "Destroy after 1 year"); - step3.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD, "year|1"); - step3.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD_PROPERTY, PROP_DISPOSITION_AS_OF); - - // add the action definitions to the schedule - dispositionService.addDispositionActionDefinition(schedule, step1); - dispositionService.addDispositionActionDefinition(schedule, step2); - dispositionService.addDispositionActionDefinition(schedule, step3); - - return null; - } - }); - - // create first record folder - final NodeRef recordFolder = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - return createRecordFolder(recordCategory, "Record Folder"); - } - }); - - // create a record - final NodeRef record = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - assertNotNull(recordFolder); - - // Create the document - Map props = new HashMap(1); - props.put(ContentModel.PROP_NAME, "MyRecord.txt"); - NodeRef record = nodeService.createNode(recordFolder, - ContentModel.ASSOC_CONTAINS, - QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "MyRecord.txt"), - ContentModel.TYPE_CONTENT).getChildRef(); - - // Set the content - ContentWriter writer = contentService.getWriter(record, ContentModel.PROP_CONTENT, true); - writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); - writer.setEncoding("UTF-8"); - writer.putContent("There is some content in this record"); - - return record; - } - }); - - // make sure the disposition lifecycle is present and correct - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - assertNotNull(record); - - assertTrue(nodeService.hasAspect(record, ASPECT_DISPOSITION_LIFECYCLE)); - NodeRef recordNextAction = nodeService.getChildAssocs(record, ASSOC_NEXT_DISPOSITION_ACTION, - RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); - assertNotNull(recordNextAction); - - // make sure the record is on the cutoff step - assertEquals("cutoff", nodeService.getProperty(recordNextAction, PROP_DISPOSITION_ACTION)); - - // make sure the record has 1 event - assertEquals(1, nodeService.getChildAssocs(recordNextAction, ASSOC_EVENT_EXECUTIONS, - RegexQNamePattern.MATCH_ALL).size()); - - return null; - } - }); - - // check for steps in schedule then attempt to delete one - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - // check there are 3 steps to the schedule - DispositionSchedule schedule = dispositionService.getDispositionSchedule(recordCategory); - assertNotNull(schedule); - List actionDefs = schedule.getDispositionActionDefinitions(); - assertEquals(3, actionDefs.size()); - - // attempt to remove step 1 from the schedule - try - { - dispositionService.removeDispositionActionDefinition(schedule, actionDefs.get(0)); - fail("Expecting the step deletion to be unsuccessful as records are present"); - } - catch (AlfrescoRuntimeException are) - { - // expected as steps are present, deletion not allowed - } - - return null; - } - }); - - // remove the record (the folder can stay) - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - // remove record folders - nodeService.removeChild(recordFolder, record); - return null; - } - }); - - // try removing last schedule step - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - // make sure there are 3 steps - DispositionSchedule schedule = dispositionService.getDispositionSchedule(recordCategory); - assertNotNull(schedule); - List actionDefs = schedule.getDispositionActionDefinitions(); - assertEquals(3, actionDefs.size()); - - // remove last step, should be successful this time - dispositionService.removeDispositionActionDefinition(schedule, actionDefs.get(2)); - - // make sure there are now 2 steps - schedule = dispositionService.getDispositionSchedule(recordCategory); - actionDefs = schedule.getDispositionActionDefinitions(); - assertEquals(2, actionDefs.size()); - - return null; - } - }); - } - - /** - * test a dispostion schedule being setup after a record folder and record - */ - public void testDispositionLifecycle_0318_existingfolders() throws Exception - { - final NodeRef recordSeries = TestUtilities.getRecordSeries(rmService, nodeService, "Reports"); - setComplete(); - endTransaction(); - - // create a category - final NodeRef recordCategory = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - assertNotNull(recordSeries); - assertEquals("Reports", nodeService.getProperty(recordSeries, ContentModel.PROP_NAME)); - - return createRecordCategoryNode(recordSeries); - } - }); - - // create a record folder - final NodeRef recordFolder = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - assertNotNull(recordCategory); - return createRecordFolder(recordCategory, "Folder1"); - } - }); - - // define the disposition schedule for the category (Cut off monthly, hold 1 month, then destroy) - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - assertNotNull(recordFolder); - - // define properties for both steps - Map step1 = new HashMap(); - step1.put(RecordsManagementModel.PROP_DISPOSITION_ACTION_NAME, "cutoff"); - step1.put(RecordsManagementModel.PROP_DISPOSITION_DESCRIPTION, "Cutoff after 1 month"); - step1.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD, "month|1"); - - Map step2 = new HashMap(); - step2.put(RecordsManagementModel.PROP_DISPOSITION_ACTION_NAME, "destroy"); - step2.put(RecordsManagementModel.PROP_DISPOSITION_DESCRIPTION, "Destroy after 1 month"); - step2.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD, "month|1"); - step2.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD_PROPERTY, PROP_CUT_OFF_DATE); - - // add the action definitions to the schedule - DispositionSchedule schedule = dispositionService.getDispositionSchedule(recordCategory); - assertNotNull(schedule); - - NodeRef temp = dispositionService.addDispositionActionDefinition(schedule, step1).getNodeRef(); - List updatedProps = new ArrayList(step1.keySet()); - refreshDispositionActionDefinition(temp, updatedProps); - - temp = dispositionService.addDispositionActionDefinition(schedule, step2).getNodeRef(); - updatedProps = new ArrayList(step2.keySet()); - refreshDispositionActionDefinition(temp, updatedProps); - - return null; - } - }); - - // make sure the disposition lifecycle is present and correct - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - @SuppressWarnings("deprecation") - public Object execute() throws Throwable - { - assertNotNull(recordFolder); - - DispositionAction da = dispositionService.getNextDispositionAction(recordFolder); - assertNotNull(da); - - assertNotNull(da.getDispositionActionDefinition()); - assertNotNull(da.getDispositionActionDefinition().getId()); - assertEquals("cutoff", da.getName()); - Date asOfDate = da.getAsOfDate(); - assertNotNull(asOfDate); - - // make sure the as of date is a month in the future - Calendar cal = Calendar.getInstance(); - cal.add(Calendar.MONTH, 1); - int monthThen = cal.get(Calendar.MONTH); - assertEquals(asOfDate.getMonth(), monthThen); - - // make sure there aren't any events - assertEquals(0, da.getEventCompletionDetails().size()); - - // Check for the search properties having been populated - checkSearchAspect(recordFolder); - - return null; - } - }); - } - - /** - * Test the updating of a disposition schedule using folder level disposition - */ - public void testFolderLevelDispositionScheduleUpdate() throws Exception - { - final NodeRef recordSeries = TestUtilities.getRecordSeries(rmService, nodeService, "Reports"); - setComplete(); - endTransaction(); - - // create a category - final NodeRef recordCategory = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - assertNotNull(recordSeries); - assertEquals("Reports", nodeService.getProperty(recordSeries, ContentModel.PROP_NAME)); - - return createRecordCategoryNode(recordSeries); - } - }); - - // define the disposition schedule for the category (Cut off monthly, hold 1 month, then destroy) - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - assertNotNull(recordCategory); - - // get the disposition schedule and turn on record level disposition - DispositionSchedule schedule = dispositionService.getDispositionSchedule(recordCategory); - assertNotNull(schedule); - nodeService.setProperty(schedule.getNodeRef(), PROP_DISPOSITION_INSTRUCTIONS, "Cutoff after 1 month then destroy after 1 month"); - nodeService.setProperty(schedule.getNodeRef(), PROP_DISPOSITION_AUTHORITY, "Alfresco"); - - return null; - } - }); - - // create a record folder - final NodeRef recordFolder = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - return createRecordFolder(recordCategory, "Folder1"); - } - }); - - // check the created folder has the correctly populated search aspect, then update the schedule - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - // check the folder has the search aspect - assertNotNull(recordFolder); - checkSearchAspect(recordFolder, false); - - // update the disposition schedule - DispositionSchedule schedule = dispositionService.getDispositionSchedule(recordCategory); - assertNotNull(schedule); - nodeService.setProperty(schedule.getNodeRef(), PROP_DISPOSITION_INSTRUCTIONS, "Cutoff immediately when case is closed then destroy after 1 year"); - nodeService.setProperty(schedule.getNodeRef(), PROP_DISPOSITION_AUTHORITY, "DoD"); - - return null; - } - }); - - // check the search aspect has been kept in sync - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - // check the folder has the search aspect - checkSearchAspect(recordFolder, false); - - return null; - } - }); - } - - /** - * Test the updating of a disposition schedule using record level disposition - */ - public void testRecordLevelDispositionScheduleUpdate() throws Exception - { - final NodeRef recordSeries = TestUtilities.getRecordSeries(rmService, nodeService, "Reports"); - setComplete(); - endTransaction(); - - // create a category - final NodeRef recordCategory = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - assertNotNull(recordSeries); - assertEquals("Reports", nodeService.getProperty(recordSeries, ContentModel.PROP_NAME)); - - return createRecordCategoryNode(recordSeries); - } - }); - - // define the disposition schedule for the category (Cut off monthly, hold 1 month, then destroy) - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - assertNotNull(recordCategory); - - // get the disposition schedule and turn on record level disposition - DispositionSchedule schedule = dispositionService.getDispositionSchedule(recordCategory); - assertNotNull(schedule); - nodeService.setProperty(schedule.getNodeRef(), PROP_DISPOSITION_INSTRUCTIONS, "Cutoff after 1 month then destroy after 1 month"); - nodeService.setProperty(schedule.getNodeRef(), PROP_DISPOSITION_AUTHORITY, "Alfresco"); - nodeService.setProperty(schedule.getNodeRef(), PROP_RECORD_LEVEL_DISPOSITION, true); - - return null; - } - }); - - // create a record folder - final NodeRef recordFolder = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - return createRecordFolder(recordCategory, "Folder1"); - } - }); - - // create a record - final NodeRef record = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - assertNotNull(recordFolder); - - // Create the document - Map props = new HashMap(1); - props.put(ContentModel.PROP_NAME, "MyRecord.txt"); - NodeRef record = nodeService.createNode(recordFolder, - ContentModel.ASSOC_CONTAINS, - QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "MyRecord.txt"), - ContentModel.TYPE_CONTENT).getChildRef(); - - // Set the content - ContentWriter writer = contentService.getWriter(record, ContentModel.PROP_CONTENT, true); - writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); - writer.setEncoding("UTF-8"); - writer.putContent("There is some content in this record"); - - return record; - } - }); - - // check the created folder has the correctly populated search aspect, then update the schedule - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - // check the record has the search aspect - assertNotNull(record); - checkSearchAspect(record, false); - - // update the disposition schedule - DispositionSchedule schedule = dispositionService.getDispositionSchedule(recordCategory); - assertNotNull(schedule); - nodeService.setProperty(schedule.getNodeRef(), PROP_DISPOSITION_INSTRUCTIONS, "Cutoff immediately when case is closed then destroy after 1 year"); - nodeService.setProperty(schedule.getNodeRef(), PROP_DISPOSITION_AUTHORITY, "DoD"); - - return null; - } - }); - - // check the search aspect has been kept in sync - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - // check the record has the search aspect - checkSearchAspect(record, false); - - return null; - } - }); - } - - public void testUnCutoff() - { - final NodeRef recordCategory = TestUtilities.getRecordCategory(rmService, nodeService, "Reports", "AIS Audit Records"); - setComplete(); - endTransaction(); - - final NodeRef recordFolder = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - - assertNotNull(recordCategory); - assertEquals("AIS Audit Records", nodeService.getProperty(recordCategory, ContentModel.PROP_NAME)); - - return createRecordFolder(recordCategory, "March AIS Audit Records"); - } - }); - - final NodeRef recordOne = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - return createRecord(recordFolder); - } - }); - - final Calendar calendar = Calendar.getInstance(); - calendar.set(Calendar.HOUR, 0); - calendar.set(Calendar.MINUTE, 0); - calendar.set(Calendar.SECOND, 0); - - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - TestUtilities.declareRecord(recordOne, unprotectedNodeService, rmActionService); - - // Clock the asOf date back to ensure eligibility - NodeRef ndNodeRef = nodeService.getChildAssocs(recordFolder, ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); - Date nowDate = calendar.getTime(); - assertFalse(nowDate.equals(nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF))); - Map params = new HashMap(1); - params.put(EditDispositionActionAsOfDateAction.PARAM_AS_OF_DATE, nowDate); - rmActionService.executeRecordsManagementAction(recordFolder, "editDispositionActionAsOfDate", params); - assertTrue(nowDate.equals(nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF))); - - // Cut off - rmActionService.executeRecordsManagementAction(recordFolder, "cutoff", null); - - // Check that everything appears to be cutoff - assertTrue(nodeService.hasAspect(recordFolder, ASPECT_CUT_OFF)); - List records = rmService.getRecords(recordFolder); - for (NodeRef record : records) - { - assertTrue(nodeService.hasAspect(record, ASPECT_CUT_OFF)); - } - DispositionAction da = dispositionService.getNextDispositionAction(recordFolder); - assertNotNull(da); - assertFalse("cutoff".equals(da.getName())); - checkLastDispositionAction(recordFolder, "cutoff", 1); - - // Revert the cutoff - rmActionService.executeRecordsManagementAction(recordFolder, "unCutoff", null); - - // Check that everything has been reverted - assertFalse(nodeService.hasAspect(recordFolder, ASPECT_CUT_OFF)); - records = rmService.getRecords(recordFolder); - for (NodeRef record : records) - { - assertFalse(nodeService.hasAspect(record, ASPECT_CUT_OFF)); - } - da = dispositionService.getNextDispositionAction(recordFolder); - assertNotNull(da); - assertTrue("cutoff".equals(da.getName())); - assertNull(da.getStartedAt()); - assertNull(da.getStartedBy()); - assertNull(da.getCompletedAt()); - assertNull(da.getCompletedBy()); - List history = dispositionService.getCompletedDispositionActions(recordFolder); - assertNotNull(history); - assertEquals(0, history.size()); - - return null; - } - }); - - } - - private void checkLastDispositionAction(NodeRef nodeRef, String daName, int expectedCount) - { - // Check the previous action details - List history = dispositionService.getCompletedDispositionActions(nodeRef); - assertNotNull(history); - assertEquals(expectedCount, history.size()); - DispositionAction lastDA = history.get(history.size()-1); - assertEquals(daName, lastDA.getName()); - assertNotNull(lastDA.getStartedAt()); - assertNotNull(lastDA.getStartedBy()); - assertNotNull(lastDA.getCompletedAt()); - assertNotNull(lastDA.getCompletedBy()); - // Check the "get last" method - lastDA = dispositionService.getLastCompletedDispostionAction(nodeRef); - assertEquals(daName, lastDA.getName()); - } - - public void testFreeze() throws Exception - { - final NodeRef recordCategory = TestUtilities.getRecordCategory(rmService, nodeService, "Reports", "AIS Audit Records"); - assertNotNull(recordCategory); - assertEquals("AIS Audit Records", this.nodeService.getProperty(recordCategory, ContentModel.PROP_NAME)); - - // Before we start just remove any outstanding holds - final NodeRef rootNode = this.rmService.getFilePlan(recordCategory); - List tempAssocs = this.nodeService.getChildAssocs(rootNode, ASSOC_HOLDS, RegexQNamePattern.MATCH_ALL); - for (ChildAssociationRef tempAssoc : tempAssocs) - { - this.nodeService.deleteNode(tempAssoc.getChildRef()); - } - - final NodeRef recordFolder = createRecordFolder(recordCategory, "March AIS Audit Records"); - - setComplete(); - endTransaction(); - - final NodeRef recordOne = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - return createRecord(recordFolder, "one.txt"); - } - }); - final NodeRef recordTwo = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - return createRecord(recordFolder, "two.txt"); - } - }); - final NodeRef recordThree = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - return createRecord(recordFolder, "three.txt"); - } - }); - - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - assertTrue(nodeService.hasAspect(recordOne, ASPECT_RECORD)); - assertTrue(nodeService.hasAspect(recordOne, ASPECT_FILE_PLAN_COMPONENT)); - - // Freeze the record - Map params = new HashMap(1); - params.put(FreezeAction.PARAM_REASON, "reason1"); - rmActionService.executeRecordsManagementAction(recordOne, "freeze", params); - - return null; - } - }); - - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - // Check the hold exists - List holdAssocs = nodeService.getChildAssocs(rootNode, ASSOC_HOLDS, RegexQNamePattern.MATCH_ALL); - assertNotNull(holdAssocs); - assertEquals(1, holdAssocs.size()); - NodeRef holdNodeRef = holdAssocs.get(0).getChildRef(); - assertEquals("reason1", nodeService.getProperty(holdNodeRef, PROP_HOLD_REASON)); - List freezeAssocs = nodeService.getChildAssocs(holdNodeRef); - assertNotNull(freezeAssocs); - assertEquals(1, freezeAssocs.size()); - - // Check the nodes are frozen - assertTrue(nodeService.hasAspect(recordOne, ASPECT_FROZEN)); - assertNotNull(nodeService.getProperty(recordOne, PROP_FROZEN_AT)); - assertNotNull(nodeService.getProperty(recordOne, PROP_FROZEN_BY)); - assertFalse(nodeService.hasAspect(recordTwo, ASPECT_FROZEN)); - assertFalse(nodeService.hasAspect(recordThree, ASPECT_FROZEN)); - - // check the records have the hold reason reflected on the search aspect - assertEquals("reason1", nodeService.getProperty(recordOne, RecordsManagementSearchBehaviour.PROP_RS_HOLD_REASON)); - assertNull(nodeService.getProperty(recordTwo, RecordsManagementSearchBehaviour.PROP_RS_HOLD_REASON)); - assertNull(nodeService.getProperty(recordThree, RecordsManagementSearchBehaviour.PROP_RS_HOLD_REASON)); - - // Update the freeze reason - Map params = new HashMap(1); - params.put(FreezeAction.PARAM_REASON, "reason1changed"); - rmActionService.executeRecordsManagementAction(holdNodeRef, "editHoldReason", params); - - // Check the hold has been updated - String updatedHoldReason = (String)nodeService.getProperty(holdNodeRef, PROP_HOLD_REASON); - assertEquals("reason1changed", updatedHoldReason); - - return null; - } - }); - - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - // check the search fields on the records have also been updated - assertEquals("reason1changed", nodeService.getProperty(recordOne, RecordsManagementSearchBehaviour.PROP_RS_HOLD_REASON)); - assertNull(nodeService.getProperty(recordTwo, RecordsManagementSearchBehaviour.PROP_RS_HOLD_REASON)); - assertNull(nodeService.getProperty(recordThree, RecordsManagementSearchBehaviour.PROP_RS_HOLD_REASON)); - - // Freeze a number of records - Map params = new HashMap(1); - params.put(FreezeAction.PARAM_REASON, "reason2"); - List records = new ArrayList(2); - records.add(recordOne); - records.add(recordTwo); - records.add(recordThree); - rmActionService.executeRecordsManagementAction(records, "freeze", params); - - return null; - } - }); - - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - // Check the holds exist - List holdAssocs = nodeService.getChildAssocs(rootNode, ASSOC_HOLDS, RegexQNamePattern.MATCH_ALL); - assertNotNull(holdAssocs); - assertEquals(2, holdAssocs.size()); - for (ChildAssociationRef holdAssoc : holdAssocs) - { - String reason = (String)nodeService.getProperty(holdAssoc.getChildRef(), PROP_HOLD_REASON); - if (reason.equals("reason2") == true) - { - List freezeAssocs = nodeService.getChildAssocs(holdAssoc.getChildRef()); - assertNotNull(freezeAssocs); - assertEquals(3, freezeAssocs.size()); - } - else if (reason.equals("reason1changed") == true) - { - List freezeAssocs = nodeService.getChildAssocs(holdAssoc.getChildRef()); - assertNotNull(freezeAssocs); - assertEquals(1, freezeAssocs.size()); - } - } - - // Check the nodes are frozen - final List testRecords = Arrays.asList(new NodeRef[]{recordOne, recordTwo, recordThree}); - for (NodeRef nr : testRecords) - { - assertTrue(nodeService.hasAspect(nr, ASPECT_FROZEN)); - assertNotNull(nodeService.getProperty(nr, PROP_FROZEN_AT)); - assertNotNull(nodeService.getProperty(nr, PROP_FROZEN_BY)); - assertNotNull(nodeService.getProperty(nr, RecordsManagementSearchBehaviour.PROP_RS_HOLD_REASON)); - } - - // Unfreeze a node - rmActionService.executeRecordsManagementAction(recordThree, "unfreeze"); - - return null; - } - }); - - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - // Check the holds - List holdAssocs = nodeService.getChildAssocs(rootNode, ASSOC_HOLDS, RegexQNamePattern.MATCH_ALL); - assertNotNull(holdAssocs); - assertEquals(2, holdAssocs.size()); - for (ChildAssociationRef holdAssoc : holdAssocs) - { - String reason = (String)nodeService.getProperty(holdAssoc.getChildRef(), PROP_HOLD_REASON); - if (reason.equals("reason2") == true) - { - List freezeAssocs = nodeService.getChildAssocs(holdAssoc.getChildRef()); - assertNotNull(freezeAssocs); - assertEquals(2, freezeAssocs.size()); - } - else if (reason.equals("reason1changed") == true) - { - List freezeAssocs = nodeService.getChildAssocs(holdAssoc.getChildRef()); - assertNotNull(freezeAssocs); - assertEquals(1, freezeAssocs.size()); - } - } - - // Check the nodes are frozen - assertTrue(nodeService.hasAspect(recordOne, ASPECT_FROZEN)); - assertNotNull(nodeService.getProperty(recordOne, PROP_FROZEN_AT)); - assertNotNull(nodeService.getProperty(recordOne, PROP_FROZEN_BY)); - assertEquals("reason2", nodeService.getProperty(recordOne, RecordsManagementSearchBehaviour.PROP_RS_HOLD_REASON)); - assertTrue(nodeService.hasAspect(recordTwo, ASPECT_FROZEN)); - assertNotNull(nodeService.getProperty(recordTwo, PROP_FROZEN_AT)); - assertNotNull(nodeService.getProperty(recordTwo, PROP_FROZEN_BY)); - assertEquals("reason2", nodeService.getProperty(recordTwo, RecordsManagementSearchBehaviour.PROP_RS_HOLD_REASON)); - assertFalse(nodeService.hasAspect(recordThree, ASPECT_FROZEN)); - assertNull(nodeService.getProperty(recordThree, RecordsManagementSearchBehaviour.PROP_RS_HOLD_REASON)); - - return null; - } - }); - - // Put the relinquish hold request into its own transaction - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - // Check the holds - List holdAssocs = nodeService.getChildAssocs(rootNode, ASSOC_HOLDS, RegexQNamePattern.MATCH_ALL); - assertNotNull(holdAssocs); - assertEquals(2, holdAssocs.size()); - // Relinquish the first hold - NodeRef holdNodeRef = holdAssocs.get(0).getChildRef(); - assertEquals("reason1changed", nodeService.getProperty(holdNodeRef, PROP_HOLD_REASON)); - - rmActionService.executeRecordsManagementAction(holdNodeRef, "relinquishHold"); - - // Check the holds - holdAssocs = nodeService.getChildAssocs(rootNode, ASSOC_HOLDS, RegexQNamePattern.MATCH_ALL); - assertNotNull(holdAssocs); - assertEquals(1, holdAssocs.size()); - holdNodeRef = holdAssocs.get(0).getChildRef(); - assertEquals("reason2", nodeService.getProperty(holdNodeRef, PROP_HOLD_REASON)); - List freezeAssocs = nodeService.getChildAssocs(holdNodeRef); - assertNotNull(freezeAssocs); - assertEquals(2, freezeAssocs.size()); - - return null; - } - }); - - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - // Check the nodes are frozen - assertTrue(nodeService.hasAspect(recordOne, ASPECT_FROZEN)); - assertNotNull(nodeService.getProperty(recordOne, PROP_FROZEN_AT)); - assertNotNull(nodeService.getProperty(recordOne, PROP_FROZEN_BY)); - // TODO: record one is still linked to a hold so should have the original hold reason - // on the search aspect but we're presuming just one hold for now so the search hold - // reason will remain unchanged - assertEquals("reason2", nodeService.getProperty(recordOne, RecordsManagementSearchBehaviour.PROP_RS_HOLD_REASON)); - assertTrue(nodeService.hasAspect(recordTwo, ASPECT_FROZEN)); - assertNotNull(nodeService.getProperty(recordTwo, PROP_FROZEN_AT)); - assertNotNull(nodeService.getProperty(recordTwo, PROP_FROZEN_BY)); - assertEquals("reason2", nodeService.getProperty(recordTwo, RecordsManagementSearchBehaviour.PROP_RS_HOLD_REASON)); - assertFalse(nodeService.hasAspect(recordThree, ASPECT_FROZEN)); - assertNull(nodeService.getProperty(recordThree, RecordsManagementSearchBehaviour.PROP_RS_HOLD_REASON)); - - // Unfreeze - rmActionService.executeRecordsManagementAction(recordOne, "unfreeze"); - - return null; - } - }); - - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - // Check the holds - List holdAssocs = nodeService.getChildAssocs(rootNode, ASSOC_HOLDS, RegexQNamePattern.MATCH_ALL); - assertNotNull(holdAssocs); - assertEquals(1, holdAssocs.size()); - NodeRef holdNodeRef = holdAssocs.get(0).getChildRef(); - assertEquals("reason2", nodeService.getProperty(holdNodeRef, PROP_HOLD_REASON)); - List freezeAssocs = nodeService.getChildAssocs(holdNodeRef); - assertNotNull(freezeAssocs); - assertEquals(1, freezeAssocs.size()); - - // Check the nodes are frozen - assertFalse(nodeService.hasAspect(recordOne, ASPECT_FROZEN)); - assertNull(nodeService.getProperty(recordOne, RecordsManagementSearchBehaviour.PROP_RS_HOLD_REASON)); - assertTrue(nodeService.hasAspect(recordTwo, ASPECT_FROZEN)); - assertNotNull(nodeService.getProperty(recordTwo, PROP_FROZEN_AT)); - assertNotNull(nodeService.getProperty(recordTwo, PROP_FROZEN_BY)); - assertEquals("reason2", nodeService.getProperty(recordTwo, RecordsManagementSearchBehaviour.PROP_RS_HOLD_REASON)); - assertFalse(nodeService.hasAspect(recordThree, ASPECT_FROZEN)); - assertNull(nodeService.getProperty(recordThree, RecordsManagementSearchBehaviour.PROP_RS_HOLD_REASON)); - - // Unfreeze - rmActionService.executeRecordsManagementAction(recordTwo, "unfreeze"); - - return null; - } - }); - - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - // Check the holds - List holdAssocs = nodeService.getChildAssocs(rootNode, ASSOC_HOLDS, RegexQNamePattern.MATCH_ALL); - assertNotNull(holdAssocs); - assertEquals(0, holdAssocs.size()); - - // Check the nodes are unfrozen - assertFalse(nodeService.hasAspect(recordOne, ASPECT_FROZEN)); - assertFalse(nodeService.hasAspect(recordTwo, ASPECT_FROZEN)); - assertFalse(nodeService.hasAspect(recordThree, ASPECT_FROZEN)); - - // check the search hold reason is null on all records - assertNull(nodeService.getProperty(recordOne, RecordsManagementSearchBehaviour.PROP_RS_HOLD_REASON)); - assertNull(nodeService.getProperty(recordTwo, RecordsManagementSearchBehaviour.PROP_RS_HOLD_REASON)); - assertNull(nodeService.getProperty(recordThree, RecordsManagementSearchBehaviour.PROP_RS_HOLD_REASON)); - - return null; - } - }); - } - - public void testAutoSuperseded() - { - final NodeRef recordCategory = TestUtilities.getRecordCategory(rmService, nodeService, "Civilian Files", "Employee Performance File System Records"); - assertNotNull(recordCategory); - assertEquals("Employee Performance File System Records", this.nodeService.getProperty(recordCategory, ContentModel.PROP_NAME)); - - final NodeRef recordFolder = createRecordFolder(recordCategory, "Test Record Folder"); - - // Before we start just remove any outstanding transfers - final NodeRef rootNode = this.rmService.getFilePlan(recordCategory); - List tempAssocs = this.nodeService.getChildAssocs(rootNode, ASSOC_TRANSFERS, RegexQNamePattern.MATCH_ALL); - for (ChildAssociationRef tempAssoc : tempAssocs) - { - this.nodeService.deleteNode(tempAssoc.getChildRef()); - } - - setComplete(); - endTransaction(); - - final NodeRef recordOne = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - return createRecord(recordFolder, "one.txt"); - } - }); - final NodeRef recordTwo = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - return createRecord(recordFolder, "two.txt"); - } - }); - - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - assertTrue(nodeService.hasAspect(recordOne, ASPECT_RECORD)); - - TestUtilities.declareRecord(recordOne, unprotectedNodeService, rmActionService); - TestUtilities.declareRecord(recordTwo, unprotectedNodeService, rmActionService); - - return null; - } - }); - - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - - DispositionAction da = dispositionService.getNextDispositionAction(recordTwo); - assertNotNull(da); - assertEquals("cutoff", da.getName()); - assertFalse(da.isEventsEligible()); - List events = da.getEventCompletionDetails(); - assertNotNull(events); - assertEquals(1, events.size()); - EventCompletionDetails event = events.get(0); - assertEquals("superseded", event.getEventName()); - assertFalse(event.isEventComplete()); - assertNull(event.getEventCompletedAt()); - assertNull(event.getEventCompletedBy()); - - rmAdminService.addCustomReference(recordOne, recordTwo, QName.createQName(RecordsManagementCustomModel.RM_CUSTOM_URI, "supersedes")); - - return null; - } - }); - - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - DispositionAction da = dispositionService.getNextDispositionAction(recordTwo); - assertNotNull(da); - assertEquals("cutoff", da.getName()); - assertTrue(da.isEventsEligible()); - List events = da.getEventCompletionDetails(); - assertNotNull(events); - assertEquals(1, events.size()); - EventCompletionDetails event = events.get(0); - assertEquals("superseded", event.getEventName()); - assertTrue(event.isEventComplete()); - assertNotNull(event.getEventCompletedAt()); - assertNotNull(event.getEventCompletedBy()); - - return null; - } - }); - } - - public void testVersioned() - { - final NodeRef recordCategory = TestUtilities.getRecordCategory(rmService, nodeService, "Civilian Files", "Employee Performance File System Records"); - assertNotNull(recordCategory); - assertEquals("Employee Performance File System Records", this.nodeService.getProperty(recordCategory, ContentModel.PROP_NAME)); - - final NodeRef recordFolder = createRecordFolder(recordCategory, "Test Record Folder"); - - // Before we start just remove any outstanding transfers - final NodeRef rootNode = this.rmService.getFilePlan(recordCategory); - List tempAssocs = this.nodeService.getChildAssocs(rootNode, ASSOC_TRANSFERS, RegexQNamePattern.MATCH_ALL); - for (ChildAssociationRef tempAssoc : tempAssocs) - { - this.nodeService.deleteNode(tempAssoc.getChildRef()); - } - - setComplete(); - endTransaction(); - - final NodeRef recordOne = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - return createRecord(recordFolder, "one.txt"); - } - }); - final NodeRef recordTwo = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - return createRecord(recordFolder, "two.txt"); - } - }); - - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - assertTrue(nodeService.hasAspect(recordOne, ASPECT_RECORD)); - - TestUtilities.declareRecord(recordOne, unprotectedNodeService, rmActionService); - TestUtilities.declareRecord(recordTwo, unprotectedNodeService, rmActionService); - - assertFalse(nodeService.hasAspect(recordOne, ASPECT_VERSIONED_RECORD)); - - rmAdminService.addCustomReference(recordOne, recordTwo, QName.createQName(RecordsManagementCustomModel.RM_CUSTOM_URI, "versions")); - - return null; - } - }); - - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - assertTrue(nodeService.hasAspect(recordOne, ASPECT_VERSIONED_RECORD)); - - rmAdminService.removeCustomReference(recordOne, recordTwo, QName.createQName(RecordsManagementCustomModel.RM_CUSTOM_URI, "versions")); - - return null; - } - }); - - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - assertFalse(nodeService.hasAspect(recordOne, ASPECT_VERSIONED_RECORD)); - - return null; - } - }); - } - - public void testDispositionLifecycle_0430_02_transfer() throws Exception - { - final NodeRef recordCategory = TestUtilities.getRecordCategory(rmService, nodeService, "Civilian Files", "Foreign Employee Award Files"); - assertNotNull(recordCategory); - assertEquals("Foreign Employee Award Files", this.nodeService.getProperty(recordCategory, ContentModel.PROP_NAME)); - - final NodeRef recordFolder = createRecordFolder(recordCategory, "Test Record Folder"); - - // Before we start just remove any outstanding transfers - final NodeRef rootNode = this.rmService.getFilePlan(recordCategory); - List tempAssocs = this.nodeService.getChildAssocs(rootNode, ASSOC_TRANSFERS, RegexQNamePattern.MATCH_ALL); - for (ChildAssociationRef tempAssoc : tempAssocs) - { - this.nodeService.deleteNode(tempAssoc.getChildRef()); - } - - setComplete(); - endTransaction(); - - final NodeRef recordOne = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - return createRecord(recordFolder, "one.txt"); - } - }); - final NodeRef recordTwo = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - return createRecord(recordFolder, "two.txt"); - } - }); - final NodeRef recordThree = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - // Create the document - Map props = new HashMap(1); - props.put(ContentModel.PROP_NAME, "three.pdf"); - NodeRef recordThree = nodeService.createNode(recordFolder, - ContentModel.ASSOC_CONTAINS, - QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "three.pdf"), - ContentModel.TYPE_CONTENT, - props).getChildRef(); - - // Set the content - ContentWriter writer = contentService.getWriter(recordOne, ContentModel.PROP_CONTENT, true); - writer.setMimetype(MimetypeMap.MIMETYPE_PDF); - writer.setEncoding("UTF-8"); - writer.putContent("asdas"); - - return recordThree; - } - }); - - final DispositionAction da = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public DispositionAction execute() throws Throwable - { - // Declare the records - TestUtilities.declareRecord(recordOne, unprotectedNodeService, rmActionService); - TestUtilities.declareRecord(recordTwo, unprotectedNodeService, rmActionService); - TestUtilities.declareRecord(recordThree, unprotectedNodeService, rmActionService); - - // Cutoff - Map params = new HashMap(3); - params.put(CompleteEventAction.PARAM_EVENT_NAME, "case_complete"); - params.put(CompleteEventAction.PARAM_EVENT_COMPLETED_AT, new Date()); - params.put(CompleteEventAction.PARAM_EVENT_COMPLETED_BY, "roy"); - rmActionService.executeRecordsManagementAction(recordFolder, "completeEvent", params); - rmActionService.executeRecordsManagementAction(recordFolder, "cutoff"); - - checkLastDispositionAction(recordFolder, "cutoff", 1); - - DispositionAction da = dispositionService.getNextDispositionAction(recordFolder); - assertNotNull(da); - assertEquals("transfer", da.getName()); - - assertFalse(nodeService.hasAspect(recordFolder, ASPECT_TRANSFERRED)); - - return da; - } - }); - - // Do the transfer - final Calendar calendar = Calendar.getInstance(); - calendar.set(Calendar.HOUR, 0); - calendar.set(Calendar.MINUTE, 0); - calendar.set(Calendar.SECOND, 0); - - final Object actionResult = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - // Clock the asOf date back to ensure eligibility - Date nowDate = calendar.getTime(); - assertFalse(nowDate.equals(nodeService.getProperty(da.getNodeRef(), PROP_DISPOSITION_AS_OF))); - Map params = new HashMap(1); - params.put(EditDispositionActionAsOfDateAction.PARAM_AS_OF_DATE, nowDate); - rmActionService.executeRecordsManagementAction(recordFolder, "editDispositionActionAsOfDate", params); - assertTrue(nowDate.equals(nodeService.getProperty(da.getNodeRef(), PROP_DISPOSITION_AS_OF))); - - return rmActionService.executeRecordsManagementAction(recordFolder, "transfer", null); - } - }); - - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - assertFalse(nodeService.hasAspect(recordFolder, ASPECT_TRANSFERRED)); - assertFalse(nodeService.hasAspect(recordOne, ASPECT_TRANSFERRED)); - assertFalse(nodeService.hasAspect(recordTwo, ASPECT_TRANSFERRED)); - assertFalse(nodeService.hasAspect(recordThree, ASPECT_TRANSFERRED)); - - // Check that the next disposition action is still in the correct state - DispositionAction da = dispositionService.getNextDispositionAction(recordFolder); - assertNotNull(da); - assertEquals("transfer", da.getName()); - assertNotNull(da.getStartedAt()); - assertNotNull(da.getStartedBy()); - assertNull(da.getCompletedAt()); - assertNull(da.getCompletedBy()); - - checkLastDispositionAction(recordFolder, "cutoff", 1); - - // Check that the transfer object is created - assertNotNull(rootNode); - List assocs = nodeService.getChildAssocs(rootNode, ASSOC_TRANSFERS, RegexQNamePattern.MATCH_ALL); - assertNotNull(assocs); - assertEquals(1, assocs.size()); - NodeRef transferNodeRef = assocs.get(0).getChildRef(); - assertEquals(TYPE_TRANSFER, nodeService.getType(transferNodeRef)); - assertTrue(((Boolean)nodeService.getProperty(transferNodeRef, PROP_TRANSFER_PDF_INDICATOR)).booleanValue()); - assertEquals("Offline Storage", (String)nodeService.getProperty(transferNodeRef, PROP_TRANSFER_LOCATION)); - assertNotNull(actionResult); - assertEquals(transferNodeRef, ((RecordsManagementActionResult)actionResult).getValue()); - List children = nodeService.getChildAssocs(transferNodeRef, ASSOC_TRANSFERRED, RegexQNamePattern.MATCH_ALL); - assertNotNull(children); - assertEquals(1, children.size()); - - - // Complete the transfer - rmActionService.executeRecordsManagementAction(assocs.get(0).getChildRef(), "transferComplete"); - - // Check nodes have been marked correctly - assertTrue(nodeService.hasAspect(recordFolder, ASPECT_TRANSFERRED)); - assertTrue(nodeService.hasAspect(recordOne, ASPECT_TRANSFERRED)); - assertTrue(nodeService.hasAspect(recordTwo, ASPECT_TRANSFERRED)); - assertTrue(nodeService.hasAspect(recordThree, ASPECT_TRANSFERRED)); - - // Check the transfer object is deleted - assocs = nodeService.getChildAssocs(rootNode, ASSOC_TRANSFERS, RegexQNamePattern.MATCH_ALL); - assertNotNull(assocs); - assertEquals(0, assocs.size()); - - // Check the disposition action has been moved on - da = dispositionService.getNextDispositionAction(recordFolder); - assertNotNull(da); - assertEquals("transfer", da.getName()); - assertNull(da.getStartedAt()); - assertNull(da.getStartedBy()); - assertNull(da.getCompletedAt()); - assertNull(da.getCompletedBy()); - assertFalse(dispositionService.isNextDispositionActionEligible(recordFolder)); - - checkLastDispositionAction(recordFolder, "transfer", 2); - - return null; - } - }); - } - - private void checkSearchAspect(NodeRef record) - { - checkSearchAspect(record, true); - } - - private void checkSearchAspect(NodeRef record, boolean isPeriodSet) - { - DispositionAction da = dispositionService.getNextDispositionAction(record); - if (da != null) - { - assertTrue(nodeService.hasAspect(record, RecordsManagementSearchBehaviour.ASPECT_RM_SEARCH)); - assertEquals(da.getName(), - nodeService.getProperty(record, RecordsManagementSearchBehaviour.PROP_RS_DISPOSITION_ACTION_NAME)); - assertEquals(da.getAsOfDate(), - nodeService.getProperty(record, RecordsManagementSearchBehaviour.PROP_RS_DISPOSITION_ACTION_AS_OF)); - assertEquals(nodeService.getProperty(da.getNodeRef(), PROP_DISPOSITION_EVENTS_ELIGIBLE), - nodeService.getProperty(record, RecordsManagementSearchBehaviour.PROP_RS_DISPOSITION_EVENTS_ELIGIBLE)); - - int eventCount = da.getEventCompletionDetails().size(); - Collection events = (Collection)nodeService.getProperty(record, RecordsManagementSearchBehaviour.PROP_RS_DISPOSITION_EVENTS); - if (eventCount == 0) - { - assertNull(events); - } - else - { - assertEquals(eventCount, events.size()); - } - - DispositionActionDefinition daDef = da.getDispositionActionDefinition(); - assertNotNull(daDef); - Period period = daDef.getPeriod(); - if (isPeriodSet) - { - assertNotNull(period); - assertEquals(period.getPeriodType(), nodeService.getProperty(record, RecordsManagementSearchBehaviour.PROP_RS_DISPOSITION_PERIOD)); - assertEquals(period.getExpression(), nodeService.getProperty(record, RecordsManagementSearchBehaviour.PROP_RS_DISPOSITION_PERIOD_EXPRESSION)); - } - else - { - assertNull(period); - assertNull(nodeService.getProperty(record, RecordsManagementSearchBehaviour.PROP_RS_DISPOSITION_PERIOD)); - assertNull(nodeService.getProperty(record, RecordsManagementSearchBehaviour.PROP_RS_DISPOSITION_PERIOD_EXPRESSION)); - } - } - - DispositionSchedule ds = dispositionService.getDispositionSchedule(record); - Boolean value = (Boolean)nodeService.getProperty(record, RecordsManagementSearchBehaviour.PROP_RS_HAS_DISPOITION_SCHEDULE); - String dsInstructions = (String)nodeService.getProperty(record, RecordsManagementSearchBehaviour.PROP_RS_DISPOITION_INSTRUCTIONS); - String dsAuthority = (String)nodeService.getProperty(record, RecordsManagementSearchBehaviour.PROP_RS_DISPOITION_AUTHORITY); - if (ds != null) - { - assertTrue(value); - assertEquals(ds.getDispositionInstructions(), dsInstructions); - assertEquals(ds.getDispositionAuthority(), dsAuthority); - } - else - { - assertFalse(value); - } - - VitalRecordDefinition vrd = vitalRecordService.getVitalRecordDefinition(record); - if (vrd == null) - { - assertNull(nodeService.getProperty(record, RecordsManagementSearchBehaviour.PROP_RS_VITAL_RECORD_REVIEW_PERIOD)); - assertNull(nodeService.getProperty(record, RecordsManagementSearchBehaviour.PROP_RS_VITAL_RECORD_REVIEW_PERIOD_EXPRESSION)); - } - else - { - assertEquals(vrd.getReviewPeriod().getPeriodType(), - nodeService.getProperty(record, RecordsManagementSearchBehaviour.PROP_RS_VITAL_RECORD_REVIEW_PERIOD)); - assertEquals(vrd.getReviewPeriod().getExpression(), - nodeService.getProperty(record, RecordsManagementSearchBehaviour.PROP_RS_VITAL_RECORD_REVIEW_PERIOD_EXPRESSION)); - } - } - - - public void testDispositionLifecycle_0430_01_recordleveldisposition() throws Exception - { - NodeRef recordCategory = TestUtilities.getRecordCategory(rmService, nodeService, "Civilian Files", "Employee Performance File System Records"); - assertNotNull(recordCategory); - assertEquals("Employee Performance File System Records", this.nodeService.getProperty(recordCategory, ContentModel.PROP_NAME)); - - NodeRef recordFolder = createRecordFolder(recordCategory, "My Record Folder"); - - setComplete(); - endTransaction(); - - UserTransaction txn = transactionService.getUserTransaction(false); - txn.begin(); - - NodeRef recordOne = createRecord(recordFolder, "one.txt"); - - txn.commit(); - txn = transactionService.getUserTransaction(false); - txn.begin(); - - TestUtilities.declareRecord(recordOne, unprotectedNodeService, rmActionService); - - // Check the disposition action - assertTrue(this.nodeService.hasAspect(recordOne, ASPECT_DISPOSITION_LIFECYCLE)); - assertFalse(this.nodeService.hasAspect(recordFolder, ASPECT_DISPOSITION_LIFECYCLE)); - - // Check the dispostion action - DispositionAction da = dispositionService.getNextDispositionAction(recordOne); - assertNotNull(da); - assertEquals("cutoff", da.getDispositionActionDefinition().getName()); - assertNull(da.getAsOfDate()); - assertFalse((Boolean)this.nodeService.getProperty(da.getNodeRef(), PROP_DISPOSITION_EVENTS_ELIGIBLE)); - assertEquals(true, da.getDispositionActionDefinition().eligibleOnFirstCompleteEvent()); - List events = da.getEventCompletionDetails(); - assertNotNull(events); - assertEquals(1, events.size()); - EventCompletionDetails event = events.get(0); - - Map params = new HashMap(3); - params.put(CompleteEventAction.PARAM_EVENT_NAME, event.getEventName()); - params.put(CompleteEventAction.PARAM_EVENT_COMPLETED_AT, new Date()); - params.put(CompleteEventAction.PARAM_EVENT_COMPLETED_BY, "roy"); - - this.rmActionService.executeRecordsManagementAction(recordOne, "completeEvent", params); - - txn.commit(); - txn = transactionService.getUserTransaction(false); - txn.begin(); - - assertTrue((Boolean)this.nodeService.getProperty(da.getNodeRef(), PROP_DISPOSITION_EVENTS_ELIGIBLE)); - - // Do the commit action - this.rmActionService.executeRecordsManagementAction(recordOne, "cutoff", null); - - txn.commit(); - txn = transactionService.getUserTransaction(false); - txn.begin(); - - // Check events are gone - da = dispositionService.getNextDispositionAction(recordOne); - - assertNotNull(da); - assertEquals("destroy", da.getDispositionActionDefinition().getName()); - assertNotNull(da.getAsOfDate()); - assertFalse((Boolean)this.nodeService.getProperty(da.getNodeRef(), PROP_DISPOSITION_EVENTS_ELIGIBLE)); - events = da.getEventCompletionDetails(); - assertNotNull(events); - assertEquals(0, events.size()); - - final Calendar calendar = Calendar.getInstance(); - calendar.set(Calendar.HOUR, 0); - calendar.set(Calendar.MINUTE, 0); - calendar.set(Calendar.SECOND, 0); - - // Clock the asOf date back to ensure eligibility for destruction - NodeRef ndNodeRef = nodeService.getChildAssocs(recordOne, ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); - Date nowDate = calendar.getTime(); - assertFalse(nowDate.equals(nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF))); - params.clear(); - params.put(EditDispositionActionAsOfDateAction.PARAM_AS_OF_DATE, nowDate); - rmActionService.executeRecordsManagementAction(recordOne, "editDispositionActionAsOfDate", params); - assertTrue(nowDate.equals(nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF))); - - - assertNotNull(nodeService.getProperty(recordOne, ContentModel.PROP_CONTENT)); - - rmActionService.executeRecordsManagementAction(recordOne, "destroy", null); - - // Check that the node has been ghosted - assertTrue(nodeService.exists(recordOne)); - assertTrue(nodeService.hasAspect(recordOne, RecordsManagementModel.ASPECT_GHOSTED)); - assertNull(nodeService.getProperty(recordOne, ContentModel.PROP_CONTENT)); - - txn.commit(); - } - - public void testDispositionLifecycle_0412_03_eventtest() throws Exception - { - NodeRef recordCategory = TestUtilities.getRecordCategory(rmService, nodeService, "Military Files", "Personnel Security Program Records"); - assertNotNull(recordCategory); - assertEquals("Personnel Security Program Records", this.nodeService.getProperty(recordCategory, ContentModel.PROP_NAME)); - - Map folderProps = new HashMap(1); - folderProps.put(ContentModel.PROP_NAME, "My Folder"); - NodeRef recordFolder = this.nodeService.createNode(recordCategory, - ContentModel.ASSOC_CONTAINS, - QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "My Folder"), - TYPE_RECORD_FOLDER).getChildRef(); - setComplete(); - endTransaction(); - - UserTransaction txn = transactionService.getUserTransaction(false); - txn.begin(); - - NodeRef recordOne = createRecord(recordFolder); - - txn.commit(); - txn = transactionService.getUserTransaction(false); - txn.begin(); - - TestUtilities.declareRecord(recordOne, unprotectedNodeService, rmActionService); - - // NOTE the disposition is being managed at a folder level ... - - // Check the disposition action - assertFalse(this.nodeService.hasAspect(recordOne, ASPECT_DISPOSITION_LIFECYCLE)); - assertTrue(this.nodeService.hasAspect(recordFolder, ASPECT_DISPOSITION_LIFECYCLE)); - - // Check the dispostion action - DispositionAction da = dispositionService.getNextDispositionAction(recordFolder); - assertNotNull(da); - assertEquals("cutoff", da.getDispositionActionDefinition().getName()); - assertNull(da.getAsOfDate()); - assertFalse((Boolean)this.nodeService.getProperty(da.getNodeRef(), PROP_DISPOSITION_EVENTS_ELIGIBLE)); - assertEquals(false, da.getDispositionActionDefinition().eligibleOnFirstCompleteEvent()); - List events = da.getEventCompletionDetails(); - assertNotNull(events); - assertEquals(3, events.size()); - - checkSearchAspect(recordFolder); - - txn.commit(); - txn = transactionService.getUserTransaction(false); - txn.begin(); - - EventCompletionDetails ecd = events.get(0); - assertFalse(ecd.isEventComplete()); - assertNull(ecd.getEventCompletedBy()); - assertNull(ecd.getEventCompletedAt()); - - Map params = new HashMap(3); - params.put(CompleteEventAction.PARAM_EVENT_NAME, events.get(0).getEventName()); - params.put(CompleteEventAction.PARAM_EVENT_COMPLETED_AT, new Date()); - params.put(CompleteEventAction.PARAM_EVENT_COMPLETED_BY, "roy"); - - checkSearchAspect(recordFolder); - - this.rmActionService.executeRecordsManagementAction(recordFolder, "completeEvent", params); - - txn.commit(); - txn = transactionService.getUserTransaction(false); - txn.begin(); - - assertFalse((Boolean)this.nodeService.getProperty(da.getNodeRef(), PROP_DISPOSITION_EVENTS_ELIGIBLE)); - assertEquals(false, da.getDispositionActionDefinition().eligibleOnFirstCompleteEvent()); - events = da.getEventCompletionDetails(); - assertNotNull(events); - assertEquals(3, events.size()); - - params = new HashMap(3); - params.put(CompleteEventAction.PARAM_EVENT_NAME, events.get(1).getEventName()); - params.put(CompleteEventAction.PARAM_EVENT_COMPLETED_AT, new Date()); - params.put(CompleteEventAction.PARAM_EVENT_COMPLETED_BY, "roy"); - - checkSearchAspect(recordFolder); - - this.rmActionService.executeRecordsManagementAction(recordFolder, "completeEvent", params); - - txn.commit(); - txn = transactionService.getUserTransaction(false); - txn.begin(); - - assertFalse((Boolean)this.nodeService.getProperty(da.getNodeRef(), PROP_DISPOSITION_EVENTS_ELIGIBLE)); - - params = new HashMap(3); - params.put(CompleteEventAction.PARAM_EVENT_NAME, events.get(2).getEventName()); - params.put(CompleteEventAction.PARAM_EVENT_COMPLETED_AT, new Date()); - params.put(CompleteEventAction.PARAM_EVENT_COMPLETED_BY, "roy"); - - checkSearchAspect(recordFolder); - - this.rmActionService.executeRecordsManagementAction(recordFolder, "completeEvent", params); - - txn.commit(); - txn = transactionService.getUserTransaction(false); - txn.begin(); - - assertTrue((Boolean)this.nodeService.getProperty(da.getNodeRef(), PROP_DISPOSITION_EVENTS_ELIGIBLE)); - - events = da.getEventCompletionDetails(); - assertNotNull(events); - assertEquals(3, events.size()); - for (EventCompletionDetails e : events) - { - assertTrue(e.isEventComplete()); - assertEquals("roy", e.getEventCompletedBy()); - assertNotNull(e.getEventCompletedAt()); - } - - checkSearchAspect(recordFolder); - - // Test undo - - params = new HashMap(1); - params.put(CompleteEventAction.PARAM_EVENT_NAME, events.get(2).getEventName()); - this.rmActionService.executeRecordsManagementAction(recordFolder, "undoEvent", params); - - txn.commit(); - txn = transactionService.getUserTransaction(false); - txn.begin(); - - assertFalse((Boolean)this.nodeService.getProperty(da.getNodeRef(), PROP_DISPOSITION_EVENTS_ELIGIBLE)); - - params = new HashMap(3); - params.put(CompleteEventAction.PARAM_EVENT_NAME, events.get(2).getEventName()); - params.put(CompleteEventAction.PARAM_EVENT_COMPLETED_AT, new Date()); - params.put(CompleteEventAction.PARAM_EVENT_COMPLETED_BY, "roy"); - - this.rmActionService.executeRecordsManagementAction(recordFolder, "completeEvent", params); - - txn.commit(); - txn = transactionService.getUserTransaction(false); - txn.begin(); - - assertTrue((Boolean)this.nodeService.getProperty(da.getNodeRef(), PROP_DISPOSITION_EVENTS_ELIGIBLE)); - - // Do the commit action - this.rmActionService.executeRecordsManagementAction(recordFolder, "cutoff", null); - - txn.commit(); - txn = transactionService.getUserTransaction(false); - txn.begin(); - - // Check events are gone - da = dispositionService.getNextDispositionAction(recordFolder); - - assertNotNull(da); - assertEquals("destroy", da.getDispositionActionDefinition().getName()); - assertNotNull(da.getAsOfDate()); - assertFalse((Boolean)this.nodeService.getProperty(da.getNodeRef(), PROP_DISPOSITION_EVENTS_ELIGIBLE)); - events = da.getEventCompletionDetails(); - assertNotNull(events); - assertEquals(0, events.size()); - - checkSearchAspect(recordFolder); - - txn.commit(); - } - - private NodeRef createRecord(NodeRef recordFolder) - { - return createRecord(recordFolder, "MyRecord.txt"); - } - - private NodeRef createRecord(NodeRef recordFolder, String name) - { - return createRecord(recordFolder, name, "There is some content in this record"); - } - - private NodeRef createRecord(NodeRef recordFolder, String name, String someTextContent) - { - // Create the document - Map props = new HashMap(1); - props.put(ContentModel.PROP_NAME, name); - NodeRef recordOne = this.nodeService.createNode(recordFolder, - ContentModel.ASSOC_CONTAINS, - QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, name), - ContentModel.TYPE_CONTENT, - props).getChildRef(); - - // Set the content - ContentWriter writer = this.contentService.getWriter(recordOne, ContentModel.PROP_CONTENT, true); - writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); - writer.setEncoding("UTF-8"); - writer.putContent(someTextContent); - - return recordOne; - } - - /** - * This method tests the filing of a custom type, as defined in DOD 5015. - */ - public void testFileDOD5015CustomTypes() throws Exception - { - NodeRef recordCategory = TestUtilities.getRecordCategory(rmService, nodeService, "Reports", "AIS Audit Records"); - - NodeRef recordFolder = createRecordFolder(recordCategory, "March AIS Audit Records"); - setComplete(); - endTransaction(); - - UserTransaction txn = transactionService.getUserTransaction(false); - txn.begin(); - - NodeRef testDocument = this.nodeService.createNode(recordFolder, - ContentModel.ASSOC_CONTAINS, - QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "CustomType"), - ContentModel.TYPE_CONTENT).getChildRef(); - - // It's not necessary to set content for this test. - - // File the record. - rmActionService.executeRecordsManagementAction(testDocument, "file"); - - assertTrue("testDocument should be a record.", rmService.isRecord(testDocument)); - - // Have the customType aspect applied.. - Map props = new HashMap(); - props.put(PROP_SCANNED_FORMAT.toPrefixString(serviceRegistry.getNamespaceService()), "f"); - props.put(PROP_SCANNED_FORMAT_VERSION.toPrefixString(serviceRegistry.getNamespaceService()), "1.0"); - props.put(PROP_RESOLUTION_X.toPrefixString(serviceRegistry.getNamespaceService()), "100"); - props.put(PROP_RESOLUTION_Y.toPrefixString(serviceRegistry.getNamespaceService()), "100"); - props.put(PROP_SCANNED_BIT_DEPTH.toPrefixString(serviceRegistry.getNamespaceService()), "10"); - rmActionService.executeRecordsManagementAction(testDocument, "applyScannedRecord", props); - - assertTrue("Custom type should have ScannedRecord aspect.", nodeService.hasAspect(testDocument, DOD5015Model.ASPECT_SCANNED_RECORD)); - - txn.rollback(); - } - - public void testFileDOD5015CustomTypes2() throws Exception - { - NodeRef recordCategory = TestUtilities.getRecordCategory(rmService, nodeService, "Reports", "AIS Audit Records"); - - NodeRef recordFolder = createRecordFolder(recordCategory, "March AIS Audit Records"); - setComplete(); - endTransaction(); - - UserTransaction txn = transactionService.getUserTransaction(false); - txn.begin(); - - NodeRef testDocument = this.nodeService.createNode(recordFolder, - ContentModel.ASSOC_CONTAINS, - QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "CustomType"), - ContentModel.TYPE_CONTENT).getChildRef(); - - // It's not necessary to set content for this test. - - // File the record - List aspects = new ArrayList(1); - aspects.add(DOD5015Model.ASPECT_SCANNED_RECORD); - Map props = new HashMap(1); - props.put(FileAction.PARAM_RECORD_METADATA_ASPECTS, (Serializable)aspects); - rmActionService.executeRecordsManagementAction(testDocument, "file", props); - - assertTrue("testDocument should be a record.", rmService.isRecord(testDocument)); - assertTrue("Custom type should have ScannedRecord aspect.", nodeService.hasAspect(testDocument, DOD5015Model.ASPECT_SCANNED_RECORD)); - - txn.rollback(); - } - - /** - * This method tests the filing of an already existing document i.e. one that is - * already contained within the document library. - */ - public void testFileFromDoclib() throws Exception - { - // Get the relevant RecordCategory and create a RecordFolder underneath it. - NodeRef recordCategory = TestUtilities.getRecordCategory(rmService, nodeService, "Reports", "AIS Audit Records"); - - NodeRef recordFolder = createRecordFolder(recordCategory, "March AIS Audit Records"); - setComplete(); - endTransaction(); - - UserTransaction txn = transactionService.getUserTransaction(false); - txn.begin(); - - // Unlike testBasicFilingTest, we now create a normal Alfresco content node - // rather than a fully-fledged record. The content must also be outside the - // fileplan. - - // Create a site - to put the content in. - final String rmTestSiteShortName = "rmTest" + System.currentTimeMillis(); - this.serviceRegistry.getSiteService().createSite("RMTestSite", rmTestSiteShortName, - "Test site for Records Management", "", SiteVisibility.PUBLIC); - - NodeRef siteRoot = this.serviceRegistry.getSiteService().getSite(rmTestSiteShortName).getNodeRef(); - NodeRef siteDocLib = this.nodeService.createNode(siteRoot, - ContentModel.ASSOC_CONTAINS, - QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "documentLibrary"), - ContentModel.TYPE_FOLDER).getChildRef(); - // Create the test document - NodeRef testDocument = this.nodeService.createNode(siteDocLib, - ContentModel.ASSOC_CONTAINS, - QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "PreexistingDocument.txt"), - ContentModel.TYPE_CONTENT).getChildRef(); - // Set some content - ContentWriter writer = this.contentService.getWriter(testDocument, ContentModel.PROP_CONTENT, true); - writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); - writer.setEncoding("UTF-8"); - writer.putContent("Some dummy content."); - - txn.commit(); - txn = transactionService.getUserTransaction(false); - txn.begin(); - - // Clearly, this should not be a record at this point. - assertFalse(this.nodeService.hasAspect(testDocument, ASPECT_RECORD)); - - // Now we want to file this document as a record within the RMA. - // To do this we simply move a document into the fileplan and file - this.serviceRegistry.getFileFolderService().move(testDocument, recordFolder, null); - rmActionService.executeRecordsManagementAction(testDocument, "file"); - - assertTrue("testDocument should be a record.", rmService.isRecord(testDocument)); - assertNotNull(this.nodeService.getProperty(testDocument, PROP_IDENTIFIER)); - assertNotNull(this.nodeService.getProperty(testDocument, PROP_DATE_FILED)); - - // Check the review schedule - assertTrue(this.nodeService.hasAspect(testDocument, ASPECT_VITAL_RECORD)); - assertNotNull(this.nodeService.getProperty(testDocument, PROP_REVIEW_AS_OF)); - - txn.commit(); - } - - /** - * This method tests the filing of non-electronic record. - */ - public void testFileNonElectronicRecord() throws Exception - { - setComplete(); - endTransaction(); - - // Create a record folder - final NodeRef recordFolder = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - // Get the relevant RecordCategory and create a RecordFolder underneath it. - NodeRef recordCategory = TestUtilities.getRecordCategory(rmService, nodeService, "Reports", "AIS Audit Records"); - NodeRef result = createRecordFolder(recordCategory, "March AIS Audit Records" + System.currentTimeMillis()); - - return result; - } - }); - - // Create a non-electronic record - final NodeRef nonElectronicTestRecord = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - // Create the document - NodeRef result = nodeService.createNode(recordFolder, - ContentModel.ASSOC_CONTAINS, - QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "Non-electronic Record" + System.currentTimeMillis()), - RecordsManagementModel.TYPE_NON_ELECTRONIC_DOCUMENT).getChildRef(); - - // There is no content on a non-electronic record. - - // These properties are required in order to declare the record. - Map props = nodeService.getProperties(result); - props.put(RecordsManagementModel.PROP_ORIGINATING_ORGANIZATION, "alfresco"); - props.put(RecordsManagementModel.PROP_ORIGINATOR, "admin"); - props.put(RecordsManagementModel.PROP_PUBLICATION_DATE, new Date()); - - Calendar fileCalendar = Calendar.getInstance(); - String year = Integer.toString(fileCalendar.get(Calendar.YEAR)); - props.put(RecordsManagementModel.PROP_DATE_FILED, fileCalendar.getTime()); - - String recordId = year + "-" + nodeService.getProperty(result, ContentModel.PROP_NODE_DBID).toString(); - props.put(RecordsManagementModel.PROP_IDENTIFIER, recordId); - - - nodeService.setProperties(result, props); - - return result; - } - }); - - // File and declare the record - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Void execute() throws Throwable - { - assertTrue("Expected non-electronic record to be a record.", rmService.isRecord(nonElectronicTestRecord)); - assertFalse("Expected non-electronic record not to be declared yet.", rmService.isRecordDeclared(nonElectronicTestRecord)); - - rmActionService.executeRecordsManagementAction(nonElectronicTestRecord, "declareRecord"); - - assertTrue("Non-electronic record should now be declared.", rmService.isRecordDeclared(nonElectronicTestRecord)); - - // These properties are added automatically when the record is filed - assertNotNull(nodeService.getProperty(nonElectronicTestRecord, RecordsManagementModel.PROP_IDENTIFIER)); - assertNotNull(nodeService.getProperty(nonElectronicTestRecord, RecordsManagementModel.PROP_DATE_FILED)); - -// assertNotNull(nodeService.getProperty(testRecord, ContentModel.PROP_TITLE)); -// assertNotNull(nodeService.getProperty(testRecord, RecordsManagementModel.PROP_SUPPLEMENTAL_MARKING_LIST)); -// assertNotNull(nodeService.getProperty(testRecord, RecordsManagementModel.PROP_MEDIA_TYPE)); -// assertNotNull(nodeService.getProperty(testRecord, RecordsManagementModel.PROP_FORMAT)); -// assertNotNull(nodeService.getProperty(testRecord, RecordsManagementModel.PROP_DATE_RECEIVED)); -// assertEquals("foo", nodeService.getProperty(testRecord, RecordsManagementModel.PROP_ADDRESS)); -// assertEquals("foo", nodeService.getProperty(testRecord, RecordsManagementModel.PROP_OTHER_ADDRESS)); -// assertNotNull(nodeService.getProperty(testRecord, RecordsManagementModel.PROP_LOCATION)); -// assertEquals("foo", nodeService.getProperty(testRecord, RecordsManagementModel.PROP_PROJECT_NAME)); - - //TODO Add links to other records as per test doc. - return null; - } - }); - } - - private NodeRef createRecordFolder(NodeRef recordCategory, String folderName) - { - Map folderProps = new HashMap(1); - folderProps.put(ContentModel.PROP_NAME, folderName); - NodeRef recordFolder = this.nodeService.createNode(recordCategory, - ContentModel.ASSOC_CONTAINS, - QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, folderName), - TYPE_RECORD_FOLDER).getChildRef(); - return recordFolder; - } - - /** - * Caveat Config - * - * @throws Exception - */ - public void testCaveatConfig() throws Exception - { - setComplete(); - endTransaction(); - - cleanCaveatConfigData(); - setupCaveatConfigData(); - - // set/reset allowed values (empty list by default) - - final List newValues = new ArrayList(4); - newValues.add(NOFORN); - newValues.add(NOCONTRACT); - newValues.add(FOUO); - newValues.add(FGI); - - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - rmAdminService.changeCustomConstraintValues(RecordsManagementCustomModel.CONSTRAINT_CUSTOM_SMLIST, newValues); - - return null; - } - }); - - final NodeRef recordFolder = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - // Test list of allowed values for caveats - - List allowedValues = AuthenticationUtil.runAs(new RunAsWork>() - { - public List doWork() - { - // get allowed values for given caveat (for current user) - return caveatConfigService.getRMAllowedValues("rmc:smList"); - } - }, "dfranco"); - - assertEquals(2, allowedValues.size()); - assertTrue(allowedValues.contains(NOFORN)); - assertTrue(allowedValues.contains(FOUO)); - - - allowedValues = AuthenticationUtil.runAs(new RunAsWork>() - { - public List doWork() - { - // get allowed values for given caveat (for current user) - return caveatConfigService.getRMAllowedValues("rmc:smList"); - } - }, "dmartinz"); - - assertEquals(4, allowedValues.size()); - assertTrue(allowedValues.contains(NOFORN)); - assertTrue(allowedValues.contains(NOCONTRACT)); - assertTrue(allowedValues.contains(FOUO)); - assertTrue(allowedValues.contains(FGI)); - - - // Create record category / record folder - - final NodeRef recordCategory = TestUtilities.getRecordCategory(rmService, nodeService, "Reports", "AIS Audit Records"); - assertNotNull(recordCategory); - assertEquals("AIS Audit Records", nodeService.getProperty(recordCategory, ContentModel.PROP_NAME)); - - NodeRef recordFolder = createRecordFolder(recordCategory, "March AIS Audit Records"); - assertNotNull(recordFolder); - assertEquals(TYPE_RECORD_FOLDER, nodeService.getType(recordFolder)); - - // set RM capabilities on the file plan - to view & read records - setPermission(filePlan, PermissionService.ALL_AUTHORITIES, RMPermissionModel.VIEW_RECORDS, true); - setPermission(filePlan, PermissionService.ALL_AUTHORITIES, RMPermissionModel.READ_RECORDS, true); - - // set RM capabilities on the record folder - to read records - setPermission(recordFolder, PermissionService.ALL_AUTHORITIES, RMPermissionModel.READ_RECORDS, true); - - - return recordFolder; - } - }); - - final String RECORD_NAME = "MyRecord"+System.currentTimeMillis()+".txt"; - final String SOME_CONTENT = "There is some content in this record"; - - final NodeRef recordOne = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - int expectedChildCount = nodeService.getChildAssocs(recordFolder).size(); - - NodeRef recordOne = createRecord(recordFolder, RECORD_NAME, SOME_CONTENT); - - assertEquals(expectedChildCount+1, nodeService.getChildAssocs(recordFolder).size()); - - return recordOne; - } - }); - - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - assertTrue(nodeService.hasAspect(recordOne, ASPECT_RECORD)); - - int expectedChildCount = nodeService.getChildAssocs(recordFolder).size()-1; - - // - // Test caveats (security interceptors) BEFORE setting properties - // - - sanityCheckAccess("dmartinz", recordFolder, recordOne, RECORD_NAME, SOME_CONTENT, true, expectedChildCount); - sanityCheckAccess("gsmith", recordFolder, recordOne, RECORD_NAME, SOME_CONTENT, true, expectedChildCount); - sanityCheckAccess("dsandy", recordFolder, recordOne, RECORD_NAME, SOME_CONTENT, true, expectedChildCount); - - // Test setting properties (with restricted set of allowed values) - - // Set supplemental markings list (on record) - // TODO - set supplemental markings list (on record folder) - - AuthenticationUtil.runAs(new RunAsWork() - { - public Object doWork() - { - // set RM capabilities on the file plan - to file records and add/edit properties (ie. edit record) - setPermission(filePlan, "dfranco", RMPermissionModel.FILING, true); - setPermission(filePlan, "dfranco", RMPermissionModel.EDIT_RECORD_METADATA, true); - return null; - } - }, "admin"); - - - AuthenticationUtil.setFullyAuthenticatedUser("dfranco"); - assertEquals(AccessStatus.ALLOWED, publicServiceAccessService.hasAccess("NodeService", "exists", recordFolder)); - - return null; - } - }); - - try - { - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - - // Set smList - - Map propValues = new HashMap(1); - List smList = new ArrayList(3); - smList.add(FOUO); - smList.add(NOFORN); - smList.add(NOCONTRACT); - propValues.put(RecordsManagementModel.PROP_SUPPLEMENTAL_MARKING_LIST, (Serializable)smList); - nodeService.addProperties(recordOne, propValues); - - return null; - } - }); - - fail("Should fail with integrity exception"); // user 'dfranco' not allowed 'NOCONTRACT' - } - catch (IntegrityException ie) - { - // expected - } - - try - { - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - // Set smList - - Map propValues = new HashMap(1); - List smList = new ArrayList(2); - smList.add(FOUO); - smList.add(NOFORN); - propValues.put(RecordsManagementModel.PROP_SUPPLEMENTAL_MARKING_LIST, (Serializable)smList); - nodeService.addProperties(recordOne, propValues); - - return null; - } - }); - } - catch (IntegrityException ie) - { - fail(""+ie); - } - - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - @SuppressWarnings("unchecked") - List smList = (List)nodeService.getProperty(recordOne, RecordsManagementModel.PROP_SUPPLEMENTAL_MARKING_LIST); - assertEquals(2, smList.size()); - assertTrue(smList.contains(NOFORN)); - assertTrue(smList.contains(FOUO)); - - return null; - } - }); - - // User-defined field (in this case, "rmc:prjList" on record) - - // Create custom constraint (or reset values if it already exists) - - // create new custom constraint - - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - try - { - List emptyList = new ArrayList(0); - rmAdminService.addCustomConstraintDefinition(CONSTRAINT_CUSTOM_PRJLIST, "Some Projects", true, emptyList, MatchLogic.AND); - } - catch (AlfrescoRuntimeException e) - { - // ignore - ie. assume exception is due to the fact that it already exists - } - - return null; - } - }); - - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - List newerValues = new ArrayList(3); - newerValues.add(PRJ_A); - newerValues.add(PRJ_B); - newerValues.add(PRJ_C); - - rmAdminService.changeCustomConstraintValues(CONSTRAINT_CUSTOM_PRJLIST, newerValues); - - return null; - } - }); - - // define custom property and reference custom constraint - - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - try - { - // Define a custom "project list" property (for records) - note: multi-valued - rmAdminService.addCustomPropertyDefinition( - PROP_CUSTOM_PRJLIST, - ASPECT_RECORD, - PROP_CUSTOM_PRJLIST.getLocalName(), - DataTypeDefinition.TEXT, "Projects", - null, - null, - true, - false, - false, - CONSTRAINT_CUSTOM_PRJLIST); - } - catch (AlfrescoRuntimeException e) - { - // ignore - ie. assume exception is due to the fact that it already exists - } - - return null; - } - }); - - try - { - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - - // Set prjList - - Map propValues = new HashMap(1); - List prjList = new ArrayList(3); - prjList.add(PRJ_A); - prjList.add(PRJ_B); - propValues.put(PROP_CUSTOM_PRJLIST, (Serializable)prjList); - nodeService.addProperties(recordOne, propValues); - - return null; - } - }); - - fail("Should fail with integrity exception"); // user 'dfranco' not allowed 'Project B' - } - catch (IntegrityException ie) - { - // expected - } - - try - { - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - // Set prjList - Map propValues = new HashMap(1); - List prjList = new ArrayList(3); - prjList.add(PRJ_A); - propValues.put(PROP_CUSTOM_PRJLIST, (Serializable)prjList); - nodeService.addProperties(recordOne, propValues); - - return null; - } - }); - } - catch (IntegrityException ie) - { - fail(""+ie); - } - - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - @SuppressWarnings("unchecked") - List prjList = (List)nodeService.getProperty(recordOne, PROP_CUSTOM_PRJLIST); - assertEquals(1, prjList.size()); - assertTrue(prjList.contains(PRJ_A)); - - return null; - } - }); - - // - // Test caveats (security interceptors) AFTER setting properties - // - - int expectedChildCount = nodeService.getChildAssocs(recordFolder).size()-1; - sanityCheckAccess("dmartinz", recordFolder, recordOne, RECORD_NAME, SOME_CONTENT, true, expectedChildCount); - sanityCheckAccess("gsmith", recordFolder, recordOne, RECORD_NAME, SOME_CONTENT, false, expectedChildCount); // denied by rma:prjList ("Project A") - - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); - addToGroup("gsmith", "Engineering"); - - return null; - } - }); - - sanityCheckAccess("gsmith", recordFolder, recordOne, RECORD_NAME, SOME_CONTENT, true, expectedChildCount); - sanityCheckAccess("dsandy", recordFolder, recordOne, RECORD_NAME, SOME_CONTENT, false, expectedChildCount); // denied by rma:smList ("NOFORN", "FOUO") - - cleanCaveatConfigData(); - } - - private void setPermission(NodeRef nodeRef, String authority, String permission, boolean allow) - { - permissionService.setPermission(nodeRef, authority, permission, allow); - if (permission.equals(RMPermissionModel.FILING)) - { - if (rmService.isRecordCategory(nodeRef) == true) - { - List assocs = nodeService.getChildAssocs(nodeRef, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); - for (ChildAssociationRef assoc : assocs) - { - NodeRef child = assoc.getChildRef(); - if (rmService.isRecordFolder(child) == true || rmService.isRecordCategory(child) == true) - { - setPermission(child, authority, permission, allow); - } - } - } - } - } - - private void cleanCaveatConfigData() - { - startNewTransaction(); - - AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); - - deleteUser("jrangel"); - deleteUser("dmartinz"); - deleteUser("jrogers"); - deleteUser("hmcneil"); - deleteUser("dfranco"); - deleteUser("gsmith"); - deleteUser("eharris"); - deleteUser("bbayless"); - deleteUser("mhouse"); - deleteUser("aly"); - deleteUser("dsandy"); - deleteUser("driggs"); - deleteUser("test1"); - - deleteGroup("Engineering"); - deleteGroup("Finance"); - deleteGroup("test1"); - - caveatConfigService.updateOrCreateCaveatConfig("{}"); // empty config ! - - setComplete(); - endTransaction(); - } - - private void setupCaveatConfigData() - { - startNewTransaction(); - - // Switch to admin - AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); - - // Create test users/groups (if they do not already exist) - - createUser("jrangel"); - createUser("dmartinz"); - createUser("jrogers"); - createUser("hmcneil"); - createUser("dfranco"); - createUser("gsmith"); - createUser("eharris"); - createUser("bbayless"); - createUser("mhouse"); - createUser("aly"); - createUser("dsandy"); - createUser("driggs"); - createUser("test1"); - - createGroup("Engineering"); - createGroup("Finance"); - createGroup("test1"); - - addToGroup("jrogers", "Engineering"); - addToGroup("dfranco", "Finance"); - - // not in grouo to start with - added later - //addToGroup("gsmith", "Engineering"); - - File file = new File(System.getProperty("user.dir")+"/test-resources/testCaveatConfig2.json"); // from test-resources - assertTrue(file.exists()); - - caveatConfigService.updateOrCreateCaveatConfig(file); - - setComplete(); - endTransaction(); - } - - protected void createUser(String userName) - { - if (! authenticationService.authenticationExists(userName)) - { - authenticationService.createAuthentication(userName, "PWD".toCharArray()); - } - - if (! personService.personExists(userName)) - { - PropertyMap ppOne = new PropertyMap(4); - ppOne.put(ContentModel.PROP_USERNAME, userName); - ppOne.put(ContentModel.PROP_FIRSTNAME, "firstName"); - ppOne.put(ContentModel.PROP_LASTNAME, "lastName"); - ppOne.put(ContentModel.PROP_EMAIL, "email@email.com"); - ppOne.put(ContentModel.PROP_JOBTITLE, "jobTitle"); - - personService.createPerson(ppOne); - } - } - - protected void deleteUser(String userName) - { - if (personService.personExists(userName)) - { - personService.deletePerson(userName); - } - } - - protected void createGroup(String groupShortName) - { - createGroup(null, groupShortName); - } - - protected void createGroup(String parentGroupShortName, String groupShortName) - { - if (parentGroupShortName != null) - { - String parentGroupFullName = authorityService.getName(AuthorityType.GROUP, parentGroupShortName); - if (authorityService.authorityExists(parentGroupFullName) == false) - { - authorityService.createAuthority(AuthorityType.GROUP, groupShortName, groupShortName, null); - authorityService.addAuthority(parentGroupFullName, groupShortName); - } - } - else - { - authorityService.createAuthority(AuthorityType.GROUP, groupShortName, groupShortName, null); - } - } - - protected void deleteGroup(String groupShortName) - { - String groupFullName = authorityService.getName(AuthorityType.GROUP, groupShortName); - if (authorityService.authorityExists(groupFullName) == true) - { - authorityService.deleteAuthority(groupFullName); - } - } - - protected void addToGroup(String authorityName, String groupShortName) - { - authorityService.addAuthority(authorityService.getName(AuthorityType.GROUP, groupShortName), authorityName); - } - - protected void removeFromGroup(String authorityName, String groupShortName) - { - authorityService.removeAuthority(authorityService.getName(AuthorityType.GROUP, groupShortName), authorityName); - } - - private void sanityCheckAccess(String user, NodeRef recordFolder, NodeRef record, String expectedName, String expectedContent, boolean expectedAllowed, int baseCount) - { - //startNewTransaction(); - - AuthenticationUtil.setFullyAuthenticatedUser(user); - - // Sanity check search service - eg. query - - String query = "ID:"+AbstractLuceneQueryParser.escape(record.toString()); - ResultSet rs = this.searchService.query(SPACES_STORE, SearchService.LANGUAGE_LUCENE, query); - - if (expectedAllowed) - { - assertEquals(1, rs.length()); - assertEquals(record.toString(), rs.getNodeRef(0).toString()); - } - else - { - assertEquals(0, rs.length()); - } - rs.close(); - - // Sanity check node service - eg. getProperty, getChildAssocs - - try - { - Serializable value = this.nodeService.getProperty(record, ContentModel.PROP_NAME); - - if (expectedAllowed) - { - assertNotNull(value); - assertEquals(expectedName, (String)value); - } - else - { - fail("Unexpected - access should be denied by caveats"); - } - } - catch (AccessDeniedException ade) - { - if (expectedAllowed) - { - fail("Unexpected - access should be allowed by caveats"); - } - - // expected - } - - List childAssocs = nodeService.getChildAssocs(recordFolder); - - if (expectedAllowed) - { - assertEquals(baseCount+1, childAssocs.size()); - assertEquals(record.toString(), childAssocs.get(baseCount).getChildRef().toString()); - } - else - { - assertEquals(baseCount, childAssocs.size()); - } - - // Sanity check content service - eg. getReader - - try - { - ContentReader reader = this.contentService.getReader(record, ContentModel.PROP_CONTENT); - - if (expectedAllowed) - { - assertNotNull(reader); - assertEquals(expectedContent, reader.getContentString()); - } - else - { - fail("Unexpected - access should be denied by caveats"); - } - } - catch (AccessDeniedException ade) - { - if (expectedAllowed) - { - fail("Unexpected - access should be allowed by caveats"); - } - - // expected - } - - //setComplete(); - //endTransaction(); - } - - /** - * https://issues.alfresco.com/jira/browse/ETHREEOH-3587 - */ - public void testETHREEOH3587() - { - NodeRef recordFolder = TestUtilities.getRecordFolder(rmService, nodeService, "Reports", "AIS Audit Records", "January AIS Audit Records"); - assertNotNull(recordFolder); - - // Create a record - final NodeRef record = createRecord(recordFolder, GUID.generate()); - - // Commit in order to trigger the setUpRecordFolder behaviour - setComplete(); - endTransaction(); - - // Now try and update the id, this should fail - try - { - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - // Lets just check the record identifier has been set - String id = (String)nodeService.getProperty(record, RecordsManagementModel.PROP_IDENTIFIER); - assertNotNull(id); - - nodeService.setProperty(record, RecordsManagementModel.PROP_IDENTIFIER, "randomValue"); - - return null; - } - }); - - fail("You should not be allowed to update the identifier of a record once it has been created."); - } - catch(AlfrescoRuntimeException e) - { - // Expected - } - - // TODO set the identifier of the second record to be the same as the first .... - } - - /** - * Vital Record Test - * - * @throws Exception - */ - public void testVitalRecords() throws Exception - { - // - // Create a record folder under a "vital" category - // - - // TODO Don't think I need to do this. Can I reuse the existing January one? - - NodeRef vitalRecCategory = - TestUtilities.getRecordCategory(rmService, nodeService, "Reports", "AIS Audit Records"); - - assertNotNull(vitalRecCategory); - assertEquals("AIS Audit Records", - this.nodeService.getProperty(vitalRecCategory, ContentModel.PROP_NAME)); - - NodeRef vitalRecFolder = this.nodeService.createNode(vitalRecCategory, - ContentModel.ASSOC_CONTAINS, - QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, - "March AIS Audit Records"), - TYPE_RECORD_FOLDER).getChildRef(); - setComplete(); - endTransaction(); - UserTransaction txn1 = transactionService.getUserTransaction(false); - txn1.begin(); - - // Check the Vital Record data - VitalRecordDefinition vitalRecCatDefinition = vitalRecordService.getVitalRecordDefinition(vitalRecCategory); - assertNotNull("This record category should have a VitalRecordDefinition", vitalRecCatDefinition); - assertTrue(vitalRecCatDefinition.isEnabled()); - - VitalRecordDefinition vitalRecFolderDefinition = vitalRecordService.getVitalRecordDefinition(vitalRecFolder); - assertNotNull("This record folder should have a VitalRecordDefinition", vitalRecFolderDefinition); - assertTrue(vitalRecFolderDefinition.isEnabled()); - - assertEquals("The Vital Record reviewPeriod in the folder did not match its parent category", - vitalRecFolderDefinition.getReviewPeriod(), - vitalRecCatDefinition.getReviewPeriod()); - - // check the search aspect for both the category and folder - checkSearchAspect(vitalRecFolder); - - // Create a vital record - NodeRef vitalRecord = this.nodeService.createNode(vitalRecFolder, - ContentModel.ASSOC_CONTAINS, - QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, - "MyVitalRecord" + System.currentTimeMillis() +".txt"), - ContentModel.TYPE_CONTENT).getChildRef(); - - // Set the content - ContentWriter writer = this.contentService.getWriter(vitalRecord, ContentModel.PROP_CONTENT, true); - writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); - writer.setEncoding("UTF-8"); - writer.putContent("There is some content in this record"); - - rmActionService.executeRecordsManagementAction(vitalRecord, "file"); - - txn1.commit(); - - UserTransaction txn2 = transactionService.getUserTransaction(false); - txn2.begin(); - - // Check the review schedule - - assertTrue(this.nodeService.hasAspect(vitalRecord, ASPECT_VITAL_RECORD)); - VitalRecordDefinition vitalRecDefinition = vitalRecordService.getVitalRecordDefinition(vitalRecord); - assertTrue(vitalRecDefinition.isEnabled()); - Date vitalRecordAsOfDate = (Date)this.nodeService.getProperty(vitalRecord, PROP_REVIEW_AS_OF); - assertNotNull("vitalRecord should have a reviewAsOf date.", vitalRecordAsOfDate); - - // check the search aspect for the vital record - checkSearchAspect(vitalRecord); - - // - // Create a record folder under a "non-vital" category - // - NodeRef nonVitalRecordCategory = TestUtilities.getRecordCategory(rmService, nodeService, "Reports", "Unit Manning Documents"); - assertNotNull(nonVitalRecordCategory); - assertEquals("Unit Manning Documents", this.nodeService.getProperty(nonVitalRecordCategory, ContentModel.PROP_NAME)); - - NodeRef nonVitalFolder = this.nodeService.createNode(nonVitalRecordCategory, - ContentModel.ASSOC_CONTAINS, - QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "4th Quarter Unit Manning Documents"), - TYPE_RECORD_FOLDER).getChildRef(); - txn2.commit(); - - UserTransaction txn3 = transactionService.getUserTransaction(false); - txn3.begin(); - - // Check the Vital Record data - assertFalse(vitalRecordService.getVitalRecordDefinition(nonVitalRecordCategory).isEnabled()); - assertFalse(vitalRecordService.getVitalRecordDefinition(nonVitalFolder).isEnabled()); - assertEquals("The Vital Record reviewPeriod in the folder did not match its parent category", - vitalRecordService.getVitalRecordDefinition(nonVitalFolder).getReviewPeriod(), - vitalRecordService.getVitalRecordDefinition(nonVitalRecordCategory).getReviewPeriod()); - - // Create a record - NodeRef nonVitalRecord = this.nodeService.createNode(nonVitalFolder, - ContentModel.ASSOC_CONTAINS, - QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "MyNonVitalRecord.txt"), - ContentModel.TYPE_CONTENT).getChildRef(); - - // Set content - writer = this.contentService.getWriter(nonVitalRecord, ContentModel.PROP_CONTENT, true); - writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); - writer.setEncoding("UTF-8"); - writer.putContent("There is some content in this record"); - - this.rmActionService.executeRecordsManagementAction(nonVitalRecord, "file"); - - txn3.commit(); - - UserTransaction txn4 = transactionService.getUserTransaction(false); - txn4.begin(); - - // Check the review schedule - assertFalse(this.nodeService.hasAspect(nonVitalRecord, ASPECT_VITAL_RECORD)); - assertFalse(vitalRecordService.getVitalRecordDefinition(nonVitalRecord).isEnabled()); - assertEquals("The Vital Record reviewPeriod did not match its parent category", - vitalRecordService.getVitalRecordDefinition(nonVitalRecord).getReviewPeriod(), - vitalRecordService.getVitalRecordDefinition(nonVitalFolder).getReviewPeriod()); - - // Declare as a record - assertTrue(this.nodeService.hasAspect(nonVitalRecord, ASPECT_RECORD)); - - assertTrue("Declared record already on prior to test", - this.nodeService.hasAspect(nonVitalRecord, ASPECT_DECLARED_RECORD) == false); - - - this.nodeService.setProperty(nonVitalRecord, RecordsManagementModel.PROP_PUBLICATION_DATE, new Date()); - List smList = new ArrayList(2); - smList.add(FOUO); - smList.add(NOFORN); - this.nodeService.setProperty(nonVitalRecord, RecordsManagementModel.PROP_SUPPLEMENTAL_MARKING_LIST, (Serializable)smList); - this.nodeService.setProperty(nonVitalRecord, RecordsManagementModel.PROP_MEDIA_TYPE, "mediaTypeValue"); - this.nodeService.setProperty(nonVitalRecord, RecordsManagementModel.PROP_FORMAT, "formatValue"); - this.nodeService.setProperty(nonVitalRecord, RecordsManagementModel.PROP_DATE_RECEIVED, new Date()); - this.nodeService.setProperty(nonVitalRecord, RecordsManagementModel.PROP_ORIGINATOR, "origValue"); - this.nodeService.setProperty(nonVitalRecord, RecordsManagementModel.PROP_ORIGINATING_ORGANIZATION, "origOrgValue"); - this.nodeService.setProperty(nonVitalRecord, ContentModel.PROP_TITLE, "titleValue"); - - this.rmActionService.executeRecordsManagementAction(nonVitalRecord, "declareRecord"); - assertTrue(this.nodeService.hasAspect(nonVitalRecord, ASPECT_RECORD)); - assertTrue("Declared aspect not set", this.nodeService.hasAspect(nonVitalRecord, ASPECT_DECLARED_RECORD)); - - // - // Now we will change the vital record indicator in the containers above these records - // and ensure that the change is reflected down to the record. - // - - // 1. Switch parent folder from non-vital to vital. - this.nodeService.setProperty(nonVitalFolder, PROP_VITAL_RECORD_INDICATOR, true); - this.nodeService.setProperty(nonVitalFolder, PROP_REVIEW_PERIOD, "week|1"); - - txn4.commit(); - - UserTransaction txn5 = transactionService.getUserTransaction(false); - txn5.begin(); - - // check the folder search aspect - checkSearchAspect(nonVitalFolder); - - NodeRef formerlyNonVitalRecord = nonVitalRecord; - - assertTrue("Expected VitalRecord aspect not present", nodeService.hasAspect(formerlyNonVitalRecord, ASPECT_VITAL_RECORD)); - VitalRecordDefinition formerlyNonVitalRecordDefinition = vitalRecordService.getVitalRecordDefinition(formerlyNonVitalRecord); - assertNotNull(formerlyNonVitalRecordDefinition); - - assertEquals("The Vital Record reviewPeriod is wrong.", new Period("week|1"), - vitalRecordService.getVitalRecordDefinition(formerlyNonVitalRecord).getReviewPeriod()); - assertNotNull("formerlyNonVitalRecord should now have a reviewAsOf date.", - nodeService.getProperty(formerlyNonVitalRecord, PROP_REVIEW_AS_OF)); - - // check search aspect for the new vital record - checkSearchAspect(formerlyNonVitalRecord); - - // 2. Switch parent folder from vital to non-vital. - this.nodeService.setProperty(vitalRecFolder, PROP_VITAL_RECORD_INDICATOR, false); - - txn5.commit(); - - UserTransaction txn6 = transactionService.getUserTransaction(false); - txn6.begin(); - - NodeRef formerlyVitalRecord = vitalRecord; - - assertTrue("Unexpected VitalRecord aspect present", - nodeService.hasAspect(formerlyVitalRecord, ASPECT_VITAL_RECORD) == false); - VitalRecordDefinition formerlyVitalRecordDefinition = vitalRecordService.getVitalRecordDefinition(formerlyVitalRecord); - assertNotNull(formerlyVitalRecordDefinition); - assertNull("formerlyVitalRecord should now not have a reviewAsOf date.", - nodeService.getProperty(formerlyVitalRecord, PROP_REVIEW_AS_OF)); - - // 3. override the VitalRecordDefinition between Category, Folder, Record and ensure - // the overrides work - - // First switch the non-vital record folder back to vital. - this.nodeService.setProperty(vitalRecFolder, PROP_VITAL_RECORD_INDICATOR, true); - - txn6.commit(); - UserTransaction txn7 = transactionService.getUserTransaction(false); - txn7.begin(); - - assertTrue("Unexpected VitalRecord aspect present", - nodeService.hasAspect(vitalRecord, ASPECT_VITAL_RECORD)); - - // The reviewAsOf date should be changing as the parent review periods are updated. - Date initialReviewAsOfDate = (Date)nodeService.getProperty(vitalRecord, PROP_REVIEW_AS_OF); - assertNotNull("record should have a reviewAsOf date.", - initialReviewAsOfDate); - - // Change some of the VitalRecordDefinition in Record Category - Map recCatProps = this.nodeService.getProperties(vitalRecCategory); - - // Run this test twice (after a clean db) and it fails at the below line. - assertEquals(new Period("week|1"), recCatProps.get(PROP_REVIEW_PERIOD)); - this.nodeService.setProperty(vitalRecCategory, PROP_REVIEW_PERIOD, new Period("day|1")); - - txn7.commit(); - UserTransaction txn8 = transactionService.getUserTransaction(false); - txn8.begin(); - - assertEquals(new Period("day|1"), vitalRecordService.getVitalRecordDefinition(vitalRecCategory).getReviewPeriod()); - assertEquals(new Period("day|1"), vitalRecordService.getVitalRecordDefinition(vitalRecFolder).getReviewPeriod()); - - // check the search aspect of the folder after period change - checkSearchAspect(vitalRecFolder); - - // Change some of the VitalRecordDefinition in Record Folder - Map folderProps = this.nodeService.getProperties(vitalRecFolder); - assertEquals(new Period("day|1"), folderProps.get(PROP_REVIEW_PERIOD)); - this.nodeService.setProperty(vitalRecFolder, PROP_REVIEW_PERIOD, new Period("month|1")); - - txn8.commit(); - UserTransaction txn9 = transactionService.getUserTransaction(false); - txn9.begin(); - - assertEquals(new Period("day|1"), vitalRecordService.getVitalRecordDefinition(vitalRecCategory).getReviewPeriod()); - assertEquals(new Period("month|1"), vitalRecordService.getVitalRecordDefinition(vitalRecFolder).getReviewPeriod()); - - // check the search aspect of the folder after period change - checkSearchAspect(vitalRecFolder); - - // Need to commit the transaction to trigger the behaviour that handles changes to VitalRecord Definition. - txn9.commit(); - UserTransaction txn10 = transactionService.getUserTransaction(false); - txn10.begin(); - - Date newReviewAsOfDate = (Date)nodeService.getProperty(vitalRecord, PROP_REVIEW_AS_OF); - assertNotNull("record should have a reviewAsOf date.", initialReviewAsOfDate); - assertTrue("reviewAsOfDate should have changed.", - initialReviewAsOfDate.toString().equals(newReviewAsOfDate.toString()) == false); - - // check the search aspect of the record after period change - checkSearchAspect(vitalRecord); - - // Now clean up after this test. - nodeService.deleteNode(vitalRecord); - nodeService.deleteNode(vitalRecFolder); - nodeService.deleteNode(nonVitalRecord); - nodeService.deleteNode(nonVitalFolder); - nodeService.setProperty(vitalRecCategory, PROP_REVIEW_PERIOD, new Period("week|1")); - - txn10.commit(); - } - -} diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/system/RecordsManagementServiceImplSystemTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/system/RecordsManagementServiceImplSystemTest.java deleted file mode 100644 index 7ca0a52d65..0000000000 --- a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/system/RecordsManagementServiceImplSystemTest.java +++ /dev/null @@ -1,812 +0,0 @@ -/* - * Copyright (C) 2005-2011 Alfresco Software Limited. - * - * This file is part of Alfresco - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - */ -package org.alfresco.module.org_alfresco_module_rm.test.system; - -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 javax.transaction.UserTransaction; - -import org.alfresco.model.ContentModel; -import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; -import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionService; -import org.alfresco.module.org_alfresco_module_rm.action.impl.BroadcastDispositionActionDefinitionUpdateAction; -import org.alfresco.module.org_alfresco_module_rm.action.impl.FileAction; -import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionAction; -import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionActionDefinition; -import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule; -import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService; -import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEvent; -import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; -import org.alfresco.module.org_alfresco_module_rm.test.util.TestUtilities; -import org.alfresco.module.org_alfresco_module_rm.vital.VitalRecordDefinition; -import org.alfresco.module.org_alfresco_module_rm.vital.VitalRecordService; -import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.repo.transaction.RetryingTransactionHelper; -import org.alfresco.service.cmr.model.FileFolderService; -import org.alfresco.service.cmr.model.FileInfo; -import org.alfresco.service.cmr.repository.ChildAssociationRef; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.repository.Period; -import org.alfresco.service.cmr.repository.StoreRef; -import org.alfresco.service.cmr.search.SearchService; -import org.alfresco.service.namespace.NamespaceService; -import org.alfresco.service.namespace.QName; -import org.alfresco.service.namespace.RegexQNamePattern; -import org.alfresco.service.transaction.TransactionService; -import org.alfresco.util.BaseSpringTest; - -/** - * System test for records management service. - * - * Awaiting refactoring into records management test. - * - * @author Roy Wetherall - */ -public class RecordsManagementServiceImplSystemTest extends BaseSpringTest implements RecordsManagementModel -{ - protected static StoreRef SPACES_STORE = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore"); - - private NodeRef filePlan; - - private FileFolderService fileFolderService; - private NodeService nodeService; - private NodeService unprotectedNodeService; - private RecordsManagementActionService rmActionService; - private RecordsManagementService rmService; - private SearchService searchService; - private TransactionService transactionService; - private RetryingTransactionHelper transactionHelper; - private DispositionService dispositionService; - private VitalRecordService vitalRecordService; - - @Override - protected void onSetUpInTransaction() throws Exception - { - super.onSetUpInTransaction(); - - // Get the service required in the tests - this.fileFolderService = (FileFolderService)this.applicationContext.getBean("FileFolderService"); - this.nodeService = (NodeService)this.applicationContext.getBean("NodeService"); - this.unprotectedNodeService = (NodeService)this.applicationContext.getBean("nodeService"); - this.transactionService = (TransactionService)this.applicationContext.getBean("TransactionService"); - this.searchService = (SearchService)this.applicationContext.getBean("searchService"); - this.rmActionService = (RecordsManagementActionService)this.applicationContext.getBean("recordsManagementActionService"); - this.rmService = (RecordsManagementService)this.applicationContext.getBean("recordsManagementService"); - this.transactionHelper = (RetryingTransactionHelper)this.applicationContext.getBean("retryingTransactionHelper"); - this.dispositionService = (DispositionService)this.applicationContext.getBean("dispositionService"); - vitalRecordService = (VitalRecordService)applicationContext.getBean("VitalRecordService"); - - // Set the current security context as admin - AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); - - // Get the test data - setUpTestData(); - } - - private void setUpTestData() - { - filePlan = TestUtilities.loadFilePlanData(applicationContext); - } - - @Override - protected void onTearDownInTransaction() throws Exception - { - try - { - UserTransaction txn = transactionService.getUserTransaction(false); - txn.begin(); - this.nodeService.deleteNode(filePlan); - txn.commit(); - } - catch (Exception e) - { - // Nothing - //System.out.println("DID NOT DELETE FILE PLAN!"); - } - } - - public void testDispositionPresence() throws Exception - { - setComplete(); - endTransaction(); - - // create a record category node in - final NodeRef nodeRef = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - NodeRef rootNode = nodeService.getRootNode(SPACES_STORE); - Map props = new HashMap(1); - String recordCategoryName = "Test Record Category"; - props.put(ContentModel.PROP_NAME, recordCategoryName); - NodeRef result = nodeService.createNode(rootNode, ContentModel.ASSOC_CHILDREN, - QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, QName.createValidLocalName(recordCategoryName)), - TYPE_RECORD_CATEGORY, props).getChildRef(); - return result; - } - }); - - - // ensure the record category node has the scheduled aspect and the disposition schedule association - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Void execute() throws Throwable - { - assertTrue(nodeService.hasAspect(nodeRef, RecordsManagementModel.ASPECT_SCHEDULED)); - List scheduleAssocs = nodeService.getChildAssocs(nodeRef, ASSOC_DISPOSITION_SCHEDULE, RegexQNamePattern.MATCH_ALL); - - - assertNotNull(scheduleAssocs); - assertEquals(1, scheduleAssocs.size()); - - // test retrieval of the disposition schedule via RM service - DispositionSchedule schedule = dispositionService.getDispositionSchedule(nodeRef); - assertNotNull(schedule); - return null; - } - }); - } - - /** - * This test method contains a subset of the tests in TC 7-2 of the DoD doc. - * @throws Exception - */ - public void testRescheduleRecord_IsNotCutOff() throws Exception - { - final NodeRef recCat = TestUtilities.getRecordCategory(rmService, nodeService, "Reports", "AIS Audit Records"); - // This RC has disposition instructions "Cut off monthly, hold 1 month, then destroy." - - setComplete(); - endTransaction(); - - // Create a suitable folder for this test. - final NodeRef testFolder = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - Map folderProps = new HashMap(1); - String folderName = "testFolder" + System.currentTimeMillis(); - folderProps.put(ContentModel.PROP_NAME, folderName); - NodeRef recordFolder = nodeService.createNode(recCat, - ContentModel.ASSOC_CONTAINS, - QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, folderName), - TYPE_RECORD_FOLDER).getChildRef(); - return recordFolder; - } - }); - - // Create a record in the test folder. File it and declare it. - final NodeRef testRecord = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - final NodeRef result = nodeService.createNode(testFolder, ContentModel.ASSOC_CONTAINS, - QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, - "Record" + System.currentTimeMillis() + ".txt"), - ContentModel.TYPE_CONTENT).getChildRef(); - - rmActionService.executeRecordsManagementAction(result, "file"); - TestUtilities.declareRecord(result, unprotectedNodeService, rmActionService); - return result; - } - }); - - assertTrue("recCat missing scheduled aspect", nodeService.hasAspect(recCat, RecordsManagementModel.ASPECT_SCHEDULED)); - assertFalse("folder should not have scheduled aspect", nodeService.hasAspect(testFolder, RecordsManagementModel.ASPECT_SCHEDULED)); - assertFalse("record should not have scheduled aspect", nodeService.hasAspect(testRecord, RecordsManagementModel.ASPECT_SCHEDULED)); - - assertFalse("recCat should not have dispositionLifecycle aspect", nodeService.hasAspect(recCat, RecordsManagementModel.ASPECT_DISPOSITION_LIFECYCLE)); - assertTrue("testFolder missing dispositionLifecycle aspect", nodeService.hasAspect(testFolder, RecordsManagementModel.ASPECT_DISPOSITION_LIFECYCLE)); - assertFalse("testRecord should not have dispositionLifecycle aspect", nodeService.hasAspect(testRecord, RecordsManagementModel.ASPECT_DISPOSITION_LIFECYCLE)); - - // Change the cutoff conditions for the associated record category - final Date dateBeforeChange = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Date execute() throws Throwable - { - Date asOfDate = dispositionService.getNextDispositionAction(testFolder).getAsOfDate(); - System.out.println("Going to change the disposition asOf Date."); - System.out.println(" - Original value: " + asOfDate); - - // Now change "Cut off monthly, hold 1 month, then destroy." - // to "Cut off yearly, hold 1 month, then destroy." - List dads = dispositionService.getDispositionSchedule(testFolder).getDispositionActionDefinitions(); - DispositionActionDefinition firstDAD = dads.get(0); - assertEquals("cutoff", firstDAD.getName()); - NodeRef dadNode = firstDAD.getNodeRef(); - - nodeService.setProperty(dadNode, PROP_DISPOSITION_PERIOD, new Period("year|1")); - - List updatedProps = new ArrayList(1); - updatedProps.add(PROP_DISPOSITION_PERIOD); - refreshDispositionActionDefinition(dadNode, updatedProps); - - return asOfDate; - } - }); - - // view the record metadata to verify that the record has been rescheduled. - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Void execute() throws Throwable - { - DispositionAction nextDispositionAction = dispositionService.getNextDispositionAction(testFolder); - - assertEquals("cutoff", nextDispositionAction.getName()); - Date asOfDateAfterChange = nextDispositionAction.getAsOfDate(); - System.out.println(" - Updated value: " + asOfDateAfterChange); - - assertFalse("Expected disposition asOf date to change.", asOfDateAfterChange.equals(dateBeforeChange)); - return null; - } - }); - - // Change the disposition type (e.g. time-based to event-based) - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Void execute() throws Throwable - { - List rmes = dispositionService.getNextDispositionAction(testFolder).getDispositionActionDefinition().getEvents(); - System.out.println("Going to change the RMEs."); - System.out.println(" - Original value: " + rmes); - - List dads = dispositionService.getDispositionSchedule(testFolder).getDispositionActionDefinitions(); - DispositionActionDefinition firstDAD = dads.get(0); - assertEquals("cutoff", firstDAD.getName()); - NodeRef dadNode = firstDAD.getNodeRef(); - -// nodeService.setProperty(dadNode, PROP_DISPOSITION_PERIOD, null); - List eventNames= new ArrayList(); - eventNames.add("study_complete"); - nodeService.setProperty(dadNode, PROP_DISPOSITION_EVENT, (Serializable)eventNames); - - return null; - } - }); - // Now add a second event to the same - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Void execute() throws Throwable - { - DispositionAction nextDispositionAction = dispositionService.getNextDispositionAction(testFolder); - StringBuilder buf = new StringBuilder(); - for (RecordsManagementEvent e : nextDispositionAction.getDispositionActionDefinition().getEvents()) { - buf.append(e.getName()).append(','); - } - - System.out.println("Going to change the RMEs again."); - System.out.println(" - Original value: " + buf.toString()); - - List dads = dispositionService.getDispositionSchedule(testFolder).getDispositionActionDefinitions(); - DispositionActionDefinition firstDAD = dads.get(0); - assertEquals("cutoff", firstDAD.getName()); - NodeRef dadNode = firstDAD.getNodeRef(); - - List eventNames= new ArrayList(); - eventNames.add("study_complete"); - eventNames.add("case_complete"); - nodeService.setProperty(dadNode, PROP_DISPOSITION_EVENT, (Serializable)eventNames); - - return null; - } - }); - - // View the record metadata to verify that the record has been rescheduled. - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Void execute() throws Throwable - { - DispositionAction nextDispositionAction = dispositionService.getNextDispositionAction(testFolder); - - assertEquals("cutoff", nextDispositionAction.getName()); - StringBuilder buf = new StringBuilder(); - for (RecordsManagementEvent e : nextDispositionAction.getDispositionActionDefinition().getEvents()) { - buf.append(e.getName()).append(','); - } - System.out.println(" - Updated value: " + buf.toString()); - - assertFalse("Disposition should not be eligible.", nextDispositionAction.isEventsEligible()); - return null; - } - }); - - // Tidy up test nodes. - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Void execute() throws Throwable - { - nodeService.deleteNode(testRecord); - - // Change the disposition Period back to what it was. - List dads = dispositionService.getDispositionSchedule(testFolder).getDispositionActionDefinitions(); - DispositionActionDefinition firstDAD = dads.get(0); - assertEquals("cutoff", firstDAD.getName()); - NodeRef dadNode = firstDAD.getNodeRef(); - nodeService.setProperty(dadNode, PROP_DISPOSITION_PERIOD, new Period("month|1")); - - nodeService.deleteNode(testFolder); - - return null; - } - }); - } - - private void refreshDispositionActionDefinition(NodeRef nodeRef, List updatedProps) - { - if (updatedProps != null) - { - Map params = new HashMap(); - params.put(BroadcastDispositionActionDefinitionUpdateAction.CHANGED_PROPERTIES, (Serializable)updatedProps); - rmActionService.executeRecordsManagementAction(nodeRef, BroadcastDispositionActionDefinitionUpdateAction.NAME, params); - } - - // Remove the unpublished update aspect - nodeService.removeAspect(nodeRef, ASPECT_UNPUBLISHED_UPDATE); - } - - public void testGetDispositionInstructions() throws Exception - { - setComplete(); - endTransaction(); - - // Get a record - // TODO - - // Get a record folder - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Void execute() throws Throwable - { - NodeRef folderRecord = TestUtilities.getRecordFolder(rmService, nodeService, "Reports", "AIS Audit Records", "January AIS Audit Records"); - assertNotNull(folderRecord); - assertEquals("January AIS Audit Records", nodeService.getProperty(folderRecord, ContentModel.PROP_NAME)); - - assertFalse(rmService.isRecord(folderRecord)); - assertTrue(rmService.isRecordFolder(folderRecord)); - assertFalse(rmService.isRecordCategory(folderRecord)); - - DispositionSchedule di = dispositionService.getDispositionSchedule(folderRecord); - assertNotNull(di); - assertEquals("N1-218-00-4 item 023", di.getDispositionAuthority()); - assertEquals("Cut off monthly, hold 1 month, then destroy.", di.getDispositionInstructions()); - assertFalse(di.isRecordLevelDisposition()); - - // Get a record category - NodeRef recordCategory = TestUtilities.getRecordCategory(rmService, nodeService, "Reports", "AIS Audit Records"); - assertNotNull(recordCategory); - assertEquals("AIS Audit Records", nodeService.getProperty(recordCategory, ContentModel.PROP_NAME)); - - assertFalse(rmService.isRecord(recordCategory)); - assertFalse(rmService.isRecordFolder(recordCategory)); - assertTrue(rmService.isRecordCategory(recordCategory)); - - di = dispositionService.getDispositionSchedule(recordCategory); - assertNotNull(di); - assertEquals("N1-218-00-4 item 023", di.getDispositionAuthority()); - assertEquals("Cut off monthly, hold 1 month, then destroy.", di.getDispositionInstructions()); - assertFalse(di.isRecordLevelDisposition()); - - List das = di.getDispositionActionDefinitions(); - assertNotNull(das); - assertEquals(2, das.size()); - assertEquals("cutoff", das.get(0).getName()); - assertEquals("destroy", das.get(1).getName()); - return null; - } - }); - } - - public void testMoveRecordWithinFileplan() - { - setComplete(); - endTransaction(); - - // We need record folders for test-filing as follows: - // 1. A 'clean' record folder with no disposition schedult and no review period. - // 2. A 'vital' record folder which has a review period defined. - // 3. A 'dispositionable' record folder which has an applicable disposition schedule. - // - // The example fileplan includes a folder which covers [2] and [3] together. - - final NodeRef cleanRecordFolder = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - NodeRef result = TestUtilities.getRecordFolder(rmService, nodeService, "Civilian Files", "Case Files and Papers", "Gilbert Competency Hearing"); - assertNotNull("cleanRecordFolder was null", result); - - final DispositionSchedule dispositionSchedule = dispositionService.getDispositionSchedule(result); - assertNull("cleanRecordFolder had non-null disposition instructions.", dispositionSchedule.getDispositionInstructions()); - assertTrue("cleanRecordFolder had non-empty disposition instruction definitions.", dispositionSchedule.getDispositionActionDefinitions().isEmpty()); - - final VitalRecordDefinition vitalRecordDefinition = vitalRecordService.getVitalRecordDefinition(result); - assertEquals("cleanRecordFolder had wrong review period.", "0", vitalRecordDefinition.getReviewPeriod().getExpression()); - assertNull("cleanRecordFolder had non-null review date.", vitalRecordDefinition.getNextReviewDate()); - return result; - } - }); - final NodeRef dispAndVitalRecordFolder = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - NodeRef result = TestUtilities.getRecordFolder(rmService, nodeService, "Reports", "AIS Audit Records", "January AIS Audit Records"); - assertNotNull("dispositionAndVitalRecordFolder was null", result); - - final DispositionSchedule dispositionSchedule = dispositionService.getDispositionSchedule(result); - assertNotNull("dispositionAndVitalRecordFolder had null disposition instructions.", dispositionSchedule.getDispositionInstructions()); - assertFalse("dispositionAndVitalRecordFolder had empty disposition instruction definitions.", dispositionSchedule.getDispositionActionDefinitions().isEmpty()); - - final VitalRecordDefinition vitalRecordDefinition = vitalRecordService.getVitalRecordDefinition(result); - assertFalse("dispositionAndVitalRecordFolder had wrong review period.", "none|0".equals(vitalRecordDefinition.getReviewPeriod().getExpression())); - assertNotNull("dispositionAndVitalRecordFolder had null review date.", vitalRecordDefinition.getNextReviewDate()); - return result; - } - }); - - // Create a record in the 'clean' folder. - final NodeRef testRecord = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - final NodeRef result = nodeService.createNode(cleanRecordFolder, ContentModel.ASSOC_CONTAINS, - QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, - "Record" + System.currentTimeMillis() + ".txt"), - ContentModel.TYPE_CONTENT).getChildRef(); - - rmActionService.executeRecordsManagementAction(result, "file"); - TestUtilities.declareRecord(result, unprotectedNodeService, rmActionService); - return result; - } - }); - - // Ensure it's devoid of all disposition and review-related state. - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Void execute() throws Throwable - { - final DispositionSchedule dispositionSchedule = dispositionService.getDispositionSchedule(testRecord); - assertNull("testRecord had non-null disposition instructions.", dispositionSchedule.getDispositionInstructions()); - assertTrue("testRecord had non-empty disposition instruction definitions.", dispositionSchedule.getDispositionActionDefinitions().isEmpty()); - - final VitalRecordDefinition vitalRecordDefinition = vitalRecordService.getVitalRecordDefinition(testRecord); - assertEquals("testRecord had wrong review period.", "0", vitalRecordDefinition.getReviewPeriod().getExpression()); - assertNull("testRecord had non-null review date.", vitalRecordDefinition.getNextReviewDate()); - return null; - } - }); - - // Move from non-vital to vital - also non-dispositionable to dispositionable at the same time. - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Void execute() throws Throwable - { - nodeService.moveNode(testRecord, dispAndVitalRecordFolder, ContentModel.ASSOC_CONTAINS, ContentModel.ASSOC_CONTAINS); - return null; - } - }); - - // Assert that the disposition and review-related data are correct after the move. - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Void execute() throws Throwable - { - final DispositionSchedule dispositionSchedule = dispositionService.getDispositionSchedule(testRecord); - assertNotNull("testRecord had null disposition instructions.", dispositionSchedule.getDispositionInstructions()); - assertFalse("testRecord had empty disposition instruction definitions.", dispositionSchedule.getDispositionActionDefinitions().isEmpty()); - - final VitalRecordDefinition vitalRecordDefinition = vitalRecordService.getVitalRecordDefinition(testRecord); - assertFalse("testRecord had wrong review period.", "0".equals(vitalRecordDefinition.getReviewPeriod().getExpression())); - assertNotNull("testRecord had null review date.", vitalRecordDefinition.getNextReviewDate()); - return null; - } - }); - - // Move the test record back from vital to non-vital - also dispositionable to non-dispositionable at the same time. - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Void execute() throws Throwable - { - nodeService.moveNode(testRecord, cleanRecordFolder, ContentModel.ASSOC_CONTAINS, ContentModel.ASSOC_CONTAINS); - return null; - } - }); - - // Assert that the disposition and review-related data are correct after the move. - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Void execute() throws Throwable - { - final DispositionSchedule dispositionSchedule = dispositionService.getDispositionSchedule(testRecord); - assertNull("testRecord had non-null disposition instructions.", dispositionSchedule.getDispositionInstructions()); - assertTrue("testRecord had non-empty disposition instruction definitions.", dispositionSchedule.getDispositionActionDefinitions().isEmpty()); - - final VitalRecordDefinition vitalRecordDefinition = vitalRecordService.getVitalRecordDefinition(testRecord); - assertEquals("testRecord had wrong review period.", "0", vitalRecordDefinition.getReviewPeriod().getExpression()); - assertNull("testRecord had non-null review date.", vitalRecordDefinition.getNextReviewDate()); - return null; - } - }); - - //TODO check the search aspect - - // Tidy up. - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Void execute() throws Throwable - { - nodeService.deleteNode(testRecord); - - return null; - } - }); - } - - public void testCopyRecordWithinFileplan() - { - setComplete(); - endTransaction(); - - // We need record folders for test-filing as follows: - // 1. A 'clean' record folder with no disposition schedule and no review period. - // 2. A 'vital' record folder which has a review period defined. - // 3. A 'dispositionable' record folder which has an applicable disposition schedule. - // - // The example fileplan includes a folder which covers [2] and [3] together. - - final NodeRef cleanRecordFolder = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - NodeRef result = TestUtilities.getRecordFolder(rmService, nodeService, "Civilian Files", "Case Files and Papers", "Gilbert Competency Hearing"); - assertNotNull("cleanRecordFolder was null", result); - - final DispositionSchedule dispositionSchedule = dispositionService.getDispositionSchedule(result); - assertNull("cleanRecordFolder had non-null disposition instructions.", dispositionSchedule.getDispositionInstructions()); - assertTrue("cleanRecordFolder had non-empty disposition instruction definitions.", dispositionSchedule.getDispositionActionDefinitions().isEmpty()); - - final VitalRecordDefinition vitalRecordDefinition = vitalRecordService.getVitalRecordDefinition(result); - assertEquals("cleanRecordFolder had wrong review period.", "0", vitalRecordDefinition.getReviewPeriod().getExpression()); - assertNull("cleanRecordFolder had non-null review date.", vitalRecordDefinition.getNextReviewDate()); - return result; - } - }); - final NodeRef dispAndVitalRecordFolder = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - NodeRef result = TestUtilities.getRecordFolder(rmService, nodeService, "Reports", "AIS Audit Records", "January AIS Audit Records"); - assertNotNull("dispositionAndVitalRecordFolder was null", result); - - final DispositionSchedule dispositionSchedule = dispositionService.getDispositionSchedule(result); - assertNotNull("dispositionAndVitalRecordFolder had null disposition instructions.", dispositionSchedule.getDispositionInstructions()); - assertFalse("dispositionAndVitalRecordFolder had empty disposition instruction definitions.", dispositionSchedule.getDispositionActionDefinitions().isEmpty()); - - final VitalRecordDefinition vitalRecordDefinition = vitalRecordService.getVitalRecordDefinition(result); - assertFalse("dispositionAndVitalRecordFolder had wrong review period.", "none|0".equals(vitalRecordDefinition.getReviewPeriod().getExpression())); - assertNotNull("dispositionAndVitalRecordFolder had null review date.", vitalRecordDefinition.getNextReviewDate()); - return result; - } - }); - - // Create a record in the 'clean' folder. - final NodeRef testRecord = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - final NodeRef result = nodeService.createNode(cleanRecordFolder, ContentModel.ASSOC_CONTAINS, - QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, - "Record" + System.currentTimeMillis() + ".txt"), - ContentModel.TYPE_CONTENT).getChildRef(); - - rmActionService.executeRecordsManagementAction(result, "file"); - TestUtilities.declareRecord(result, unprotectedNodeService, rmActionService); - return result; - } - }); - - // Ensure it's devoid of all disposition and review-related state. - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Void execute() throws Throwable - { - final DispositionSchedule dispositionSchedule = dispositionService.getDispositionSchedule(testRecord); - assertNull("testRecord had non-null disposition instructions.", dispositionSchedule.getDispositionInstructions()); - assertTrue("testRecord had non-empty disposition instruction definitions.", dispositionSchedule.getDispositionActionDefinitions().isEmpty()); - - final VitalRecordDefinition vitalRecordDefinition = vitalRecordService.getVitalRecordDefinition(testRecord); - assertEquals("testRecord had wrong review period.", "0", vitalRecordDefinition.getReviewPeriod().getExpression()); - assertNull("testRecord had non-null review date.", vitalRecordDefinition.getNextReviewDate()); - return null; - } - }); - - // Copy from non-vital to vital - also non-dispositionable to dispositionable at the same time. - final NodeRef copiedNode = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - FileInfo fileInfo = fileFolderService.copy(testRecord, dispAndVitalRecordFolder, null); - NodeRef n = fileInfo.getNodeRef(); - return n; - } - }); - - // Assert that the disposition and review-related data are correct after the copy. - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Void execute() throws Throwable - { - final DispositionSchedule dispositionSchedule = dispositionService.getDispositionSchedule(copiedNode); - assertNotNull("copiedNode had null disposition instructions.", dispositionSchedule.getDispositionInstructions()); - assertFalse("copiedNode had empty disposition instruction definitions.", dispositionSchedule.getDispositionActionDefinitions().isEmpty()); - - final VitalRecordDefinition vitalRecordDefinition = vitalRecordService.getVitalRecordDefinition(copiedNode); - assertFalse("copiedNode had wrong review period.", "0".equals(vitalRecordDefinition.getReviewPeriod().getExpression())); - assertNotNull("copiedNode had null review date.", vitalRecordDefinition.getNextReviewDate()); - return null; - } - }); - - // Create a record in the 'vital and disposition' folder. - final NodeRef testRecord2 = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - final NodeRef result = nodeService.createNode(dispAndVitalRecordFolder, ContentModel.ASSOC_CONTAINS, - QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, - "Record2" + System.currentTimeMillis() + ".txt"), - ContentModel.TYPE_CONTENT).getChildRef(); - - rmActionService.executeRecordsManagementAction(result, "file"); - TestUtilities.declareRecord(result, unprotectedNodeService, rmActionService); - return result; - } - }); - - // Check the vital and disposition status. - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Void execute() throws Throwable - { - final DispositionSchedule dispositionSchedule = dispositionService.getDispositionSchedule(testRecord2); - assertNotNull("testRecord2 had null disposition instructions.", dispositionSchedule.getDispositionInstructions()); - assertFalse("testRecord2 had empty disposition instruction definitions.", dispositionSchedule.getDispositionActionDefinitions().isEmpty()); - - final VitalRecordDefinition vitalRecordDefinition = vitalRecordService.getVitalRecordDefinition(testRecord2); - assertFalse("testRecord2 had wrong review period.", "0".equals(vitalRecordDefinition.getReviewPeriod().getExpression())); - assertNotNull("testRecord2 had null review date.", vitalRecordDefinition.getNextReviewDate()); - return null; - } - }); - - // copy the record back from vital to non-vital - also dispositionable to non-dispositionable at the same time. - final NodeRef copiedBackNode = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public NodeRef execute() throws Throwable - { - FileInfo fileInfo = fileFolderService.copy(testRecord2, cleanRecordFolder, null); // TODO Something wrong here. - NodeRef n = fileInfo.getNodeRef(); - return n; - } - }); - - // Assert that the disposition and review-related data are correct after the copy-back. - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Void execute() throws Throwable - { - final DispositionSchedule dispositionSchedule = dispositionService.getDispositionSchedule(copiedBackNode); - assertNull("copiedBackNode had non-null disposition instructions.", dispositionSchedule.getDispositionInstructions()); - assertTrue("copiedBackNode had non-empty disposition instruction definitions.", dispositionSchedule.getDispositionActionDefinitions().isEmpty()); - - final VitalRecordDefinition vitalRecordDefinition = vitalRecordService.getVitalRecordDefinition(copiedBackNode); - assertEquals("copiedBackNode had wrong review period.", "0", vitalRecordDefinition.getReviewPeriod().getExpression()); - assertNull("copiedBackNode had non-null review date.", vitalRecordDefinition.getNextReviewDate()); - return null; - } - }); - - //TODO check the search aspect - - // Tidy up. - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Void execute() throws Throwable - { - nodeService.deleteNode(copiedBackNode); - nodeService.deleteNode(testRecord2); - nodeService.deleteNode(copiedNode); - nodeService.deleteNode(testRecord); - - return null; - } - }); - } - - public void xxxtestUpdateNextDispositionAction() - { - setComplete(); - endTransaction(); - - final FileAction fileAction = (FileAction)applicationContext.getBean("file"); - - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Void execute() throws Throwable - { - // Get a record folder - NodeRef folderRecord = TestUtilities.getRecordFolder(rmService, nodeService, "Reports", "AIS Audit Records", "January AIS Audit Records"); - assertNotNull(folderRecord); - assertEquals("January AIS Audit Records", nodeService.getProperty(folderRecord, ContentModel.PROP_NAME)); - - DispositionSchedule di = dispositionService.getDispositionSchedule(folderRecord); - assertNotNull(di); - assertEquals("N1-218-00-4 item 023", di.getDispositionAuthority()); - assertEquals("Cut off monthly, hold 1 month, then destroy.", di.getDispositionInstructions()); - assertFalse(di.isRecordLevelDisposition()); - - assertFalse(nodeService.hasAspect(folderRecord, ASPECT_DISPOSITION_LIFECYCLE)); - - fileAction.updateNextDispositionAction(folderRecord); - - - // Check the next disposition action - assertTrue(nodeService.hasAspect(folderRecord, ASPECT_DISPOSITION_LIFECYCLE)); - NodeRef ndNodeRef = nodeService.getChildAssocs(folderRecord, ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); - assertNotNull(ndNodeRef); - assertEquals("cutoff", nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_ACTION)); - assertEquals(di.getDispositionActionDefinitions().get(0).getId(), nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_ACTION_ID)); - assertNotNull(nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF)); - - // Check the history is empty - // TODO - - Map props = new HashMap(1); - props.put(PROP_CUT_OFF_DATE, new Date()); - unprotectedNodeService.addAspect(folderRecord, ASPECT_CUT_OFF, props); - fileAction.updateNextDispositionAction(folderRecord); - - assertTrue(nodeService.hasAspect(folderRecord, ASPECT_DISPOSITION_LIFECYCLE)); - ndNodeRef = nodeService.getChildAssocs(folderRecord, ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).get(0).getChildRef(); - assertNotNull(ndNodeRef); - assertEquals("destroy", nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_ACTION)); - assertEquals(di.getDispositionActionDefinitions().get(1).getId(), nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_ACTION_ID)); - assertNotNull(nodeService.getProperty(ndNodeRef, PROP_DISPOSITION_AS_OF)); - - // Check the history has an action - // TODO - - fileAction.updateNextDispositionAction(folderRecord); - - assertTrue(nodeService.hasAspect(folderRecord, ASPECT_DISPOSITION_LIFECYCLE)); - assertTrue(nodeService.getChildAssocs(folderRecord, ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL).isEmpty()); - - // Check the history has both actions - // TODO - return null; - } - }); - } -} diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseRMTestCase.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseRMTestCase.java index c5536ea33d..78edd70461 100644 --- a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseRMTestCase.java +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseRMTestCase.java @@ -31,7 +31,8 @@ import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedul import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService; import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEventService; import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; -import org.alfresco.module.org_alfresco_module_rm.model.RmSiteType; +import org.alfresco.module.org_alfresco_module_rm.model.behaviour.RmSiteType; +import org.alfresco.module.org_alfresco_module_rm.record.RecordService; import org.alfresco.module.org_alfresco_module_rm.search.RecordsManagementSearchService; import org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService; import org.alfresco.module.org_alfresco_module_rm.vital.VitalRecordService; @@ -40,6 +41,7 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.model.FileFolderService; import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; @@ -101,6 +103,7 @@ public abstract class BaseRMTestCase extends RetryingTransactionHelperTestCase protected AuthorityService authorityService; protected PersonService personService; protected TransactionService transactionService; + protected FileFolderService fileFolderService; /** RM Services */ protected RecordsManagementService rmService; @@ -112,6 +115,7 @@ public abstract class BaseRMTestCase extends RetryingTransactionHelperTestCase protected RecordsManagementSecurityService securityService; protected CapabilityService capabilityService; protected VitalRecordService vitalRecordService; + protected RecordService recordService; /** test data */ protected StoreRef storeRef; @@ -255,6 +259,7 @@ public abstract class BaseRMTestCase extends RetryingTransactionHelperTestCase authenticationService = (MutableAuthenticationService)this.applicationContext.getBean("AuthenticationService"); personService = (PersonService)this.applicationContext.getBean("PersonService"); transactionService = (TransactionService)applicationContext.getBean("TransactionService"); + fileFolderService = (FileFolderService)applicationContext.getBean("FileFolderService"); // Get RM services rmService = (RecordsManagementService)applicationContext.getBean("RecordsManagementService"); @@ -266,6 +271,7 @@ public abstract class BaseRMTestCase extends RetryingTransactionHelperTestCase securityService = (RecordsManagementSecurityService)this.applicationContext.getBean("RecordsManagementSecurityService"); capabilityService = (CapabilityService)this.applicationContext.getBean("CapabilityService"); vitalRecordService = (VitalRecordService)this.applicationContext.getBean("VitalRecordService"); + recordService = (RecordService)this.applicationContext.getBean("RecordService"); } /** @@ -316,16 +322,24 @@ public abstract class BaseRMTestCase extends RetryingTransactionHelperTestCase */ protected void setupTestData() { - retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback() + doTestInTransaction(new Test() { - @Override - public Object execute() throws Throwable + public Void run() { - AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); - setupTestDataImpl(); - return null; + setupTestDataImpl(); + return null; } - }); + + // check that the new records container has been created for the file plan + public void test(Void arg0) throws Exception + { + NodeRef newRecordsContainer = recordService.getNewRecordContainer(filePlan); + assertNotNull(newRecordsContainer); + assertEquals(TYPE_NEW_RECORDS_CONTAINER, nodeService.getType(newRecordsContainer)); + + }; + }, + AuthenticationUtil.getSystemUserName()); } /** diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseRMWebScriptTestCase.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseRMWebScriptTestCase.java index f45947b6f1..50bcd6f5ba 100644 --- a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseRMWebScriptTestCase.java +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseRMWebScriptTestCase.java @@ -1,5 +1,20 @@ -/** - * +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . */ package org.alfresco.module.org_alfresco_module_rm.test.util; @@ -17,7 +32,7 @@ import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedul import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService; import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEventService; import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; -import org.alfresco.module.org_alfresco_module_rm.model.RmSiteType; +import org.alfresco.module.org_alfresco_module_rm.model.behaviour.RmSiteType; import org.alfresco.module.org_alfresco_module_rm.search.RecordsManagementSearchService; import org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService; import org.alfresco.module.org_alfresco_module_rm.vital.VitalRecordService; diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/TestUtilities.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/TestUtilities.java index 214e03d4a9..b14ddb239e 100644 --- a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/TestUtilities.java +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/TestUtilities.java @@ -35,7 +35,7 @@ import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionService; import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService; import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; -import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementSearchBehaviour; +import org.alfresco.module.org_alfresco_module_rm.model.behaviour.RecordsManagementSearchBehaviour; import org.alfresco.module.org_alfresco_module_rm.script.BootstrapTestDataGet; import org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService; import org.alfresco.service.cmr.repository.ChildAssociationRef; From 58bb845f421f8e86f3eb9a6d4920cac73ec05968 Mon Sep 17 00:00:00 2001 From: Roy Wetherall Date: Tue, 18 Sep 2012 08:16:53 +0000 Subject: [PATCH 3/9] RM: In-place filing prototype WIP * dynamic authority set ReadRecord permission on new record and ViewRecord capability on containing file plan * dynamic 'Record Readers' authority working correctly (checking whether current user is contained in the users/groups snap shoted as readers when the document was made a record) * unit test showing document in collab site being made a record and the existing collab user having record read permissions without being added to the RM site or given an RM role. * this check-in is sufficient to demonstrate that collaboration users can create records and view them without having to introduce any form of noticable mapping between the RM and Collab sites. (relates to RM-485) git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/DEV/INPLACE@41708 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../rm-service-context.xml | 2 - .../action/dm/CreateRecordAction.java | 19 +- .../RecordReadersDynamicAuthority.java | 44 +++-- .../record/RecordService.java | 4 +- .../record/RecordServiceImpl.java | 113 ++++++------ .../test/service/RecordServiceTestImpl.java | 166 ++++++++++++++++++ .../RecordsManagementServiceImplTest.java | 41 ----- .../test/util/BaseRMTestCase.java | 6 +- 8 files changed, 284 insertions(+), 111 deletions(-) create mode 100644 rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/RecordServiceTestImpl.java diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml index 4a921877a2..937a12005d 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml @@ -839,8 +839,6 @@ diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/dm/CreateRecordAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/dm/CreateRecordAction.java index be3d6570f9..6da56869ab 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/dm/CreateRecordAction.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/dm/CreateRecordAction.java @@ -41,6 +41,7 @@ import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.RegexQNamePattern; /** * Creates a new record from an existing content object. @@ -52,6 +53,8 @@ import org.alfresco.service.namespace.QName; public class CreateRecordAction extends ActionExecuterAbstractBase implements RecordsManagementModel { + public static final String NAME = "create-record"; + private RecordsManagementService recordsManagementService; private RecordService recordService; @@ -103,7 +106,7 @@ public class CreateRecordAction extends ActionExecuterAbstractBase ChildAssociationRef parentAssoc = nodeService.getPrimaryParent(actionedUponNodeRef); /// get the new record container for the file plan - NodeRef newRecordContainer = recordService.getNewRecordContainer(filePlan); + NodeRef newRecordContainer = getNewRecordContainer(filePlan); if (newRecordContainer == null) { throw new AlfrescoRuntimeException("Unable to create record, because new record container could not be found."); @@ -127,6 +130,10 @@ public class CreateRecordAction extends ActionExecuterAbstractBase RecordReadersDynamicAuthority.RECORD_READERS, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(filePlan, + RecordReadersDynamicAuthority.RECORD_READERS, + RMPermissionModel.VIEW_RECORDS, + true); return null; } @@ -137,6 +144,16 @@ public class CreateRecordAction extends ActionExecuterAbstractBase throw new AlfrescoRuntimeException("Unable to file file plan."); } } + + private NodeRef getNewRecordContainer(NodeRef filePlan) + { + List assocs = nodeService.getChildAssocs(filePlan, ASSOC_NEW_RECORDS, RegexQNamePattern.MATCH_ALL); + if (assocs.size() != 1) + { + throw new AlfrescoRuntimeException("Error getting the new record container, because the container cannot be indentified."); + } + return assocs.get(0).getChildRef(); + } @Override protected void addParameterDefinitions(List params) diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/permission/RecordReadersDynamicAuthority.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/permission/RecordReadersDynamicAuthority.java index d8d63a64e6..0b3452e7bf 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/permission/RecordReadersDynamicAuthority.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/permission/RecordReadersDynamicAuthority.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Alfresco Software Limited. +` * Copyright (C) 2005-2012 Alfresco Software Limited. * * This file is part of Alfresco * @@ -18,6 +18,7 @@ */ package org.alfresco.module.org_alfresco_module_rm.permission; +import java.util.List; import java.util.Set; import org.alfresco.module.org_alfresco_module_rm.FilePlanComponentKind; @@ -28,6 +29,7 @@ import org.alfresco.repo.security.permissions.PermissionReference; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.security.AuthorityService; +import org.alfresco.service.cmr.security.AuthorityType; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; @@ -104,17 +106,39 @@ public class RecordReadersDynamicAuthority implements DynamicAuthority, RecordsM { if (getNodeService().hasAspect(nodeRef, ASPECT_EXTENDED_RECORD_SECURITY) == true) { - result = true; - - System.out.println("Setting hasAuthority to true! - " + userName + " - " + nodeRef.toString()); - -// Set readers = (Set)nodeService.getProperty(nodeRef, PROP_READERS); -// for (String reader : readers) -// { -// // check to see if the user is one of the readers or is contained there within -// } + List readers = (List)nodeService.getProperty(nodeRef, PROP_READERS); + for (String reader : readers) + { + if (reader.startsWith("GROUP_") == true) + { + Set contained = getAuthorityService().getContainedAuthorities(AuthorityType.USER, reader, false); + if (contained.isEmpty() == false && + contained.contains(userName) == true) + { + System.out.println("User " + userName + " is contained in the read group " + reader); + + result = true; + break; + } + } + else + { + // presume we have a user + if (reader.equals(userName) == true) + { + System.out.println("User " + userName + " matches read user " + reader); + + result = true; + break; + } + } + } } } + else if (FilePlanComponentKind.FILE_PLAN.equals(kind) == true) + { + result = true; + } return result; } diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordService.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordService.java index e311a3071e..90746b8511 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordService.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordService.java @@ -49,9 +49,9 @@ public interface RecordService // TODO boolean isRecordFiled(NodeRef record); // TODO boolean isRecordClassified(NodeRef record); - NodeRef getNewRecordContainer(NodeRef filePlan); + // NodeRef getNewRecordContainer(NodeRef filePlan); - NodeRef createRecord(NodeRef filePlan, NodeRef document); + //NodeRef createRecord(NodeRef filePlan, NodeRef document); // TODO NodeRef createAndFileRecord(NodeRef recordFolder, NodeRef document); diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImpl.java index 4f0425d8a2..839611cf91 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImpl.java @@ -20,7 +20,6 @@ package org.alfresco.module.org_alfresco_module_rm.record; import java.util.Collection; import java.util.HashSet; -import java.util.List; import java.util.Set; import org.alfresco.error.AlfrescoRuntimeException; @@ -31,6 +30,8 @@ import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; import org.alfresco.repo.policy.JavaBehaviour; import org.alfresco.repo.policy.PolicyComponent; import org.alfresco.repo.policy.Behaviour.NotificationFrequency; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.service.cmr.dictionary.AspectDefinition; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.repository.ChildAssociationRef; @@ -38,7 +39,6 @@ import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; -import org.alfresco.service.namespace.RegexQNamePattern; /** * @author Roy Wetherall @@ -93,22 +93,31 @@ public class RecordServiceImpl implements RecordService, RecordsManagementModel new JavaBehaviour(this, "onCreateNewRecord", NotificationFrequency.TRANSACTION_COMMIT)); } - public void onCreateNewRecord(ChildAssociationRef childAssocRef, boolean bNew) + public void onCreateNewRecord(final ChildAssociationRef childAssocRef, boolean bNew) { - NodeRef nodeRef = childAssocRef.getChildRef(); - if (nodeService.exists(nodeRef) == true) + AuthenticationUtil.runAsSystem(new RunAsWork() { - QName type = nodeService.getType(nodeRef); - if (ContentModel.TYPE_CONTENT.equals(type) == true || - dictionaryService.isSubClass(type, ContentModel.TYPE_CONTENT) == true) + @Override + public Void doWork() throws Exception { - makeRecord(nodeRef); - } - else - { - throw new AlfrescoRuntimeException("Only content can be created as a record."); - } - } + NodeRef nodeRef = childAssocRef.getChildRef(); + if (nodeService.exists(nodeRef) == true) + { + QName type = nodeService.getType(nodeRef); + if (ContentModel.TYPE_CONTENT.equals(type) == true || + dictionaryService.isSubClass(type, ContentModel.TYPE_CONTENT) == true) + { + makeRecord(nodeRef); + } + else + { + throw new AlfrescoRuntimeException("Only content can be created as a record."); + } + } + + return null; + } + }); } /** @@ -147,44 +156,44 @@ public class RecordServiceImpl implements RecordService, RecordsManagementModel /** * @see org.alfresco.module.org_alfresco_module_rm.record.RecordService#getNewRecordContainer(org.alfresco.service.cmr.repository.NodeRef) */ - public NodeRef getNewRecordContainer(NodeRef filePlan) - { - NodeRef result = null; - - if (recordsManagementService.isFilePlan(filePlan) == true) - { - List assocs = nodeService.getChildAssocs(filePlan, ASSOC_NEW_RECORDS, RegexQNamePattern.MATCH_ALL); - if (assocs.size() != 1) - { - throw new AlfrescoRuntimeException("Error getting the new record container, because the container cannot be indentified."); - } - result = assocs.get(0).getChildRef(); - } - - return result; - } +// public NodeRef getNewRecordContainer(NodeRef filePlan) +// { +// NodeRef result = null; +// +// if (recordsManagementService.isFilePlan(filePlan) == true) +// { +// List assocs = nodeService.getChildAssocs(filePlan, ASSOC_NEW_RECORDS, RegexQNamePattern.MATCH_ALL); +// if (assocs.size() != 1) +// { +// throw new AlfrescoRuntimeException("Error getting the new record container, because the container cannot be indentified."); +// } +// result = assocs.get(0).getChildRef(); +// } +// +// return result; +// } - @Override - public NodeRef createRecord(NodeRef filePlan, NodeRef document) - { - // get the documents primary parent assoc - ChildAssociationRef parentAssoc = nodeService.getPrimaryParent(document); - - /// get the new record container for the file plan - NodeRef newRecordContainer = getNewRecordContainer(filePlan); - if (newRecordContainer == null) - { - throw new AlfrescoRuntimeException("Unable to create record, because new record container could not be found."); - } - - // move the document into the file plan - nodeService.moveNode(document, newRecordContainer, ContentModel.ASSOC_CONTAINS, parentAssoc.getQName()); - - // maintain the original primary location - nodeService.addChild(parentAssoc.getParentRef(), document, parentAssoc.getTypeQName(), parentAssoc.getQName()); - - return document; - } +// @Override +// public NodeRef createRecord(NodeRef filePlan, NodeRef document) +// { +// // get the documents primary parent assoc +// ChildAssociationRef parentAssoc = nodeService.getPrimaryParent(document); +// +// /// get the new record container for the file plan +// NodeRef newRecordContainer = getNewRecordContainer(filePlan); +// if (newRecordContainer == null) +// { +// throw new AlfrescoRuntimeException("Unable to create record, because new record container could not be found."); +// } +// +// // move the document into the file plan +// nodeService.moveNode(document, newRecordContainer, ContentModel.ASSOC_CONTAINS, parentAssoc.getQName()); +// +// // maintain the original primary location +// nodeService.addChild(parentAssoc.getParentRef(), document, parentAssoc.getTypeQName(), parentAssoc.getQName()); +// +// return document; +// } /** * diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/RecordServiceTestImpl.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/RecordServiceTestImpl.java new file mode 100644 index 0000000000..d9cd74d926 --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/RecordServiceTestImpl.java @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.test.service; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.action.dm.CreateRecordAction; +import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel; +import org.alfresco.module.org_alfresco_module_rm.permission.RecordReadersDynamicAuthority; +import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.site.SiteModel; +import org.alfresco.repo.site.SiteServiceImpl; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.action.ActionService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.security.AccessStatus; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.cmr.site.SiteInfo; +import org.alfresco.service.cmr.site.SiteService; +import org.alfresco.service.cmr.site.SiteVisibility; +import org.alfresco.service.cmr.tagging.TaggingService; +import org.alfresco.util.GUID; + +/** + * Record service implementation unit test. + * + * @author Roy Wetherall + */ +public class RecordServiceTestImpl extends BaseRMTestCase +{ + protected static final String COLLABORATION_SITE_ID = "collab-site-id"; + + protected ActionService dmActionService; + protected TaggingService taggingService; + protected PermissionService dmPermissionService; + + protected SiteInfo collaborationSite; + protected NodeRef documentLibrary; + protected NodeRef dmFolder; + protected NodeRef dmDocument; + + protected String dmUserName; + protected NodeRef dmUserPerson; + + @Override + protected void initServices() + { + super.initServices(); + + dmActionService = (ActionService)applicationContext.getBean("ActionService"); + taggingService = (TaggingService)applicationContext.getBean("TaggingService"); + dmPermissionService = (PermissionService)applicationContext.getBean("PermissionService"); + } + + @Override + protected boolean isUserTest() + { + return true; + } + + @Override + protected void setupTestData() + { + super.setupTestData(); + + doTestInTransaction(new Test() + { + public Void run() + { + setupCollaborationSiteTestDataImpl(); + return null; + } + }, + AuthenticationUtil.getSystemUserName()); + } + + protected void setupCollaborationSiteTestDataImpl() + { + // create collaboration site + collaborationSite = siteService.createSite("preset", COLLABORATION_SITE_ID, "title", "description", SiteVisibility.PRIVATE); + documentLibrary = SiteServiceImpl.getSiteContainer( + COLLABORATION_SITE_ID, + SiteService.DOCUMENT_LIBRARY, + true, + siteService, + transactionService, + taggingService); + + assertNotNull("Collaboration site document library component was not successfully created.", documentLibrary); + + // create a folder and documents + dmFolder = fileFolderService.create(documentLibrary, "collabFolder", ContentModel.TYPE_FOLDER).getNodeRef(); + dmDocument = fileFolderService.create(dmFolder, "collabDocument.txt", ContentModel.TYPE_CONTENT).getNodeRef(); + } + + @Override + protected void setupTestUsersImpl(NodeRef filePlan) + { + super.setupTestUsersImpl(filePlan); + + dmUserName = GUID.generate(); + dmUserPerson = createPerson(dmUserName); + siteService.setMembership(COLLABORATION_SITE_ID, dmUserName, SiteModel.SITE_COLLABORATOR); + } + + @Override + protected void tearDownImpl() + { + super.tearDownImpl(); + siteService.deleteSite(COLLABORATION_SITE_ID); + } + + public void testCreateRecordAction() + { + doTestInTransaction(new Test() + { + public Void run() + { + //assertFalse(rmService.isRecord(dmDocument)); + + assertEquals(AccessStatus.DENIED, dmPermissionService.hasPermission(dmDocument, RMPermissionModel.READ_RECORDS)); + assertEquals(AccessStatus.DENIED, dmPermissionService.hasPermission(filePlan, RMPermissionModel.VIEW_RECORDS)); + + Action action = dmActionService.createAction(CreateRecordAction.NAME); + dmActionService.executeAction(action, dmDocument); + + return null; + } + + public void test(Void result) throws Exception + { + assertEquals(AccessStatus.ALLOWED, dmPermissionService.hasPermission(dmDocument, RMPermissionModel.READ_RECORDS)); + assertEquals(AccessStatus.ALLOWED, dmPermissionService.hasPermission(filePlan, RMPermissionModel.VIEW_RECORDS)); + + assertTrue(rmService.isRecord(dmDocument)); + }; + }, + dmUserName); + + doTestInTransaction(new Test() + { + public Void run() + { + + return null; + } + }); + } + +} diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/RecordsManagementServiceImplTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/RecordsManagementServiceImplTest.java index fad5ec9ac2..c342b1011f 100644 --- a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/RecordsManagementServiceImplTest.java +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/RecordsManagementServiceImplTest.java @@ -21,7 +21,6 @@ package org.alfresco.module.org_alfresco_module_rm.test.service; import java.util.List; import java.util.Set; -import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.module.org_alfresco_module_rm.FilePlanComponentKind; import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; import org.alfresco.module.org_alfresco_module_rm.dod5015.DOD5015Model; @@ -612,46 +611,6 @@ public class RecordsManagementServiceImplTest extends BaseRMTestCase // TODO void testGetRecordFolders(NodeRef record); - // TODO void testIsRecordDeclared(NodeRef nodeRef); - - public void testGetNewRecordsContainer() - { - doTestInTransaction(new Test() - { - @Override - public Void run() - { - NodeRef result1 = recordService.getNewRecordContainer(filePlan); - assertNotNull(result1); - assertEquals(TYPE_NEW_RECORDS_CONTAINER, nodeService.getType(result1)); - - assertNull(recordService.getNewRecordContainer(rmContainer)); - assertNull(recordService.getNewRecordContainer(rmFolder)); - - return null; - } - }); - - // Failure: File plan with no new record container - doTestInTransaction(new FailureTest - ( - "The newly created file plan shouldn't yet have a new record container.", - AlfrescoRuntimeException.class - ) - { - @Override - public void run() - { - NodeRef newFilePlan = rmService.createFilePlan(folder, GUID.generate()); - recordService.getNewRecordContainer(newFilePlan); - } - }); - } - - public void testCreateRecord() - { - - } /********** RM2 - Multi-hierarchy record taxonomy's **********/ diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseRMTestCase.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseRMTestCase.java index 78edd70461..8dc01dd7c6 100644 --- a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseRMTestCase.java +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseRMTestCase.java @@ -333,9 +333,9 @@ public abstract class BaseRMTestCase extends RetryingTransactionHelperTestCase // check that the new records container has been created for the file plan public void test(Void arg0) throws Exception { - NodeRef newRecordsContainer = recordService.getNewRecordContainer(filePlan); - assertNotNull(newRecordsContainer); - assertEquals(TYPE_NEW_RECORDS_CONTAINER, nodeService.getType(newRecordsContainer)); + // NodeRef newRecordsContainer = recordService.getNewRecordContainer(filePlan); + // assertNotNull(newRecordsContainer); + // assertEquals(TYPE_NEW_RECORDS_CONTAINER, nodeService.getType(newRecordsContainer)); }; }, From 6b54f8f9f897fa2143d73e6552a493f152fce404 Mon Sep 17 00:00:00 2001 From: Roy Wetherall Date: Thu, 27 Sep 2012 07:12:52 +0000 Subject: [PATCH 4/9] RM: Inplace filing prototype * extension of seciruty service to allow management of extended readers * extended reader maintained within file plan hierarchy * support ready for removal (ie move) and overlapping of readers in hirearchy (maintained in reference counting map) * general rename to "Unfiled Records" rather than "New Records" * File plan unfiled records filter * Unit tests * Correct permissions on created unfiled container (file for admin as per file plan root) * record readers dynamic authority applied to file plan components on bootstrap and creation git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/DEV/INPLACE@42063 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../org_alfresco_module_rm/action-context.xml | 2 +- .../rm-capabilities-group-context.xml | 1 + .../model/recordsModel.xml | 17 +- .../org_alfresco_module_rm/module-context.xml | 4 +- .../rm-public-services-security-context.xml | 1 + .../rm-service-context.xml | 8 +- .../rm-ui-evaluators-context.xml | 2 + .../documentlibrary-v2/rm-filters.lib.js | 7 + .../documentlibrary/rm-treenode.get.js | 2 +- .../RecordsManagementService.java | 8 + .../RecordsManagementServiceImpl.java | 9 + .../action/dm/CreateRecordAction.java | 40 +--- .../capability/RMAfterInvocationProvider.java | 3 + .../capability/RMSecurityCommon.java | 11 +- .../capability/impl/CreateCapability.java | 8 + .../model/RecordsManagementModel.java | 10 +- .../model/behaviour/FilePlanType.java | 35 +-- .../model/behaviour/RmSiteType.java | 65 +++--- .../record/RecordService.java | 1 + .../record/RecordServiceImpl.java | 2 +- .../ExtendedReaderDynamicAuthority.java} | 103 +++++---- .../RecordsManagementSecurityService.java | 27 +++ .../RecordsManagementSecurityServiceImpl.java | 216 ++++++++++++++++-- ...ordsManagementSecurityServiceImplTest.java | 151 ++++++++++++ .../test/service/RecordServiceTestImpl.java | 13 +- ...ordsManagementSecurityServiceImplTest.java | 2 +- .../test/util/BaseRMTestCase.java | 12 +- 27 files changed, 567 insertions(+), 193 deletions(-) rename rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/{permission/RecordReadersDynamicAuthority.java => security/ExtendedReaderDynamicAuthority.java} (61%) create mode 100644 rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/NewRecordsManagementSecurityServiceImplTest.java 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 b308c58434..3179870f95 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 @@ -15,7 +15,7 @@ - + diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/capability/rm-capabilities-group-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/capability/rm-capabilities-group-context.xml index 8b06e20f26..555f89a78d 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/capability/rm-capabilities-group-context.xml +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/capability/rm-capabilities-group-context.xml @@ -6,6 +6,7 @@ + diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/model/recordsModel.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/model/recordsModel.xml index 754d382efd..50d4493ca5 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/model/recordsModel.xml +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/model/recordsModel.xml @@ -90,8 +90,8 @@ - - New Records Container + + Unfiled Record Container rma:recordsManagementContainer @@ -624,14 +624,14 @@ - - New Records + + Unfiled Records true false - rma:newRecordsContainer + rma:unfiledRecordContainer true true @@ -844,13 +844,12 @@ - + - + - d:text - true + d:any diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/module-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/module-context.xml index 139c502bb0..6c663e725c 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/module-context.xml +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/module-context.xml @@ -46,12 +46,12 @@ - + - + + diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml index 937a12005d..d27cff9d0d 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml @@ -67,6 +67,7 @@ org.alfresco.module.org_alfresco_module_rm.RecordsManagementService.isFilePlanComponent=RM.Read.0 org.alfresco.module.org_alfresco_module_rm.RecordsManagementService.getFilePlanComponentKind=ACL_NODE.0.sys:base.ReadProperties, RM.Read.0 org.alfresco.module.org_alfresco_module_rm.RecordsManagementService.getFilePlanComponentKindFromType=RM_ALLOW + org.alfresco.module.org_alfresco_module_rm.RecordsManagementService.isRecordsManagementContainer=RM.Read.0 org.alfresco.module.org_alfresco_module_rm.RecordsManagementService.isFilePlan=RM.Read.0 org.alfresco.module.org_alfresco_module_rm.RecordsManagementService.isRecordCategory=RM.Read.0 org.alfresco.module.org_alfresco_module_rm.RecordsManagementService.isRecordFolder=RM.Read.0 @@ -480,7 +481,12 @@ org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService.createRole=RM_ALLOW org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService.updateRole=RM_ALLOW org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService.deleteRole=RM_ALLOW - org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService.assignRoleToAuthority=RM_ALLOW + org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService.assignRoleToAuthority=RM_ALLOW + org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService.getExtendedReaders=RM_ALLOW + org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService.setExtendedReaders=RM_ALLOW + org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService.removeExtendedReaders=RM_ALLOW + org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService.removeAllExtendedReaders=RM_ALLOW + org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService.*=RM_DENY ]]> diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-ui-evaluators-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-ui-evaluators-context.xml index 72a35ea07a..925fa0cee7 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-ui-evaluators-context.xml +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-ui-evaluators-context.xml @@ -7,6 +7,7 @@ class="org.alfresco.module.org_alfresco_module_rm.jscript.app.JSONConversionComponent" parent="baseJsonConversionComponent"> + @@ -14,6 +15,7 @@ abstract="true"> + diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/rm-filters.lib.js b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/rm-filters.lib.js index 25a0234d47..fa31ed974e 100644 --- a/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/rm-filters.lib.js +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/rm-filters.lib.js @@ -179,6 +179,13 @@ Filters.getFilterParams = function RecordsManagementFilter_getFilterParams(filte filterParams.query = "+PARENT:\"" + filterData + "\""; } break; + + case "unfiledRecords": + filterParams.variablePath = false; + filterQuery = "+PATH:\"" + parsedArgs.pathNode.qnamePath + "/rma:Unfiled_x0020_Records/*\""; + filterParams.query = filterQuery + filterQueryDefaults; + + break; default: filterParams.variablePath = false; diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/rm-treenode.get.js b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/rm-treenode.get.js index 09c27856d2..8dd5e01d78 100644 --- a/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/rm-treenode.get.js +++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/rm-treenode.get.js @@ -120,7 +120,7 @@ function itemIsAllowed(item) var typeShort = String(item.typeShort); // Don't show Hold and Transfer top-level containers - if (typeShort == "rma:hold" || typeShort == "rma:transfer") + if (typeShort == "rma:hold" || typeShort == "rma:transfer" || typeShort == "rma:unfiledRecordContainer") { return false; } diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementService.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementService.java index cf6fe257c5..3d25fb2bfe 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementService.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementService.java @@ -73,6 +73,14 @@ public interface RecordsManagementService */ FilePlanComponentKind getFilePlanComponentKindFromType(QName type); + /** + * Indicates whether the given node is a records management container or not. + * + * @param nodeRef node reference + * @return boolean true if node is a record container, false otherwise. + */ + boolean isRecordsManagementContainer(NodeRef nodeRef); + /** * Indicates whether the given node is file plan node or not. * diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementServiceImpl.java index 6c09f8bea4..eef43031b7 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementServiceImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementServiceImpl.java @@ -436,6 +436,15 @@ public class RecordsManagementServiceImpl implements RecordsManagementService, return instanceOf(nodeRef, TYPE_FILE_PLAN); } + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementService#isRecordsManagementContainer(org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + public boolean isRecordsManagementContainer(NodeRef nodeRef) + { + return instanceOf(nodeRef, TYPE_RECORDS_MANAGEMENT_CONTAINER); + } + /** * Utility method to safely and quickly determine if a node is a type (or sub-type) of the one specified. */ diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/dm/CreateRecordAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/dm/CreateRecordAction.java index 6da56869ab..89f8c020e2 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/dm/CreateRecordAction.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/dm/CreateRecordAction.java @@ -18,19 +18,14 @@ */ package org.alfresco.module.org_alfresco_module_rm.action.dm; -import java.io.Serializable; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Set; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; -import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel; import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; -import org.alfresco.module.org_alfresco_module_rm.permission.RecordReadersDynamicAuthority; -import org.alfresco.module.org_alfresco_module_rm.record.RecordService; +import org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService; import org.alfresco.repo.action.executer.ActionExecuterAbstractBase; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; @@ -40,7 +35,6 @@ import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.security.PermissionService; -import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.RegexQNamePattern; /** @@ -56,8 +50,8 @@ public class CreateRecordAction extends ActionExecuterAbstractBase public static final String NAME = "create-record"; private RecordsManagementService recordsManagementService; - - private RecordService recordService; + + private RecordsManagementSecurityService recordsManagementSecurityService; private PermissionService permissionService; @@ -68,9 +62,9 @@ public class CreateRecordAction extends ActionExecuterAbstractBase this.recordsManagementService = recordsManagementService; } - public void setRecordService(RecordService recordService) + public void setRecordsManagementSecurityService(RecordsManagementSecurityService recordsManagementSecurityService) { - this.recordService = recordService; + this.recordsManagementSecurityService = recordsManagementSecurityService; } public void setPermissionService(PermissionService permissionService) @@ -111,29 +105,15 @@ public class CreateRecordAction extends ActionExecuterAbstractBase { throw new AlfrescoRuntimeException("Unable to create record, because new record container could not be found."); } - + // move the document into the file plan nodeService.moveNode(actionedUponNodeRef, newRecordContainer, ContentModel.ASSOC_CONTAINS, parentAssoc.getQName()); // maintain the original primary location nodeService.addChild(parentAssoc.getParentRef(), actionedUponNodeRef, parentAssoc.getTypeQName(), parentAssoc.getQName()); - // add extended security information to the record - Map props = new HashMap(1); - props.put(PROP_READERS, (Serializable)readers); - nodeService.addAspect(actionedUponNodeRef, ASPECT_EXTENDED_RECORD_SECURITY, props); - - // add permission so readers can still 'see' the new record - // Note: using the regular permission service as we don't want to reflect this permission up (and down) the - // hierarchy - permissionService.setPermission(actionedUponNodeRef, - RecordReadersDynamicAuthority.RECORD_READERS, - RMPermissionModel.READ_RECORDS, - true); - permissionService.setPermission(filePlan, - RecordReadersDynamicAuthority.RECORD_READERS, - RMPermissionModel.VIEW_RECORDS, - true); + // set the readers + recordsManagementSecurityService.setExtendedReaders(actionedUponNodeRef, readers); return null; } @@ -141,13 +121,13 @@ public class CreateRecordAction extends ActionExecuterAbstractBase } else { - throw new AlfrescoRuntimeException("Unable to file file plan."); + throw new AlfrescoRuntimeException("Unable to find file plan."); } } private NodeRef getNewRecordContainer(NodeRef filePlan) { - List assocs = nodeService.getChildAssocs(filePlan, ASSOC_NEW_RECORDS, RegexQNamePattern.MATCH_ALL); + List assocs = nodeService.getChildAssocs(filePlan, ASSOC_UNFILED_RECORDS, RegexQNamePattern.MATCH_ALL); if (assocs.size() != 1) { throw new AlfrescoRuntimeException("Error getting the new record container, because the container cannot be indentified."); diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMAfterInvocationProvider.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMAfterInvocationProvider.java index e0ca52355d..7d725a9d1a 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMAfterInvocationProvider.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMAfterInvocationProvider.java @@ -61,6 +61,9 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.InitializingBean; +/** + * RM After Invocation Provider + */ public class RMAfterInvocationProvider extends RMSecurityCommon implements AfterInvocationProvider, InitializingBean { diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMSecurityCommon.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMSecurityCommon.java index d2dde825dc..756faefff9 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMSecurityCommon.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMSecurityCommon.java @@ -31,6 +31,9 @@ import org.alfresco.service.cmr.security.AccessStatus; import org.alfresco.service.cmr.security.PermissionService; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; /** * @author Roy Wetherall @@ -42,10 +45,11 @@ public class RMSecurityCommon private static Log logger = LogFactory.getLog(RMSecurityCommon.class); + private ApplicationContext applicationContext; + protected NodeService nodeService; protected PermissionService permissionService; protected RecordsManagementService rmService; - protected RecordService recordService; protected RMCaveatConfigComponent caveatConfigComponent; public void setNodeService(NodeService nodeService) @@ -63,11 +67,6 @@ public class RMSecurityCommon this.rmService = rmService; } - public void setRecordService(RecordService recordService) - { - this.recordService = recordService; - } - public void setCaveatConfigComponent(RMCaveatConfigComponent caveatConfigComponent) { this.caveatConfigComponent = caveatConfigComponent; diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/impl/CreateCapability.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/impl/CreateCapability.java index e4acc7b194..c606f4a7b7 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/impl/CreateCapability.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/impl/CreateCapability.java @@ -26,6 +26,7 @@ import net.sf.acegisecurity.vote.AccessDecisionVoter; import org.alfresco.model.ContentModel; import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel; import org.alfresco.module.org_alfresco_module_rm.capability.declarative.DeclarativeCapability; +import org.alfresco.module.org_alfresco_module_rm.record.RecordService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.security.AccessStatus; import org.alfresco.service.namespace.QName; @@ -37,6 +38,13 @@ import org.alfresco.service.namespace.QName; */ public class CreateCapability extends DeclarativeCapability { + private RecordService recordService; + + public void setRecordService(RecordService recordService) + { + this.recordService = recordService; + } + /** * @see org.alfresco.module.org_alfresco_module_rm.capability.Capability#evaluate(org.alfresco.service.cmr.repository.NodeRef) */ diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/RecordsManagementModel.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/RecordsManagementModel.java index 992059c02a..6a18bf9a60 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/RecordsManagementModel.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/RecordsManagementModel.java @@ -57,8 +57,8 @@ public interface RecordsManagementModel extends RecordsManagementCustomModel // Records management root container public static final QName TYPE_FILE_PLAN = QName.createQName(RM_URI, "filePlan"); - // New records container - public static final QName TYPE_NEW_RECORDS_CONTAINER = QName.createQName(RM_URI, "newRecordsContainer"); + // Unfiled record container + public static final QName TYPE_UNFILED_RECORD_CONTAINER = QName.createQName(RM_URI, "unfiledRecordContainer"); // Disposition instructions aspect public static final QName ASPECT_SCHEDULED = QName.createQName(RM_URI, "scheduled"); @@ -170,7 +170,7 @@ public interface RecordsManagementModel extends RecordsManagementCustomModel public static final QName ASPECT_RECORDS_MANAGEMENT_ROOT = QName.createQName(RM_URI, "recordsManagementRoot"); public static final QName ASSOC_HOLDS = QName.createQName(RM_URI, "holds"); public static final QName ASSOC_TRANSFERS = QName.createQName(RM_URI, "transfers"); - public static final QName ASSOC_NEW_RECORDS = QName.createQName(RM_URI, "newRecords"); + public static final QName ASSOC_UNFILED_RECORDS = QName.createQName(RM_URI, "unfiledRecords"); // Hold type public static final QName TYPE_HOLD = QName.createQName(RM_URI, "hold"); @@ -224,7 +224,7 @@ public interface RecordsManagementModel extends RecordsManagementCustomModel public static final QName PROP_RS_DISPOITION_AUTHORITY = QName.createQName(RM_URI, "recordSearchDispositionAuthority"); public static final QName PROP_RS_HOLD_REASON = QName.createQName(RM_URI, "recordSearchHoldReason"); - // Extended record security aspect - public static final QName ASPECT_EXTENDED_RECORD_SECURITY = QName.createQName(RM_URI, "extendedRecordSecurity"); + // Extended readers aspect + public static final QName ASPECT_EXTENDED_READERS = QName.createQName(RM_URI, "extendedReaders"); public static final QName PROP_READERS = QName.createQName(RM_URI, "readers"); } diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/behaviour/FilePlanType.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/behaviour/FilePlanType.java index 5f961b6185..350e2ea7a9 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/behaviour/FilePlanType.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/behaviour/FilePlanType.java @@ -18,21 +18,12 @@ */ package org.alfresco.module.org_alfresco_module_rm.model.behaviour; -import java.io.Serializable; -import java.util.HashMap; -import java.util.Map; - -import org.alfresco.model.ContentModel; import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; import org.alfresco.repo.node.NodeServicePolicies; -import org.alfresco.repo.policy.JavaBehaviour; import org.alfresco.repo.policy.PolicyComponent; -import org.alfresco.repo.policy.Behaviour.NotificationFrequency; import org.alfresco.service.cmr.repository.ChildAssociationRef; -import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.security.PermissionService; -import org.alfresco.service.namespace.QName; /** * Behaviour associated with the file plan type @@ -82,10 +73,10 @@ public class FilePlanType implements RecordsManagementModel, */ public void init() { - policyComponent.bindClassBehaviour( - NodeServicePolicies.OnCreateNodePolicy.QNAME, - TYPE_FILE_PLAN, - new JavaBehaviour(this, "onCreateNode", NotificationFrequency.TRANSACTION_COMMIT)); + // policyComponent.bindClassBehaviour( + // NodeServicePolicies.OnCreateNodePolicy.QNAME, + // TYPE_FILE_PLAN, + // new JavaBehaviour(this, "onCreateNode", NotificationFrequency.TRANSACTION_COMMIT)); } /** @@ -94,22 +85,6 @@ public class FilePlanType implements RecordsManagementModel, @Override public void onCreateNode(ChildAssociationRef assoc) { - // grab the newly created file plan - NodeRef filePlan = assoc.getChildRef(); - - // create the properties map - Map properties = new HashMap(1); - properties.put(ContentModel.PROP_NAME, NAME_NR_CONTAINER); - - // create the 'new records' folder - NodeRef container = nodeService.createNode( - filePlan, - ASSOC_NEW_RECORDS, - QName.createQName(RM_URI, NAME_NR_CONTAINER), - TYPE_NEW_RECORDS_CONTAINER, - properties).getChildRef(); - - // set inheritance to false - permissionService.setInheritParentPermissions(container, false); + // TODO refactor the file plan behaviours from the service code } } diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/behaviour/RmSiteType.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/behaviour/RmSiteType.java index c66039c5a0..51a0ea48de 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/behaviour/RmSiteType.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/behaviour/RmSiteType.java @@ -56,6 +56,9 @@ public class RmSiteType implements RecordsManagementModel, /** Record Management Search Service */ private RecordsManagementSearchService recordsManagementSearchService; + /** Behaviour */ + JavaBehaviour behaviour = new JavaBehaviour(this, "onCreateNode", NotificationFrequency.FIRST_EVENT); + /** * Set the policy component * @param policyComponent policy component @@ -99,7 +102,7 @@ public class RmSiteType implements RecordsManagementModel, policyComponent.bindClassBehaviour( NodeServicePolicies.OnCreateNodePolicy.QNAME, TYPE_RM_SITE, - new JavaBehaviour(this, "onCreateNode", NotificationFrequency.FIRST_EVENT)); + behaviour); } /** @@ -108,33 +111,41 @@ public class RmSiteType implements RecordsManagementModel, @Override public void onCreateNode(ChildAssociationRef childAssocRef) { - final NodeRef rmSite = childAssocRef.getChildRef(); - - // Do not execute behaviour if this has been created in the archive store - if(rmSite.getStoreRef().equals(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE) == true) - { - // This is not the spaces store - probably the archive store - return; - } - - if (nodeService.exists(rmSite) == true) - { - AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + behaviour.disable(); + try + { + final NodeRef rmSite = childAssocRef.getChildRef(); + + // Do not execute behaviour if this has been created in the archive store + if(rmSite.getStoreRef().equals(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE) == true) { - public Object doWork() + // This is not the spaces store - probably the archive store + return; + } + + if (nodeService.exists(rmSite) == true) + { + AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() { - SiteInfo siteInfo = siteService.getSite(rmSite); - if (siteInfo != null) - { - // Create the file plan component - siteService.createContainer(siteInfo.getShortName(), COMPONENT_DOCUMENT_LIBRARY, TYPE_FILE_PLAN, null); - - // Add the reports - recordsManagementSearchService.addReports(siteInfo.getShortName()); - } - return null; - } - }, AuthenticationUtil.getAdminUserName()); - } + public Object doWork() + { + SiteInfo siteInfo = siteService.getSite(rmSite); + if (siteInfo != null) + { + // Create the file plan component + siteService.createContainer(siteInfo.getShortName(), COMPONENT_DOCUMENT_LIBRARY, TYPE_FILE_PLAN, null); + + // Add the reports + recordsManagementSearchService.addReports(siteInfo.getShortName()); + } + return null; + } + }, AuthenticationUtil.getAdminUserName()); + } + } + finally + { + behaviour.enable(); + } } } diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordService.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordService.java index 90746b8511..8b4df14e4b 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordService.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordService.java @@ -25,6 +25,7 @@ import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.QName; /** + * Record Service Interface. * * @author Roy Wetherall * @since 2.1 diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImpl.java index 839611cf91..e39f5ef22c 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImpl.java @@ -88,7 +88,7 @@ public class RecordServiceImpl implements RecordService, RecordsManagementModel { policyComponent.bindAssociationBehaviour( QName.createQName(NamespaceService.ALFRESCO_URI, "onCreateChildAssociation"), - TYPE_NEW_RECORDS_CONTAINER, + TYPE_UNFILED_RECORD_CONTAINER, ContentModel.ASSOC_CONTAINS, new JavaBehaviour(this, "onCreateNewRecord", NotificationFrequency.TRANSACTION_COMMIT)); } diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/permission/RecordReadersDynamicAuthority.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/security/ExtendedReaderDynamicAuthority.java similarity index 61% rename from rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/permission/RecordReadersDynamicAuthority.java rename to rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/security/ExtendedReaderDynamicAuthority.java index 0b3452e7bf..8eaf471512 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/permission/RecordReadersDynamicAuthority.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/security/ExtendedReaderDynamicAuthority.java @@ -16,13 +16,10 @@ * You should have received a copy of the GNU Lesser General Public License * along with Alfresco. If not, see . */ -package org.alfresco.module.org_alfresco_module_rm.permission; +package org.alfresco.module.org_alfresco_module_rm.security; -import java.util.List; import java.util.Set; -import org.alfresco.module.org_alfresco_module_rm.FilePlanComponentKind; -import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; import org.alfresco.repo.security.permissions.DynamicAuthority; import org.alfresco.repo.security.permissions.PermissionReference; @@ -30,44 +27,42 @@ import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.security.AuthorityService; import org.alfresco.service.cmr.security.AuthorityType; +import org.alfresco.service.namespace.QName; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; /** + * Extended readers dynamic authority implementation. + * * @author Roy Wetherall * @since 2.1 */ -public class RecordReadersDynamicAuthority implements DynamicAuthority, RecordsManagementModel, ApplicationContextAware +public class ExtendedReaderDynamicAuthority implements DynamicAuthority, + RecordsManagementModel, + ApplicationContextAware { - public static final String RECORD_READERS = "ROLE_RECORD_READERS"; - - private RecordsManagementService recordsManagementService; - - private NodeService nodeService; + /** Extended reader role */ + public static final String EXTENDED_READER = "ROLE_EXTENDED_READER"; + /** Authority service */ private AuthorityService authorityService; + /** Records management security service */ + private RecordsManagementSecurityService recordsManagementSecurityService; + + /** Node service */ + private NodeService nodeService; + + /** Application context */ private ApplicationContext applicationContext; - private RecordsManagementService getRecordsManagementService() - { - if (recordsManagementService == null) - { - recordsManagementService = (RecordsManagementService)applicationContext.getBean("recordsManagementService"); - } - return recordsManagementService; - } - - private NodeService getNodeService() - { - if (nodeService == null) - { - nodeService = (NodeService)applicationContext.getBean("nodeService"); - } - return nodeService; - } + // NOTE: we get the services directly from the application context in this way to avoid + // cyclic relationships and issues when loading the application context + /** + * @return authority service + */ private AuthorityService getAuthorityService() { if (authorityService == null) @@ -76,7 +71,34 @@ public class RecordReadersDynamicAuthority implements DynamicAuthority, RecordsM } return authorityService; } + + /** + * @return records management security service + */ + public RecordsManagementSecurityService getRecordsManagementSecurityService() + { + if (recordsManagementSecurityService == null) + { + recordsManagementSecurityService = (RecordsManagementSecurityService)applicationContext.getBean("recordsManagementSecurityService"); + } + return recordsManagementSecurityService; + } + + /** + * @return node service + */ + public NodeService getNodeService() + { + if (nodeService == null) + { + nodeService = (NodeService)applicationContext.getBean("nodeService"); + } + return nodeService; + } + /** + * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext) + */ @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { @@ -89,34 +111,37 @@ public class RecordReadersDynamicAuthority implements DynamicAuthority, RecordsM @Override public String getAuthority() { - return RECORD_READERS; + return EXTENDED_READER; } /** * @see org.alfresco.repo.security.permissions.DynamicAuthority#hasAuthority(org.alfresco.service.cmr.repository.NodeRef, java.lang.String) */ - @SuppressWarnings("unchecked") @Override public boolean hasAuthority(NodeRef nodeRef, String userName) { boolean result = false; - FilePlanComponentKind kind = getRecordsManagementService().getFilePlanComponentKind(nodeRef); - if (FilePlanComponentKind.RECORD.equals(kind) == true) + if (getNodeService().hasAspect(nodeRef, ASPECT_EXTENDED_READERS) == true) { - if (getNodeService().hasAspect(nodeRef, ASPECT_EXTENDED_RECORD_SECURITY) == true) + Set readers = getRecordsManagementSecurityService().getExtendedReaders(nodeRef); + if (readers != null) { - List readers = (List)nodeService.getProperty(nodeRef, PROP_READERS); for (String reader : readers) { - if (reader.startsWith("GROUP_") == true) + if ("GROUP_EVERYONE".equals(reader) == true) { + // 'eveyone' has read + result = true; + break; + } + else if (reader.startsWith("GROUP_") == true) + { + // check group to see if the user is contained Set contained = getAuthorityService().getContainedAuthorities(AuthorityType.USER, reader, false); if (contained.isEmpty() == false && contained.contains(userName) == true) { - System.out.println("User " + userName + " is contained in the read group " + reader); - result = true; break; } @@ -126,8 +151,6 @@ public class RecordReadersDynamicAuthority implements DynamicAuthority, RecordsM // presume we have a user if (reader.equals(userName) == true) { - System.out.println("User " + userName + " matches read user " + reader); - result = true; break; } @@ -135,10 +158,6 @@ public class RecordReadersDynamicAuthority implements DynamicAuthority, RecordsM } } } - else if (FilePlanComponentKind.FILE_PLAN.equals(kind) == true) - { - result = true; - } return result; } diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/security/RecordsManagementSecurityService.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/security/RecordsManagementSecurityService.java index 7cb8b18895..c782db6611 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/security/RecordsManagementSecurityService.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/security/RecordsManagementSecurityService.java @@ -152,4 +152,31 @@ public interface RecordsManagementSecurityService * @param permission permission */ void deletePermission(NodeRef nodeRef, String authority, String permission); + + /** + * + * @param nodeRef + * @return + */ + Set getExtendedReaders(NodeRef nodeRef); + + /** + * + * @param nodeRef + * @param readers + */ + void setExtendedReaders(NodeRef nodeRef, Set readers); + + /** + * + * @param nodeRef + * @param readers + */ + void removeExtendedReaders(NodeRef nodeRef, Set readers); + + /** + * + * @param nodeRef + */ + void removeAllExtendedReaders(NodeRef nodeRef); } diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/security/RecordsManagementSecurityServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/security/RecordsManagementSecurityServiceImpl.java index 9ae1855e1e..c80ff5a520 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/security/RecordsManagementSecurityServiceImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/security/RecordsManagementSecurityServiceImpl.java @@ -22,8 +22,11 @@ import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.io.Serializable; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import org.alfresco.error.AlfrescoRuntimeException; @@ -56,6 +59,9 @@ import org.apache.commons.logging.LogFactory; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; /** * Records management permission service implementation @@ -63,7 +69,8 @@ import org.json.JSONObject; * @author Roy Wetherall */ public class RecordsManagementSecurityServiceImpl implements RecordsManagementSecurityService, - RecordsManagementModel + RecordsManagementModel, + ApplicationContextAware { /** Capability service */ @@ -90,9 +97,24 @@ public class RecordsManagementSecurityServiceImpl implements RecordsManagementSe /** Records management role zone */ public static final String RM_ROLE_ZONE_PREFIX = "rmRoleZone"; + /** Unfiled record container name */ + private static final String NAME_UNFILED_CONTAINER = "Unfiled Records"; + /** Logger */ private static Log logger = LogFactory.getLog(RecordsManagementSecurityServiceImpl.class); + /** Application context */ + private ApplicationContext applicationContext; + + /** + * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext) + */ + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException + { + this.applicationContext = applicationContext; + } + /** * Set the capability service * @@ -204,9 +226,9 @@ public class RecordsManagementSecurityServiceImpl implements RecordsManagementSe if (nodeService.exists(rmRootNode) == true) { - AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + NodeRef unfiledContainer = AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() { - public Object doWork() + public NodeRef doWork() { // Create "all" role group for root node String allRoles = authorityService.createAuthority(AuthorityType.GROUP, getAllRolesGroupShortName(rmRootNode), "All Roles", null); @@ -214,16 +236,47 @@ public class RecordsManagementSecurityServiceImpl implements RecordsManagementSe // Set the permissions permissionService.setInheritParentPermissions(rmRootNode, false); permissionService.setPermission(rmRootNode, allRoles, RMPermissionModel.READ_RECORDS, true); - - return null; + permissionService.setPermission(rmRootNode, ExtendedReaderDynamicAuthority.EXTENDED_READER, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(rmRootNode, ExtendedReaderDynamicAuthority.EXTENDED_READER, RMPermissionModel.VIEW_RECORDS, true); + + // Create the unfiled record container + return createUnfiledContainer(rmRootNode, allRoles); } }, AuthenticationUtil.getSystemUserName()); // Bootstrap in the default set of roles for the newly created root node - bootstrapDefaultRoles(rmRootNode); + bootstrapDefaultRoles(rmRootNode, unfiledContainer); } } + /** + * Creates unfiled container node and sets up permissions + * + * @param rmRootNode + * @param allRoles + */ + private NodeRef createUnfiledContainer(NodeRef rmRootNode, String allRoles) + { + // create the properties map + Map properties = new HashMap(1); + properties.put(ContentModel.PROP_NAME, NAME_UNFILED_CONTAINER); + + // create the unfiled container + NodeRef container = nodeService.createNode( + rmRootNode, + ASSOC_UNFILED_RECORDS, + QName.createQName(RM_URI, NAME_UNFILED_CONTAINER), + TYPE_UNFILED_RECORD_CONTAINER, + properties).getChildRef(); + + // set inheritance to false + permissionService.setInheritParentPermissions(container, false); + permissionService.setPermission(container, allRoles, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(container, ExtendedReaderDynamicAuthority.EXTENDED_READER, RMPermissionModel.READ_RECORDS, true); + + return container; + } + /** * Delete root node behaviour * @@ -293,17 +346,20 @@ public class RecordsManagementSecurityServiceImpl implements RecordsManagementSe Set perms = permissionService.getAllSetPermissions(catNodeRef); for (AccessPermission perm : perms) { - AccessStatus accessStatus = perm.getAccessStatus(); - boolean allow = false; - if (AccessStatus.ALLOWED.equals(accessStatus) == true) - { - allow = true; - } - permissionService.setPermission( - folderNodeRef, - perm.getAuthority(), - perm.getPermission(), - allow); + if (ExtendedReaderDynamicAuthority.EXTENDED_READER.equals(perm.getAuthority()) == false) + { + AccessStatus accessStatus = perm.getAccessStatus(); + boolean allow = false; + if (AccessStatus.ALLOWED.equals(accessStatus) == true) + { + allow = true; + } + permissionService.setPermission( + folderNodeRef, + perm.getAuthority(), + perm.getPermission(), + allow); + } } return null; @@ -324,8 +380,11 @@ public class RecordsManagementSecurityServiceImpl implements RecordsManagementSe { public Object doWork() { - // Break inheritance + // break inheritance permissionService.setInheritParentPermissions(nodeRef, false); + + // set extended reader permissions + permissionService.setPermission(nodeRef, ExtendedReaderDynamicAuthority.EXTENDED_READER, RMPermissionModel.READ_RECORDS, true); return null; } @@ -352,7 +411,12 @@ public class RecordsManagementSecurityServiceImpl implements RecordsManagementSe /** * @see org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService#bootstrapDefaultRoles(org.alfresco.service.cmr.repository.NodeRef) */ - public void bootstrapDefaultRoles(final NodeRef rmRootNode) + public void bootstrapDefaultRoles(NodeRef rmRootNode) + { + bootstrapDefaultRoles(rmRootNode, null); + } + + private void bootstrapDefaultRoles(final NodeRef rmRootNode, final NodeRef unfiledContainer) { AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() { @@ -434,7 +498,12 @@ public class RecordsManagementSecurityServiceImpl implements RecordsManagementSe // Add any additional admin permissions if (isAdmin == true) { + // Admin has filing permissionService.setPermission(rmRootNode, role.getRoleGroupName(), RMPermissionModel.FILING, true); + if (unfiledContainer != null) + { + permissionService.setPermission(unfiledContainer, role.getRoleGroupName(), RMPermissionModel.FILING, true); + } // Add the creating user to the administration group String user = AuthenticationUtil.getFullyAuthenticatedUser(); @@ -597,6 +666,12 @@ public class RecordsManagementSecurityServiceImpl implements RecordsManagementSe }, AuthenticationUtil.getSystemUserName()); } + /** + * + * @param rmRootNode + * @param roleAuthority + * @return + */ private Set getCapabilitiesImpl(NodeRef rmRootNode, String roleAuthority) { Set permissions = permissionService.getAllSetPermissions(rmRootNode); @@ -636,7 +711,7 @@ public class RecordsManagementSecurityServiceImpl implements RecordsManagementSe }, AuthenticationUtil.getSystemUserName()).booleanValue(); } - /* + /** * @see org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService#hasRMAdminRole(org.alfresco.service.cmr.repository.NodeRef, java.lang.String) */ public boolean hasRMAdminRole(NodeRef rmRootNode, String user) @@ -791,7 +866,7 @@ public class RecordsManagementSecurityServiceImpl implements RecordsManagementSe public Boolean doWork() throws Exception { if (recordsManagementService.isFilePlan(nodeRef) == false && - recordsManagementService.isRecordCategory(nodeRef) == true) + recordsManagementService.isRecordsManagementContainer(nodeRef) == true) { setReadPermissionUp(nodeRef, authority); setPermissionDown(nodeRef, authority, permission); @@ -841,13 +916,13 @@ public class RecordsManagementSecurityServiceImpl implements RecordsManagementSe private void setPermissionDown(NodeRef nodeRef, String authority, String permission) { setPermissionImpl(nodeRef, authority, permission); - if (recordsManagementService.isRecordCategory(nodeRef) == true) + if (recordsManagementService.isRecordsManagementContainer(nodeRef) == true) { List assocs = nodeService.getChildAssocs(nodeRef, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); for (ChildAssociationRef assoc : assocs) { NodeRef child = assoc.getChildRef(); - if (recordsManagementService.isRecordCategory(child) == true || + if (recordsManagementService.isRecordsManagementContainer(child) == true || recordsManagementService.isRecordFolder(child) == true) { setPermissionDown(child, authority, permission); @@ -886,13 +961,13 @@ public class RecordsManagementSecurityServiceImpl implements RecordsManagementSe // Delete permission on this node permissionService.deletePermission(nodeRef, authority, permission); - if (recordsManagementService.isRecordCategory(nodeRef) == true) + if (recordsManagementService.isRecordsManagementContainer(nodeRef) == true) { List assocs = nodeService.getChildAssocs(nodeRef, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); for (ChildAssociationRef assoc : assocs) { NodeRef child = assoc.getChildRef(); - if (recordsManagementService.isRecordCategory(child) == true || + if (recordsManagementService.isRecordsManagementContainer(child) == true || recordsManagementService.isRecordFolder(child) == true) { deletePermission(child, authority, permission); @@ -904,4 +979,95 @@ public class RecordsManagementSecurityServiceImpl implements RecordsManagementSe } }, AuthenticationUtil.getSystemUserName()); } + + @SuppressWarnings("unchecked") + @Override + public Set getExtendedReaders(NodeRef nodeRef) + { + NodeService nodeService = (NodeService)applicationContext.getBean("nodeService"); + Set result = null; + + Map readerMap = (Map)nodeService.getProperty(nodeRef, PROP_READERS); + if (readerMap != null) + { + result = readerMap.keySet(); + } + + return result; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService#setExtendedReaders(org.alfresco.service.cmr.repository.NodeRef, java.util.Set) + */ + @SuppressWarnings("unchecked") + @Override + public void setExtendedReaders(NodeRef nodeRef, Set readers) + { + ParameterCheck.mandatory("nodeRef", nodeRef); + ParameterCheck.mandatory("readers", readers); + + NodeService nodeService = (NodeService)applicationContext.getBean("nodeService"); + RecordsManagementService recordsManagementService = (RecordsManagementService)applicationContext.getBean("recordsManagementService"); + + if (nodeRef != null && + readers.isEmpty() == false) + { + // add the aspect if missing + if (nodeService.hasAspect(nodeRef, ASPECT_EXTENDED_READERS) == false) + { + nodeService.addAspect(nodeRef, ASPECT_EXTENDED_READERS, null); + } + + // get reader map + Map readersMap = (Map)nodeService.getProperty(nodeRef, PROP_READERS); + if (readersMap == null) + { + // create reader map + readersMap = new HashMap(7); + } + + for (String reader : readers) + { + if (readersMap.containsKey(reader) == true) + { + // increment reference count + Integer count = readersMap.get(reader); + readersMap.put(reader, Integer.valueOf(count.intValue()+1)); + } + else + { + // add reader with initial count + readersMap.put(reader, Integer.valueOf(1)); + } + } + + // set the readers property (this will in turn apply the aspect if required) + nodeService.setProperty(nodeRef, PROP_READERS, (Serializable)readersMap); + + // apply the extended readers up the file plan primary hierarchy + NodeRef parent = nodeService.getPrimaryParent(nodeRef).getParentRef(); + if (parent != null && + recordsManagementService.isFilePlanComponent(parent) == true) + { + setExtendedReaders(parent, readers); + } + } + } + + @Override + public void removeExtendedReaders(NodeRef nodeRef, Set readers) + { + // TODO Auto-generated method stub + + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService#removeAllExtendedReaders(org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + public void removeAllExtendedReaders(NodeRef nodeRef) + { + // TODO Auto-generated method stub + + } } diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/NewRecordsManagementSecurityServiceImplTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/NewRecordsManagementSecurityServiceImplTest.java new file mode 100644 index 0000000000..938768a545 --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/NewRecordsManagementSecurityServiceImplTest.java @@ -0,0 +1,151 @@ + +package org.alfresco.module.org_alfresco_module_rm.test.service; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * Records management security service test. + * + * @author Roy Wetherall + */ +public class NewRecordsManagementSecurityServiceImplTest extends BaseRMTestCase +{ + private NodeRef record; + private NodeRef recordToo; + + @Override + protected boolean isUserTest() + { + return true; + } + + @Override + protected void setupTestDataImpl() + { + super.setupTestDataImpl(); + + record = utils.createRecord(rmFolder, "record.txt"); + recordToo = utils.createRecord(rmFolder, "recordToo.txt"); + } + + + // TODO testGetProtectedAspects + + // TODO getProtectedProperties + + // TODO bootstrapDefaultRoles + + // TODO getRoles + + // TODO getRolesByUser + + // TODO getRole + + // TODO existsRole + + // TODO hasRMAdminRole + + // TODO createRole + + // TODO updateRole + + // TODO deleteRole + + // TODO assignRoleToAuthority + + // TODO setPermission + + // TODO deletePermission + + public void testExtendedReaders() + { + doTestInTransaction(new Test() + { + public Void run() + { + assertFalse(hasExtendedReadersAspect(filePlan)); + assertFalse(hasExtendedReadersAspect(rmContainer)); + assertFalse(hasExtendedReadersAspect(rmFolder)); + assertFalse(hasExtendedReadersAspect(record)); + + assertNull(securityService.getExtendedReaders(record)); + + Set extendedReaders = new HashSet(2); + extendedReaders.add("monkey"); + extendedReaders.add("elephant"); + + securityService.setExtendedReaders(record, extendedReaders); + + Map testMap = new HashMap(2); + testMap.put("monkey", Integer.valueOf(1)); + testMap.put("elephant", Integer.valueOf(1)); + + test(filePlan, testMap); + test(rmContainer, testMap); + test(rmFolder, testMap); + test(record, testMap); + + Set extendedReadersToo = new HashSet(2); + extendedReadersToo.add("monkey"); + extendedReadersToo.add("snake"); + + securityService.setExtendedReaders(recordToo, extendedReadersToo); + + Map testMapToo = new HashMap(2); + testMapToo.put("monkey", Integer.valueOf(1)); + testMapToo.put("snake", Integer.valueOf(1)); + + Map testMapThree = new HashMap(3); + testMapThree.put("monkey", Integer.valueOf(2)); + testMapThree.put("elephant", Integer.valueOf(1)); + testMapThree.put("snake", Integer.valueOf(1)); + + test(filePlan, testMapThree); + test(rmContainer, testMapThree); + test(rmFolder, testMapThree); + test(recordToo, testMapToo); + + return null; + } + + private boolean hasExtendedReadersAspect(NodeRef nodeRef) + { + return nodeService.hasAspect(nodeRef, ASPECT_EXTENDED_READERS); + } + + private void test(NodeRef nodeRef, Map testMap) + { + assertTrue(hasExtendedReadersAspect(nodeRef)); + + Map readersMap = (Map)nodeService.getProperty(nodeRef, PROP_READERS); + assertNotNull(readersMap); + assertEquals(testMap.size(), readersMap.size()); + + for (Map.Entry entry: testMap.entrySet()) + { + assertTrue(readersMap.containsKey(entry.getKey())); + assertEquals(entry.getValue(), readersMap.get(entry.getKey())); + + } + + Set readers = securityService.getExtendedReaders(nodeRef); + assertNotNull(readers); + assertEquals(testMap.size(), readers.size()); + } + }); + } + + // TODO getExtendedReaders + + // TODO setExtendedReaders + + // TODO removeExtendedReaders + + // TODO removeAllExtendedReaders +} diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/RecordServiceTestImpl.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/RecordServiceTestImpl.java index d9cd74d926..9bd4d054bf 100644 --- a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/RecordServiceTestImpl.java +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/RecordServiceTestImpl.java @@ -20,8 +20,9 @@ package org.alfresco.module.org_alfresco_module_rm.test.service; import org.alfresco.model.ContentModel; import org.alfresco.module.org_alfresco_module_rm.action.dm.CreateRecordAction; +import org.alfresco.module.org_alfresco_module_rm.capability.Capability; import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel; -import org.alfresco.module.org_alfresco_module_rm.permission.RecordReadersDynamicAuthority; +import org.alfresco.module.org_alfresco_module_rm.security.ExtendedReaderDynamicAuthority; import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.site.SiteModel; @@ -132,8 +133,6 @@ public class RecordServiceTestImpl extends BaseRMTestCase { public Void run() { - //assertFalse(rmService.isRecord(dmDocument)); - assertEquals(AccessStatus.DENIED, dmPermissionService.hasPermission(dmDocument, RMPermissionModel.READ_RECORDS)); assertEquals(AccessStatus.DENIED, dmPermissionService.hasPermission(filePlan, RMPermissionModel.VIEW_RECORDS)); @@ -149,6 +148,14 @@ public class RecordServiceTestImpl extends BaseRMTestCase assertEquals(AccessStatus.ALLOWED, dmPermissionService.hasPermission(filePlan, RMPermissionModel.VIEW_RECORDS)); assertTrue(rmService.isRecord(dmDocument)); + + // + Capability createCapability = capabilityService.getCapability("Create"); + assertNotNull(createCapability); + createCapability.evaluate(dmDocument); + + + }; }, dmUserName); diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/RecordsManagementSecurityServiceImplTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/RecordsManagementSecurityServiceImplTest.java index 76838128a3..a1872523a8 100644 --- a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/RecordsManagementSecurityServiceImplTest.java +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/RecordsManagementSecurityServiceImplTest.java @@ -56,7 +56,7 @@ import org.alfresco.util.GUID; import org.alfresco.util.PropertyMap; /** - * Event service implementation unit test + * Security service implementation unit test * * @author Roy Wetherall */ diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseRMTestCase.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseRMTestCase.java index 8dc01dd7c6..e0ef5ac41f 100644 --- a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseRMTestCase.java +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseRMTestCase.java @@ -49,6 +49,7 @@ import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.cmr.security.AuthorityService; import org.alfresco.service.cmr.security.MutableAuthenticationService; +import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.cmr.site.SiteInfo; import org.alfresco.service.cmr.site.SiteService; @@ -104,6 +105,7 @@ public abstract class BaseRMTestCase extends RetryingTransactionHelperTestCase protected PersonService personService; protected TransactionService transactionService; protected FileFolderService fileFolderService; + protected PermissionService permissionService; /** RM Services */ protected RecordsManagementService rmService; @@ -260,6 +262,7 @@ public abstract class BaseRMTestCase extends RetryingTransactionHelperTestCase personService = (PersonService)this.applicationContext.getBean("PersonService"); transactionService = (TransactionService)applicationContext.getBean("TransactionService"); fileFolderService = (FileFolderService)applicationContext.getBean("FileFolderService"); + permissionService = (PermissionService)applicationContext.getBean("PermissionService"); // Get RM services rmService = (RecordsManagementService)applicationContext.getBean("RecordsManagementService"); @@ -329,15 +332,6 @@ public abstract class BaseRMTestCase extends RetryingTransactionHelperTestCase setupTestDataImpl(); return null; } - - // check that the new records container has been created for the file plan - public void test(Void arg0) throws Exception - { - // NodeRef newRecordsContainer = recordService.getNewRecordContainer(filePlan); - // assertNotNull(newRecordsContainer); - // assertEquals(TYPE_NEW_RECORDS_CONTAINER, nodeService.getType(newRecordsContainer)); - - }; }, AuthenticationUtil.getSystemUserName()); } From 4e433635df9d22733ed08ad61341c77afcc79b71 Mon Sep 17 00:00:00 2001 From: Roy Wetherall Date: Fri, 5 Oct 2012 06:03:21 +0000 Subject: [PATCH 5/9] RM InPlace Prototype: * permission mapping improvements ... allows filling and declaration of unfiled records * actions of 'records' in doc lib are shown as restricted rm list * "Record" banner is shown in doc lib if content is a record (this differs from the normal record view where the undeclared status is shown, this isn't relevant for a content user) * linked to many record folders indicator not shown unless it really is linked to many record folders git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/DEV/INPLACE@42374 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../org_alfresco_module_rm/action-context.xml | 3 +- .../model/recordsModel.xml | 1 - .../rm-service-context.xml | 6 +- .../RecordsManagementServiceImpl.java | 11 ++ .../action/dm/CreateRecordAction.java | 111 +++++------ .../app/evaluator/MultiParentEvaluator.java | 28 ++- .../record/RecordService.java | 8 + .../record/RecordServiceImpl.java | 172 ++++++++++-------- .../RecordsManagementSecurityService.java | 33 +++- .../RecordsManagementSecurityServiceImpl.java | 92 +++++++++- 10 files changed, 304 insertions(+), 161 deletions(-) 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 3179870f95..aeaf6af44d 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 @@ -15,8 +15,7 @@ - - + diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/model/recordsModel.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/model/recordsModel.xml index 50d4493ca5..675b96dc26 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/model/recordsModel.xml +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/model/recordsModel.xml @@ -726,7 +726,6 @@ Date Filed d:date - true diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml index d27cff9d0d..f316f9938c 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml @@ -481,7 +481,8 @@ org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService.createRole=RM_ALLOW org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService.updateRole=RM_ALLOW org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService.deleteRole=RM_ALLOW - org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService.assignRoleToAuthority=RM_ALLOW + org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService.assignRoleToAuthority=RM_ALLOW + org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService.hasExtendedReaders=RM_ALLOW org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService.getExtendedReaders=RM_ALLOW org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService.setExtendedReaders=RM_ALLOW org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService.removeExtendedReaders=RM_ALLOW @@ -801,6 +802,8 @@ + + @@ -845,6 +848,7 @@ diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementServiceImpl.java index eef43031b7..a2411a0c82 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementServiceImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementServiceImpl.java @@ -33,6 +33,7 @@ import org.alfresco.model.RenditionModel; import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionService; import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementCustomModel; import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService; import org.alfresco.repo.domain.node.NodeDAO; import org.alfresco.repo.policy.JavaBehaviour; import org.alfresco.repo.policy.PolicyComponent; @@ -284,7 +285,17 @@ public class RecordsManagementServiceImpl implements RecordsManagementService, NodeRef thumbnail = childAssocRef.getChildRef(); if (nodeService.exists(thumbnail) == true) { + // apply file plan component aspect to thumbnail nodeService.addAspect(thumbnail, ASPECT_FILE_PLAN_COMPONENT, null); + + // manage any extended readers + RecordsManagementSecurityService securityService = serviceRegistry.getRecordsManagementSecurityService(); + NodeRef parent = childAssocRef.getParentRef(); + Set readers = securityService.getExtendedReaders(parent); + if (readers != null && readers.size() != 0) + { + securityService.setExtendedReaders(thumbnail, readers, false); + } } } diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/dm/CreateRecordAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/dm/CreateRecordAction.java index 89f8c020e2..f3febba383 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/dm/CreateRecordAction.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/dm/CreateRecordAction.java @@ -19,23 +19,18 @@ package org.alfresco.module.org_alfresco_module_rm.action.dm; import java.util.List; -import java.util.Set; import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.model.ContentModel; 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.security.RecordsManagementSecurityService; +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.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.security.PermissionService; -import org.alfresco.service.namespace.RegexQNamePattern; /** * Creates a new record from an existing content object. @@ -47,29 +42,31 @@ import org.alfresco.service.namespace.RegexQNamePattern; public class CreateRecordAction extends ActionExecuterAbstractBase implements RecordsManagementModel { + /** Action name */ public static final String NAME = "create-record"; + /** Records management service */ private RecordsManagementService recordsManagementService; - - private RecordsManagementSecurityService recordsManagementSecurityService; - private PermissionService permissionService; + /** Record service */ + private RecordService recordService; private NodeService nodeService; + /** + * @param recordsManagementService records management service + */ public void setRecordsManagementService(RecordsManagementService recordsManagementService) { this.recordsManagementService = recordsManagementService; } - public void setRecordsManagementSecurityService(RecordsManagementSecurityService recordsManagementSecurityService) + /** + * @param recordService record service + */ + public void setRecordService(RecordService recordService) { - this.recordsManagementSecurityService = recordsManagementSecurityService; - } - - public void setPermissionService(PermissionService permissionService) - { - this.permissionService = permissionService; + this.recordService = recordService; } public void setNodeService(NodeService nodeService) @@ -77,64 +74,46 @@ public class CreateRecordAction extends ActionExecuterAbstractBase this.nodeService = nodeService; } + /** + * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef) + */ @Override protected void executeImpl(Action action, final NodeRef actionedUponNodeRef) { - // TODO we should use the file plan passed as a parameter - // grab the file plan - List filePlans = recordsManagementService.getFilePlans(); - if (filePlans.size() == 1) + // skip everything if the actioned upon node reference is already a record + if (nodeService.hasAspect(actionedUponNodeRef, ASPECT_RECORD) == false) { - final NodeRef filePlan = filePlans.get(0); - - AuthenticationUtil.runAsSystem(new RunAsWork() + // TODO we should use the file plan passed as a parameter + // grab the file plan + List filePlans = recordsManagementService.getFilePlans(); + if (filePlans.size() == 1) { - @Override - public Void doWork() throws Exception - { - // get the documents readers - Long aclId = nodeService.getNodeAclId(actionedUponNodeRef); - Set readers = permissionService.getReaders(aclId); - - // get the documents primary parent assoc - ChildAssociationRef parentAssoc = nodeService.getPrimaryParent(actionedUponNodeRef); - - /// get the new record container for the file plan - NodeRef newRecordContainer = getNewRecordContainer(filePlan); - if (newRecordContainer == null) - { - throw new AlfrescoRuntimeException("Unable to create record, because new record container could not be found."); - } - - // move the document into the file plan - nodeService.moveNode(actionedUponNodeRef, newRecordContainer, ContentModel.ASSOC_CONTAINS, parentAssoc.getQName()); - - // maintain the original primary location - nodeService.addChild(parentAssoc.getParentRef(), actionedUponNodeRef, parentAssoc.getTypeQName(), parentAssoc.getQName()); - - // set the readers - recordsManagementSecurityService.setExtendedReaders(actionedUponNodeRef, readers); - - return null; - } - }); - } - else - { - throw new AlfrescoRuntimeException("Unable to find file plan."); - } - } + // TODO parameterise the action with the file plan + final NodeRef filePlan = filePlans.get(0); - private NodeRef getNewRecordContainer(NodeRef filePlan) - { - List assocs = nodeService.getChildAssocs(filePlan, ASSOC_UNFILED_RECORDS, RegexQNamePattern.MATCH_ALL); - if (assocs.size() != 1) - { - throw new AlfrescoRuntimeException("Error getting the new record container, because the container cannot be indentified."); + // 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; + } + }); + } + else + { + throw new AlfrescoRuntimeException("Unable to find file plan."); + } } - return assocs.get(0).getChildRef(); - } + } + /** + * @see org.alfresco.repo.action.ParameterizedItemAbstractBase#addParameterDefinitions(java.util.List) + */ @Override protected void addParameterDefinitions(List params) { diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/evaluator/MultiParentEvaluator.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/evaluator/MultiParentEvaluator.java index 805a082711..a90b49c491 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/evaluator/MultiParentEvaluator.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/evaluator/MultiParentEvaluator.java @@ -22,19 +22,39 @@ import java.util.List; import org.alfresco.model.ContentModel; import org.alfresco.module.org_alfresco_module_rm.jscript.app.BaseEvaluator; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.RegexQNamePattern; /** + * Determines whether a node has multiple parents within a file plan + * * @author Roy Wetherall */ public class MultiParentEvaluator extends BaseEvaluator { @Override - protected boolean evaluateImpl(NodeRef nodeRef) - { - List parents = nodeService.getParentAssocs(nodeRef, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); - return (parents.size() > 1); + protected boolean evaluateImpl(final NodeRef nodeRef) + { + return AuthenticationUtil.runAsSystem(new RunAsWork() + { + @Override + public Boolean doWork() throws Exception + { + List parents = nodeService.getParentAssocs(nodeRef, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); + int count = 0; + for (ChildAssociationRef parent : parents) + { + if (nodeService.hasAspect(parent.getParentRef(), ASPECT_FILE_PLAN_COMPONENT) == true) + { + count++; + } + } + + return (count > 1); + } + }); } } diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordService.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordService.java index 8b4df14e4b..f885c18921 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordService.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordService.java @@ -47,6 +47,14 @@ public interface RecordService */ boolean isDeclared(NodeRef nodeRef); + /** + * Create a new record from an existing document. + * + * @param filePlan + * @param document + */ + void createRecordFromDocument(NodeRef filePlan, NodeRef document); + // TODO boolean isRecordFiled(NodeRef record); // TODO boolean isRecordClassified(NodeRef record); diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImpl.java index e39f5ef22c..00c0aa89fa 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImpl.java @@ -20,6 +20,7 @@ package org.alfresco.module.org_alfresco_module_rm.record; import java.util.Collection; import java.util.HashSet; +import java.util.List; import java.util.Set; import org.alfresco.error.AlfrescoRuntimeException; @@ -27,20 +28,20 @@ import org.alfresco.model.ContentModel; import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; import org.alfresco.module.org_alfresco_module_rm.identifier.IdentifierService; import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; -import org.alfresco.repo.policy.JavaBehaviour; +import org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService; import org.alfresco.repo.policy.PolicyComponent; -import org.alfresco.repo.policy.Behaviour.NotificationFrequency; -import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.service.cmr.dictionary.AspectDefinition; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.RegexQNamePattern; /** + * Record service implementation + * * @author Roy Wetherall * @since 2.1 */ @@ -56,6 +57,10 @@ public class RecordServiceImpl implements RecordService, RecordsManagementModel private PolicyComponent policyComponent; + private PermissionService permissionService; + + private RecordsManagementSecurityService recordsManagementSecurityService; + /** List of available record meta-data aspects */ private Set recordMetaDataAspects; @@ -84,42 +89,52 @@ public class RecordServiceImpl implements RecordService, RecordsManagementModel this.policyComponent = policyComponent; } - public void init() + public void setPermissionService(PermissionService permissionService) { - policyComponent.bindAssociationBehaviour( - QName.createQName(NamespaceService.ALFRESCO_URI, "onCreateChildAssociation"), - TYPE_UNFILED_RECORD_CONTAINER, - ContentModel.ASSOC_CONTAINS, - new JavaBehaviour(this, "onCreateNewRecord", NotificationFrequency.TRANSACTION_COMMIT)); + this.permissionService = permissionService; } - public void onCreateNewRecord(final ChildAssociationRef childAssocRef, boolean bNew) + public void setRecordsManagementSecurityService(RecordsManagementSecurityService recordsManagementSecurityService) { - AuthenticationUtil.runAsSystem(new RunAsWork() - { - @Override - public Void doWork() throws Exception - { - NodeRef nodeRef = childAssocRef.getChildRef(); - if (nodeService.exists(nodeRef) == true) - { - QName type = nodeService.getType(nodeRef); - if (ContentModel.TYPE_CONTENT.equals(type) == true || - dictionaryService.isSubClass(type, ContentModel.TYPE_CONTENT) == true) - { - makeRecord(nodeRef); - } - else - { - throw new AlfrescoRuntimeException("Only content can be created as a record."); - } - } - - return null; - } - }); + this.recordsManagementSecurityService = recordsManagementSecurityService; } + public void init() + { + // policyComponent.bindAssociationBehaviour( + // QName.createQName(NamespaceService.ALFRESCO_URI, "onCreateChildAssociation"), + // TYPE_UNFILED_RECORD_CONTAINER, + // ContentModel.ASSOC_CONTAINS, + // new JavaBehaviour(this, "onCreateNewRecord", NotificationFrequency.TRANSACTION_COMMIT)); + } + +// public void onCreateNewRecord(final ChildAssociationRef childAssocRef, boolean bNew) +// { +// AuthenticationUtil.runAsSystem(new RunAsWork() +// { +// @Override +// public Void doWork() throws Exception +// { +// NodeRef nodeRef = childAssocRef.getChildRef(); +// if (nodeService.exists(nodeRef) == true) +// { +// QName type = nodeService.getType(nodeRef); +// if (ContentModel.TYPE_CONTENT.equals(type) == true || +// dictionaryService.isSubClass(type, ContentModel.TYPE_CONTENT) == true) +// { +// makeRecord(nodeRef); +// } +// else +// { +// throw new AlfrescoRuntimeException("Only content can be created as a record."); +// } +// } +// +// return null; +// } +// }); +// } + /** * @see org.alfresco.module.org_alfresco_module_rm.record.RecordService#getRecordMetaDataAspects() */ @@ -153,47 +168,39 @@ public class RecordServiceImpl implements RecordService, RecordsManagementModel return (nodeService.hasAspect(record, ASPECT_DECLARED_RECORD)); } - /** - * @see org.alfresco.module.org_alfresco_module_rm.record.RecordService#getNewRecordContainer(org.alfresco.service.cmr.repository.NodeRef) - */ -// public NodeRef getNewRecordContainer(NodeRef filePlan) -// { -// NodeRef result = null; -// -// if (recordsManagementService.isFilePlan(filePlan) == true) -// { -// List assocs = nodeService.getChildAssocs(filePlan, ASSOC_NEW_RECORDS, RegexQNamePattern.MATCH_ALL); -// if (assocs.size() != 1) -// { -// throw new AlfrescoRuntimeException("Error getting the new record container, because the container cannot be indentified."); -// } -// result = assocs.get(0).getChildRef(); -// } -// -// return result; -// } + @Override + public void createRecordFromDocument(NodeRef filePlan, NodeRef document) + { + // skip everything if the document is already a record + if (nodeService.hasAspect(document, ASPECT_RECORD) == false) + { + // get the documents readers + Long aclId = nodeService.getNodeAclId(document); + Set readers = permissionService.getReaders(aclId); + + // get the documents primary parent assoc + ChildAssociationRef parentAssoc = nodeService.getPrimaryParent(document); + + /// get the new record container for the file plan + NodeRef newRecordContainer = getNewRecordContainer(filePlan); + if (newRecordContainer == null) + { + throw new AlfrescoRuntimeException("Unable to create record, because new record container could not be found."); + } -// @Override -// public NodeRef createRecord(NodeRef filePlan, NodeRef document) -// { -// // get the documents primary parent assoc -// ChildAssociationRef parentAssoc = nodeService.getPrimaryParent(document); -// -// /// get the new record container for the file plan -// NodeRef newRecordContainer = getNewRecordContainer(filePlan); -// if (newRecordContainer == null) -// { -// throw new AlfrescoRuntimeException("Unable to create record, because new record container could not be found."); -// } -// -// // move the document into the file plan -// nodeService.moveNode(document, newRecordContainer, ContentModel.ASSOC_CONTAINS, parentAssoc.getQName()); -// -// // maintain the original primary location -// nodeService.addChild(parentAssoc.getParentRef(), document, parentAssoc.getTypeQName(), parentAssoc.getQName()); -// -// return document; -// } + // move the document into the file plan + nodeService.moveNode(document, newRecordContainer, ContentModel.ASSOC_CONTAINS, parentAssoc.getQName()); + + // maintain the original primary location + nodeService.addChild(parentAssoc.getParentRef(), document, parentAssoc.getTypeQName(), parentAssoc.getQName()); + + // make the document a record + makeRecord(document); + + // set the readers + recordsManagementSecurityService.setExtendedReaders(document, readers); + } + } /** * @@ -206,5 +213,20 @@ public class RecordServiceImpl implements RecordService, RecordsManagementModel String recordId = identifierService.generateIdentifier(ASPECT_RECORD, nodeService.getPrimaryParent(document).getParentRef()); nodeService.setProperty(document, PROP_IDENTIFIER, recordId); } + + /** + * + * @param filePlan + * @return + */ + private NodeRef getNewRecordContainer(NodeRef filePlan) + { + List assocs = nodeService.getChildAssocs(filePlan, ASSOC_UNFILED_RECORDS, RegexQNamePattern.MATCH_ALL); + if (assocs.size() != 1) + { + throw new AlfrescoRuntimeException("Error getting the new record container, because the container cannot be indentified."); + } + return assocs.get(0).getChildRef(); + } } diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/security/RecordsManagementSecurityService.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/security/RecordsManagementSecurityService.java index c782db6611..3eba852122 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/security/RecordsManagementSecurityService.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/security/RecordsManagementSecurityService.java @@ -154,16 +154,30 @@ public interface RecordsManagementSecurityService void deletePermission(NodeRef nodeRef, String authority, String permission); /** + * Indicates whether the node has any extended readers set or not. * - * @param nodeRef - * @return + * @param nodeRef node reference + * @return boolean true if the node has extended readers set, false otherwise + * @since 2.1 + */ + boolean hasExtendedReaders(NodeRef nodeRef); + + /** + * Gets the set authorities that are extended readers for the given node. + * + * @param nodeRef node reference + * @return {@link Set}<{@link String}> extended readers + * @since 2.1 */ Set getExtendedReaders(NodeRef nodeRef); /** + * Set the authorities that are extended readers on the node. Applies extended readers to + * file plan parent hierarchy. * - * @param nodeRef - * @param readers + * @param nodeRef node reference + * @param readers extended readers + * @since 2.1 */ void setExtendedReaders(NodeRef nodeRef, Set readers); @@ -171,12 +185,23 @@ public interface RecordsManagementSecurityService * * @param nodeRef * @param readers + * @param applyToParents + * @since 2.1 + */ + void setExtendedReaders(NodeRef nodeRef, Set readers, boolean applyToParents); + + /** + * + * @param nodeRef + * @param readers + * @since 2.1 */ void removeExtendedReaders(NodeRef nodeRef, Set readers); /** * * @param nodeRef + * @since 2.1 */ void removeAllExtendedReaders(NodeRef nodeRef); } diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/security/RecordsManagementSecurityServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/security/RecordsManagementSecurityServiceImpl.java index c80ff5a520..5d355a0af4 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/security/RecordsManagementSecurityServiceImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/security/RecordsManagementSecurityServiceImpl.java @@ -31,6 +31,7 @@ import java.util.Set; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; +import org.alfresco.model.RenditionModel; import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService; import org.alfresco.module.org_alfresco_module_rm.capability.Capability; import org.alfresco.module.org_alfresco_module_rm.capability.CapabilityService; @@ -42,6 +43,7 @@ import org.alfresco.repo.policy.JavaBehaviour; import org.alfresco.repo.policy.PolicyComponent; import org.alfresco.repo.policy.Behaviour.NotificationFrequency; import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; @@ -70,7 +72,8 @@ import org.springframework.context.ApplicationContextAware; */ public class RecordsManagementSecurityServiceImpl implements RecordsManagementSecurityService, RecordsManagementModel, - ApplicationContextAware + ApplicationContextAware, + NodeServicePolicies.OnMoveNodePolicy { /** Capability service */ @@ -205,7 +208,12 @@ public class RecordsManagementSecurityServiceImpl implements RecordsManagementSe policyComponent.bindClassBehaviour( NodeServicePolicies.OnCreateNodePolicy.QNAME, TYPE_RECORD_FOLDER, - new JavaBehaviour(this, "onCreateRecordFolder", NotificationFrequency.TRANSACTION_COMMIT)); + new JavaBehaviour(this, "onCreateRecordFolder", NotificationFrequency.TRANSACTION_COMMIT)); + + policyComponent.bindClassBehaviour( + NodeServicePolicies.OnMoveNodePolicy.QNAME, + ASPECT_RECORD, + new JavaBehaviour(this, "onMoveNode", NotificationFrequency.TRANSACTION_COMMIT)); } /** @@ -980,6 +988,24 @@ public class RecordsManagementSecurityServiceImpl implements RecordsManagementSe }, AuthenticationUtil.getSystemUserName()); } + /** + * @see org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService#hasExtendedReaders(org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + public boolean hasExtendedReaders(NodeRef nodeRef) + { + boolean result = false; + Set extendedReaders = getExtendedReaders(nodeRef); + if (extendedReaders != null && extendedReaders.size() != 0) + { + result = true; + } + return result; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService#getExtendedReaders(org.alfresco.service.cmr.repository.NodeRef) + */ @SuppressWarnings("unchecked") @Override public Set getExtendedReaders(NodeRef nodeRef) @@ -999,10 +1025,19 @@ public class RecordsManagementSecurityServiceImpl implements RecordsManagementSe /** * @see org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService#setExtendedReaders(org.alfresco.service.cmr.repository.NodeRef, java.util.Set) */ - @SuppressWarnings("unchecked") @Override public void setExtendedReaders(NodeRef nodeRef, Set readers) { + setExtendedReaders(nodeRef, readers, true); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService#setExtendedReaders(org.alfresco.service.cmr.repository.NodeRef, java.util.Set, boolean) + */ + @SuppressWarnings("unchecked") + @Override + public void setExtendedReaders(NodeRef nodeRef, java.util.Set readers, boolean applyToParents) + { ParameterCheck.mandatory("nodeRef", nodeRef); ParameterCheck.mandatory("readers", readers); @@ -1044,12 +1079,26 @@ public class RecordsManagementSecurityServiceImpl implements RecordsManagementSe // set the readers property (this will in turn apply the aspect if required) nodeService.setProperty(nodeRef, PROP_READERS, (Serializable)readersMap); - // apply the extended readers up the file plan primary hierarchy - NodeRef parent = nodeService.getPrimaryParent(nodeRef).getParentRef(); - if (parent != null && - recordsManagementService.isFilePlanComponent(parent) == true) + // apply the readers to any renditions of the content + if (recordsManagementService.isRecord(nodeRef) == true) { - setExtendedReaders(parent, readers); + List assocs = nodeService.getChildAssocs(nodeRef, RenditionModel.ASSOC_RENDITION, RegexQNamePattern.MATCH_ALL); + for (ChildAssociationRef assoc : assocs) + { + NodeRef child = assoc.getChildRef(); + setExtendedReaders(child, readers, false); + } + } + + if (applyToParents == true) + { + // apply the extended readers up the file plan primary hierarchy + NodeRef parent = nodeService.getPrimaryParent(nodeRef).getParentRef(); + if (parent != null && + recordsManagementService.isFilePlanComponent(parent) == true) + { + setExtendedReaders(parent, readers); + } } } } @@ -1069,5 +1118,32 @@ public class RecordsManagementSecurityServiceImpl implements RecordsManagementSe { // TODO Auto-generated method stub + } + + @Override + public void onMoveNode(final ChildAssociationRef origAssoc, final ChildAssociationRef newAssoc) + { + // TODO temp solution for demo + + AuthenticationUtil.runAsSystem(new RunAsWork() + { + + @Override + public Void doWork() throws Exception + { + NodeRef record = newAssoc.getChildRef(); + NodeRef parent = newAssoc.getParentRef(); + + Set readers = getExtendedReaders(record); + if (readers != null && readers.size() != 0) + { + setExtendedReaders(parent, readers); + } + + return null; + }}); + + + } } From 389c775945c5b10ac0fb17ed9819e118dcb6cac0 Mon Sep 17 00:00:00 2001 From: Roy Wetherall Date: Wed, 17 Oct 2012 05:00:56 +0000 Subject: [PATCH 6/9] RM Prototype: * stoppped hidden RM actions from appearing in UI * added file record action (TODO run-as RecordsManager and make applicable via aspect) * added hook points to allow customisation of Rule UI and customised for file record action git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/DEV/INPLACE@42700 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../org_alfresco_module_rm/action-context.xml | 12 ++++- .../rm-capabilities-fileplan-context.xml | 20 ++++++- .../rm-action-context.xml | 9 ++-- .../rm-service-context.xml | 1 + .../FilePlanComponentKind.java | 3 +- .../RecordsManagementServiceImpl.java | 4 ++ .../action/RMActionExecuterAbstractBase.java | 11 ++++ .../action/dm/FileRecordAction.java | 54 +++++++++++++++++++ .../capability/RMActionProxyFactoryBean.java | 6 ++- .../jscript/app/JSONConversionComponent.java | 6 ++- .../record/RecordService.java | 4 +- .../record/RecordServiceImpl.java | 13 +++-- 12 files changed, 130 insertions(+), 13 deletions(-) create mode 100644 rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/dm/FileRecordAction.java 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 aeaf6af44d..dfc411ea07 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 @@ -22,6 +22,16 @@ {http://www.alfresco.org/model/content/1.0}content - + + + + + + + \ No newline at end of file diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/capability/rm-capabilities-fileplan-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/capability/rm-capabilities-fileplan-context.xml index 95ad0bf90e..898b546ea9 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/capability/rm-capabilities-fileplan-context.xml +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/capability/rm-capabilities-fileplan-context.xml @@ -12,13 +12,13 @@ - + + - @@ -62,5 +62,21 @@ + + + + + + + UNFILED_RECORD_CONTAINER + + + + + + + + \ No newline at end of file diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-action-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-action-context.xml index 391f3c6711..14740569ff 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-action-context.xml +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-action-context.xml @@ -33,7 +33,10 @@ - + @@ -64,7 +67,7 @@ - + @@ -94,7 +97,7 @@ - + diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml index f316f9938c..916c601ed6 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml @@ -849,6 +849,7 @@ org.alfresco.module.org_alfresco_module_rm.record.RecordService.getRecordMetaDataAspects=RM_ALLOW org.alfresco.module.org_alfresco_module_rm.record.RecordService.isDeclared=RM.Read.0 org.alfresco.module.org_alfresco_module_rm.record.RecordService.createRecordFromDocument=RM_ALLOW + org.alfresco.module.org_alfresco_module_rm.record.RecordService.getUnfiledRecordContainer=RM_ALLOW org.alfresco.module.org_alfresco_module_rm.record.RecordService.*=RM_DENY ]]> diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/FilePlanComponentKind.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/FilePlanComponentKind.java index 3f7da216df..e8b5b94324 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/FilePlanComponentKind.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/FilePlanComponentKind.java @@ -35,5 +35,6 @@ public enum FilePlanComponentKind RECORD, TRANSFER, HOLD, - DISPOSITION_SCHEDULE; + DISPOSITION_SCHEDULE, + UNFILED_RECORD_CONTAINER; } diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementServiceImpl.java index a2411a0c82..49fa37e409 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementServiceImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementServiceImpl.java @@ -526,6 +526,10 @@ public class RecordsManagementServiceImpl implements RecordsManagementService, { result = FilePlanComponentKind.DISPOSITION_SCHEDULE; } + else if (instanceOf(nodeRef, TYPE_UNFILED_RECORD_CONTAINER) == true) + { + result = FilePlanComponentKind.UNFILED_RECORD_CONTAINER; + } } return result; diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/RMActionExecuterAbstractBase.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/RMActionExecuterAbstractBase.java index c76ca0a65e..239250e7cd 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/RMActionExecuterAbstractBase.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/RMActionExecuterAbstractBase.java @@ -282,12 +282,23 @@ public abstract class RMActionExecuterAbstractBase extends ActionExecuterAbstra PropertyCheck.mandatory(this, "recordsManagementService", recordsManagementService); PropertyCheck.mandatory(this, "recordsManagementAdminService", recordsManagementAdminService); PropertyCheck.mandatory(this, "recordsManagementEventService", recordsManagementEventService); + for(AbstractCapability capability : capabilities) { capability.registerAction(this); } } + /** + * Indicates whether this records management action is public or not + * + * @return boolean true if public, false otherwise + */ + public boolean isPublicAction() + { + return publicAction; + } + /** * @see org.alfresco.repo.action.CommonResourceAbstractBase#setBeanName(java.lang.String) */ diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/dm/FileRecordAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/dm/FileRecordAction.java new file mode 100644 index 0000000000..5290b7d7b6 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/dm/FileRecordAction.java @@ -0,0 +1,54 @@ +package org.alfresco.module.org_alfresco_module_rm.action.dm; + +import java.util.List; + +import org.alfresco.repo.action.ParameterDefinitionImpl; +import org.alfresco.repo.action.executer.ActionExecuterAbstractBase; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.action.ParameterDefinition; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.model.FileFolderService; +import org.alfresco.service.cmr.model.FileNotFoundException; +import org.alfresco.service.cmr.repository.NodeRef; + +public class FileRecordAction extends ActionExecuterAbstractBase +{ + public static final String NAME = "file-record"; + public static final String PARAM_DESTINATION_RECORD_FOLDER = "destination-record-folder"; + + /** + * FileFolder service + */ + private FileFolderService fileFolderService; + + public void setFileFolderService(FileFolderService fileFolderService) + { + this.fileFolderService = fileFolderService; + } + + @Override + protected void addParameterDefinitions(List paramList) + { + paramList.add(new ParameterDefinitionImpl( + PARAM_DESTINATION_RECORD_FOLDER, + DataTypeDefinition.NODE_REF, + true, + getParamDisplayLabel(PARAM_DESTINATION_RECORD_FOLDER))); + } + + /** + * @see org.alfresco.repo.action.executer.ActionExecuter#execute(org.alfresco.repo.ref.NodeRef, org.alfresco.repo.ref.NodeRef) + */ + public void executeImpl(Action ruleAction, NodeRef actionedUponNodeRef) + { + NodeRef destinationParent = (NodeRef)ruleAction.getParameterValue(PARAM_DESTINATION_RECORD_FOLDER); + try + { + fileFolderService.move(actionedUponNodeRef, destinationParent, null); + } + catch (FileNotFoundException e) + { + // Do nothing + } + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMActionProxyFactoryBean.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMActionProxyFactoryBean.java index 5470a46f85..ab3b1f5a9a 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMActionProxyFactoryBean.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMActionProxyFactoryBean.java @@ -18,6 +18,7 @@ */ package org.alfresco.module.org_alfresco_module_rm.capability; +import org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase; import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction; import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionService; import org.alfresco.repo.action.RuntimeActionService; @@ -59,7 +60,10 @@ public class RMActionProxyFactoryBean extends ProxyFactoryBean { public Void doWork() throws Exception { - runtimeActionService.registerActionExecuter((ActionExecuter) getObject()); + if (((RMActionExecuterAbstractBase)getTargetSource().getTarget()).isPublicAction() == true) + { + runtimeActionService.registerActionExecuter((ActionExecuter) getObject()); + } recordsManagementActionService.register((RecordsManagementAction) getObject()); return null; } diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/JSONConversionComponent.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/JSONConversionComponent.java index 94b9e1bccf..fce108024a 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/JSONConversionComponent.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/jscript/app/JSONConversionComponent.java @@ -140,7 +140,11 @@ public class JSONConversionComponent extends org.alfresco.repo.jscript.app.JSONC rmNodeValues.put("kind", kind.toString()); // File plan node reference - rmNodeValues.put("filePlan", recordsManagementService.getFilePlan(nodeRef).toString()); + NodeRef filePlan = recordsManagementService.getFilePlan(nodeRef); + rmNodeValues.put("filePlan", filePlan.toString()); + + // Unfiled container node reference + rmNodeValues.put("unfiledRecordContainer", recordService.getUnfiledRecordContainer(filePlan).toString()); // Set the indicators array setIndicators(rmNodeValues, nodeRef); diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordService.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordService.java index f885c18921..037dd56ae9 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordService.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordService.java @@ -64,5 +64,7 @@ public interface RecordService // TODO NodeRef createAndFileRecord(NodeRef recordFolder, NodeRef document); - // TODO void fileRecord(NodeRef recordFolder, NodeRef record); + // TODO void fileRecord(NodeRef recordFolder, NodeRef record); + + public NodeRef getUnfiledRecordContainer(NodeRef filePlan); } diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImpl.java index 00c0aa89fa..dc53ecf1c9 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImpl.java @@ -47,18 +47,25 @@ import org.alfresco.service.namespace.RegexQNamePattern; */ public class RecordServiceImpl implements RecordService, RecordsManagementModel { + /** Node service **/ private NodeService nodeService; + /** Indentiy service */ private IdentifierService identifierService; + /** Records management service */ private RecordsManagementService recordsManagementService; + /** Dictionary service */ private DictionaryService dictionaryService; + /** Policy component */ private PolicyComponent policyComponent; + /** Permission service */ private PermissionService permissionService; + /** Records management security service */ private RecordsManagementSecurityService recordsManagementSecurityService; /** List of available record meta-data aspects */ @@ -182,7 +189,7 @@ public class RecordServiceImpl implements RecordService, RecordsManagementModel ChildAssociationRef parentAssoc = nodeService.getPrimaryParent(document); /// get the new record container for the file plan - NodeRef newRecordContainer = getNewRecordContainer(filePlan); + NodeRef newRecordContainer = getUnfiledRecordContainer(filePlan); if (newRecordContainer == null) { throw new AlfrescoRuntimeException("Unable to create record, because new record container could not be found."); @@ -219,12 +226,12 @@ public class RecordServiceImpl implements RecordService, RecordsManagementModel * @param filePlan * @return */ - private NodeRef getNewRecordContainer(NodeRef filePlan) + public NodeRef getUnfiledRecordContainer(NodeRef filePlan) { List assocs = nodeService.getChildAssocs(filePlan, ASSOC_UNFILED_RECORDS, RegexQNamePattern.MATCH_ALL); if (assocs.size() != 1) { - throw new AlfrescoRuntimeException("Error getting the new record container, because the container cannot be indentified."); + throw new AlfrescoRuntimeException("Error getting the unfiled container, because the container cannot be indentified."); } return assocs.get(0).getChildRef(); } From 691865e5744cbe24087cadc4b5f2a2d395c7b0b3 Mon Sep 17 00:00:00 2001 From: Roy Wetherall Date: Wed, 24 Oct 2012 00:24:55 +0000 Subject: [PATCH 7/9] RM: In-place prototype * demo model * file and create actions updated * bug fixes for demo scenario git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/DEV/INPLACE@43030 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../demo/demo-context.xml | 15 +++++ .../demo/demo-model.xml | 64 +++++++++++++++++++ .../org_alfresco_module_rm/module-context.xml | 3 + .../rm-actions.properties | 6 ++ .../action/dm/CreateRecordAction.java | 19 ++++++ .../action/dm/FileRecordAction.java | 34 +++++++--- 6 files changed, 132 insertions(+), 9 deletions(-) create mode 100644 rm-server/config/alfresco/module/org_alfresco_module_rm/demo/demo-context.xml create mode 100644 rm-server/config/alfresco/module/org_alfresco_module_rm/demo/demo-model.xml diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/demo/demo-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/demo/demo-context.xml new file mode 100644 index 0000000000..fc82db086e --- /dev/null +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/demo/demo-context.xml @@ -0,0 +1,15 @@ + + + + + + + + + + alfresco/module/org_alfresco_module_rm/demo/demo-model.xml + + + + + \ No newline at end of file diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/demo/demo-model.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/demo/demo-model.xml new file mode 100644 index 0000000000..1168b571d7 --- /dev/null +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/demo/demo-model.xml @@ -0,0 +1,64 @@ + + + + + Demo Model + Roy Wetherall + 1.0 + + + + + + + + + + + + + + + + + + + + + + + + Purchase Order + cm:content + + + + + Order Id + d:text + + + + Customer Id + d:text + + + + Order Quantity + d:int + + + + Delivery Date + d:date + + + + Sales Confirmation Sent + d:boolean + + + + + + \ No newline at end of file diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/module-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/module-context.xml index 6c663e725c..80bbbf27d7 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/module-context.xml +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/module-context.xml @@ -131,6 +131,9 @@ + + + diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-actions.properties b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-actions.properties index 47a031717d..22ff6e9457 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-actions.properties +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-actions.properties @@ -86,3 +86,9 @@ editDispositionActionAsOfDate.description=Edit Disposition Action As Of Date createDispositionSchedule.title=Create Disposition Schedule createDispositionSchedule.description=Create Disposition Schedule + +file-record.title=File Record +file-record.description=File a record into the file plan. + +create-record.title=Create A Record +create-record.description=Create a record from a document. diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/dm/CreateRecordAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/dm/CreateRecordAction.java index f3febba383..d90443a337 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/dm/CreateRecordAction.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/dm/CreateRecordAction.java @@ -18,6 +18,7 @@ */ package org.alfresco.module.org_alfresco_module_rm.action.dm; +import java.util.Date; import java.util.List; import org.alfresco.error.AlfrescoRuntimeException; @@ -51,6 +52,7 @@ public class CreateRecordAction extends ActionExecuterAbstractBase /** Record service */ private RecordService recordService; + /** Node service */ private NodeService nodeService; /** @@ -69,6 +71,9 @@ public class CreateRecordAction extends ActionExecuterAbstractBase this.recordService = recordService; } + /** + * @param nodeService node service + */ public void setNodeService(NodeService nodeService) { this.nodeService = nodeService; @@ -100,6 +105,20 @@ public class CreateRecordAction extends ActionExecuterAbstractBase // create record from existing document recordService.createRecordFromDocument(filePlan, actionedUponNodeRef); + // 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()); + } + return null; } }); diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/dm/FileRecordAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/dm/FileRecordAction.java index 5290b7d7b6..62c9242f15 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/dm/FileRecordAction.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/dm/FileRecordAction.java @@ -2,8 +2,11 @@ package org.alfresco.module.org_alfresco_module_rm.action.dm; import java.util.List; +import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.repo.action.ParameterDefinitionImpl; 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.DataTypeDefinition; @@ -39,16 +42,29 @@ public class FileRecordAction extends ActionExecuterAbstractBase /** * @see org.alfresco.repo.action.executer.ActionExecuter#execute(org.alfresco.repo.ref.NodeRef, org.alfresco.repo.ref.NodeRef) */ - public void executeImpl(Action ruleAction, NodeRef actionedUponNodeRef) + public void executeImpl(final Action ruleAction, final NodeRef actionedUponNodeRef) { - NodeRef destinationParent = (NodeRef)ruleAction.getParameterValue(PARAM_DESTINATION_RECORD_FOLDER); - try + final NodeRef destinationParent = (NodeRef)ruleAction.getParameterValue(PARAM_DESTINATION_RECORD_FOLDER); + + AuthenticationUtil.runAsSystem(new RunAsWork() { - fileFolderService.move(actionedUponNodeRef, destinationParent, null); - } - catch (FileNotFoundException e) - { - // Do nothing - } + @Override + public Void doWork() throws Exception + { + try + { + fileFolderService.move(actionedUponNodeRef, destinationParent, null); + } + catch (FileNotFoundException e) + { + throw new AlfrescoRuntimeException("Could not file record.", e); + } + + return null; + } + + }); + + } } From 75443d6ad5f433b0e5942da99bf191132069f31d Mon Sep 17 00:00:00 2001 From: Tuna Aksoy Date: Fri, 2 Nov 2012 10:37:51 +0000 Subject: [PATCH 8/9] Implemented a DeclareRecordAction so that a rule can be setup for a folder and a user can declare a record just uploading a file (in a collaboration site) into the folder. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/DEV/INPLACE@43332 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../org_alfresco_module_rm/action-context.xml | 8 + .../messages/actions.properties | 18 +- .../action/dm/DeclareRecordAction.java | 256 ++++++++++++++++++ 3 files changed, 281 insertions(+), 1 deletion(-) create mode 100644 rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/dm/DeclareRecordAction.java 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 From 3b0bb346f6e71aca1c879040523557cdb7eacc3f Mon Sep 17 00:00:00 2001 From: Tuna Aksoy Date: Fri, 2 Nov 2012 17:22:50 +0000 Subject: [PATCH 9/9] DeclareRecordAction: The actioned upon node should be a record no need to create a record from the actioned node. If it is not a record an error should be thrown. If a user wants to create and declare a file as record a composite rule can be used. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/DEV/INPLACE@43348 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../action/dm/DeclareRecordAction.java | 79 +++++-------------- 1 file changed, 21 insertions(+), 58 deletions(-) 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 index e63ac21907..8e1770192e 100644 --- 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 @@ -14,7 +14,6 @@ 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; @@ -32,6 +31,7 @@ import org.springframework.extensions.surf.util.I18NUtil; public class DeclareRecordAction extends ActionExecuterAbstractBase implements RecordsManagementModel { /** I18N */ + private static final String MSG_UNDECLARED_ONLY_RECORDS = "rm.action.undeclared-only-records"; private static final String MSG_NO_DECLARE_MAND_PROP = "rm.action.no-declare-mand-prop"; /** Logger */ @@ -96,68 +96,32 @@ public class DeclareRecordAction extends ActionExecuterAbstractBase implements R @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) + if (recordsManagementService.isRecord(actionedUponNodeRef) == true) { - // TODO we should use the file plan passed as a parameter - // grab the file plan - List filePlans = recordsManagementService.getFilePlans(); - if (filePlans.size() == 1) + if (recordService.isDeclared(actionedUponNodeRef) == false) { - // TODO parameterise the action with the file plan - final NodeRef filePlan = filePlans.get(0); + 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); - // 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"); + // remove all owner related rights + ownableService.setOwner(actionedUponNodeRef, OwnableService.NO_OWNER); } - if (nodeService.getProperty(actionedUponNodeRef, PROP_ORIGINATING_ORGANIZATION) == null) + else { - 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)); - } + throw new AlfrescoRuntimeException(buildMissingPropertiesErrorString(missingProperties)); } } - else - { - throw new AlfrescoRuntimeException("Unable to find file plan."); - } + } + else + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_UNDECLARED_ONLY_RECORDS, actionedUponNodeRef.toString())); } } @@ -249,8 +213,7 @@ public class DeclareRecordAction extends ActionExecuterAbstractBase implements R @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 parameters } } \ No newline at end of file