From c649c16345484825bb64467a004da88e33e5ec41 Mon Sep 17 00:00:00 2001 From: roxana Date: Mon, 31 Oct 2016 12:18:34 +0200 Subject: [PATCH 1/5] Update disposition schedule action for a record when linking or unlinking it. --- .../rm-service-context.xml | 1 + .../disposition/DispositionService.java | 8 ++++++++ .../disposition/DispositionServiceImpl.java | 20 +++++++++++++++++++ .../record/RecordServiceImpl.java | 12 ++++++++--- 4 files changed, 38 insertions(+), 3 deletions(-) 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 97f6a97480..81a2ad8478 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 @@ -142,6 +142,7 @@ org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService.updateDispositionActionDefinition=RM_ALLOW org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService.isNextDispositionActionEligible=RM.Read.0 org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService.getNextDispositionAction=RM.Read.0 + org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService.updateDispositionWhenLinkingOrUnlinking=RM.Read.0 org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService.getCompletedDispositionActions=RM.Read.0 org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService.getLastCompletedDispostionAction=RM.Read.0 org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService.isDisposableItemCutoff=RM.Read.0 diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionService.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionService.java index ed2ed79ecd..67e0ff072d 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionService.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionService.java @@ -264,4 +264,12 @@ public interface DispositionService * @return the initial disposition */ DispositionSchedule getOriginDispositionSchedule(NodeRef nodeRef); + + /** + * Updates disposition action step when linking or unlinking + * the given record to/from a record folder + * + * @param record + */ + void updateDispositionWhenLinkingOrUnlinking(NodeRef record); } diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImpl.java index 3a6e1d5beb..272a2beb7a 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImpl.java @@ -1094,6 +1094,26 @@ public class DispositionServiceImpl extends ServiceBaseImpl } return null; } + + public void updateDispositionWhenLinkingOrUnlinking(NodeRef record) + { + List recordFolders = recordFolderService.getRecordFolders(record); + + DispositionAction nextDispositionAction = getNextDispositionAction(record); + + NextActionFromDisposition dsNextAction = getNextDispositionAction(record, recordFolders, nextDispositionAction); + final NodeRef action = dsNextAction.getNextActionNodeRef(); + final Date dispositionActionDate = dsNextAction.getNextActionDateAsOf(); + AuthenticationUtil.runAsSystem(new RunAsWork() + { + @Override + public Void doWork() + { + nodeService.setProperty(action, PROP_DISPOSITION_AS_OF, dispositionActionDate); + return null; + } + }); + } /** * Helper method to determine if a node is frozen or has frozen children 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 a523ac93b6..c56cec4f50 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 @@ -34,14 +34,12 @@ import java.util.Set; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; -import org.alfresco.module.org_alfresco_module_rm.RecordsManagementServiceRegistry; import org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies.BeforeFileRecord; import org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies.OnFileRecord; 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.capability.RMPermissionModel; import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule; -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.dod5015.DOD5015Model; import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService; @@ -547,6 +545,8 @@ public class RecordServiceImpl extends BaseBehaviourBean // create and file the content as a record file(nodeRef); + // recalculate disposition schedule for the record when linking it + dispositionService.updateDispositionWhenLinkingOrUnlinking(nodeRef); } } } @@ -1695,7 +1695,7 @@ public class RecordServiceImpl extends BaseBehaviourBean // ensure we are linking a record to a record folder if(isRecord(record) && isRecordFolder(recordFolder)) { - // ensure that we are not linking a record to an exisiting location + // ensure that we are not linking a record to an existing location List parents = nodeService.getParentAssocs(record); for (ChildAssociationRef parent : parents) { @@ -1718,6 +1718,9 @@ public class RecordServiceImpl extends BaseBehaviourBean record, ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, name)); + + // recalculate disposition schedule for the record when linking it + dispositionService.updateDispositionWhenLinkingOrUnlinking(record); } else { @@ -1774,6 +1777,9 @@ public class RecordServiceImpl extends BaseBehaviourBean // remove the link nodeService.removeChild(recordFolder, record); + + // recalculate disposition schedule for record after unlinking it + dispositionService.updateDispositionWhenLinkingOrUnlinking(record); } else { From d22c941fceee9f9ad9394518e1e3828d2cfc47ac Mon Sep 17 00:00:00 2001 From: roxana Date: Mon, 31 Oct 2016 14:41:58 +0200 Subject: [PATCH 2/5] Renamed method for calculating next disposition step and added null check. --- .../rm-service-context.xml | 2 +- .../disposition/DispositionService.java | 2 +- .../disposition/DispositionServiceImpl.java | 25 +++++++++++-------- .../record/RecordServiceImpl.java | 6 ++--- 4 files changed, 19 insertions(+), 16 deletions(-) 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 81a2ad8478..6b39ea2065 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 @@ -142,7 +142,7 @@ org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService.updateDispositionActionDefinition=RM_ALLOW org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService.isNextDispositionActionEligible=RM.Read.0 org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService.getNextDispositionAction=RM.Read.0 - org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService.updateDispositionWhenLinkingOrUnlinking=RM.Read.0 + org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService.recalculateNextDispositionStep=RM.Read.0 org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService.getCompletedDispositionActions=RM.Read.0 org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService.getLastCompletedDispostionAction=RM.Read.0 org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService.isDisposableItemCutoff=RM.Read.0 diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionService.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionService.java index 67e0ff072d..a666b950c4 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionService.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionService.java @@ -271,5 +271,5 @@ public interface DispositionService * * @param record */ - void updateDispositionWhenLinkingOrUnlinking(NodeRef record); + void recalculateNextDispositionStep(NodeRef record); } diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImpl.java index 272a2beb7a..2dcd859cf4 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImpl.java @@ -1095,24 +1095,27 @@ public class DispositionServiceImpl extends ServiceBaseImpl return null; } - public void updateDispositionWhenLinkingOrUnlinking(NodeRef record) + public void recalculateNextDispositionStep(NodeRef record) { List recordFolders = recordFolderService.getRecordFolders(record); DispositionAction nextDispositionAction = getNextDispositionAction(record); - NextActionFromDisposition dsNextAction = getNextDispositionAction(record, recordFolders, nextDispositionAction); - final NodeRef action = dsNextAction.getNextActionNodeRef(); - final Date dispositionActionDate = dsNextAction.getNextActionDateAsOf(); - AuthenticationUtil.runAsSystem(new RunAsWork() + if (nextDispositionAction != null) { - @Override - public Void doWork() + NextActionFromDisposition dsNextAction = getNextDispositionAction(record, recordFolders, nextDispositionAction); + final NodeRef action = dsNextAction.getNextActionNodeRef(); + final Date dispositionActionDate = dsNextAction.getNextActionDateAsOf(); + AuthenticationUtil.runAsSystem(new RunAsWork() { - nodeService.setProperty(action, PROP_DISPOSITION_AS_OF, dispositionActionDate); - return null; - } - }); + @Override + public Void doWork() + { + nodeService.setProperty(action, PROP_DISPOSITION_AS_OF, dispositionActionDate); + return null; + } + }); + } } /** 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 c56cec4f50..22d4c95733 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 @@ -546,7 +546,7 @@ public class RecordServiceImpl extends BaseBehaviourBean // create and file the content as a record file(nodeRef); // recalculate disposition schedule for the record when linking it - dispositionService.updateDispositionWhenLinkingOrUnlinking(nodeRef); + dispositionService.recalculateNextDispositionStep(nodeRef); } } } @@ -1720,7 +1720,7 @@ public class RecordServiceImpl extends BaseBehaviourBean QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, name)); // recalculate disposition schedule for the record when linking it - dispositionService.updateDispositionWhenLinkingOrUnlinking(record); + dispositionService.recalculateNextDispositionStep(record); } else { @@ -1779,7 +1779,7 @@ public class RecordServiceImpl extends BaseBehaviourBean nodeService.removeChild(recordFolder, record); // recalculate disposition schedule for record after unlinking it - dispositionService.updateDispositionWhenLinkingOrUnlinking(record); + dispositionService.recalculateNextDispositionStep(record); } else { From 27e8110d3e21fc0d754457ce526ec259fdf056ba Mon Sep 17 00:00:00 2001 From: roxana Date: Mon, 31 Oct 2016 15:14:14 +0200 Subject: [PATCH 3/5] Added integration test for recalculating disposition for a record when unlinking it. --- .../disposition/MultipleSchedulesTest.java | 29 +++++++++++++++++++ .../UpdateNextDispositionActionTest.java | 2 -- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/MultipleSchedulesTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/MultipleSchedulesTest.java index f166c97a1f..26d119210d 100644 --- a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/MultipleSchedulesTest.java +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/MultipleSchedulesTest.java @@ -173,4 +173,33 @@ public class MultipleSchedulesTest extends BaseRMTestCase .from(() -> dispositionService.getNextDispositionAction(record).getAsOfDate()) .because("Record should follow largest rentention schedule period, which is one week."); } + + /** + * RM-4292 + *

+     * Given a record subject to a mixed disposition schedule
+     * When the record is unlinked from one of its secondary parents
+     * Then the next disposition action is recalculated.
+     * 
+ */ + public void testRecalculateDispositionWhenUnlinking() + { + test() + .given(() -> { + setUpFilePlan(); + // Create a record filed under category A and linked to category B. + record = fileFolderService.create(folderA, RECORD_NAME, ContentModel.TYPE_CONTENT).getNodeRef(); + recordService.link(record, folderB); + }) + .when(() -> { + // Cut off the record. + dispositionService.cutoffDisposableItem(record); + // Unlink the record from folder B. + recordService.unlink(record, folderB); + }) + .then() + .expect(true) + .from(() -> dispositionService.isNextDispositionActionEligible(record)) + .because("Destroy action should be available, as the record should follow its origin disposition schedule."); + } } diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/UpdateNextDispositionActionTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/UpdateNextDispositionActionTest.java index d8dbb0b078..66bf5513b8 100644 --- a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/UpdateNextDispositionActionTest.java +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/UpdateNextDispositionActionTest.java @@ -26,13 +26,11 @@ import static org.alfresco.util.GUID.generate; import java.io.Serializable; import java.util.Collections; -import java.util.Date; import java.util.HashMap; import java.util.Map; import org.alfresco.module.org_alfresco_module_rm.action.impl.CutOffAction; import org.alfresco.module.org_alfresco_module_rm.action.impl.DestroyAction; -import org.alfresco.module.org_alfresco_module_rm.action.impl.EditDispositionActionAsOfDateAction; import org.alfresco.module.org_alfresco_module_rm.action.impl.TransferAction; import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule; import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase; From 8eba41396b56ef4825b8fecba43eda45234e5e92 Mon Sep 17 00:00:00 2001 From: roxana Date: Mon, 31 Oct 2016 15:53:17 +0200 Subject: [PATCH 4/5] Added null check. --- .../disposition/DispositionServiceImpl.java | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImpl.java index 2dcd859cf4..907c6c2b90 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImpl.java @@ -1104,17 +1104,20 @@ public class DispositionServiceImpl extends ServiceBaseImpl if (nextDispositionAction != null) { NextActionFromDisposition dsNextAction = getNextDispositionAction(record, recordFolders, nextDispositionAction); - final NodeRef action = dsNextAction.getNextActionNodeRef(); - final Date dispositionActionDate = dsNextAction.getNextActionDateAsOf(); - AuthenticationUtil.runAsSystem(new RunAsWork() + if (dsNextAction != null) { - @Override - public Void doWork() + final NodeRef action = dsNextAction.getNextActionNodeRef(); + final Date dispositionActionDate = dsNextAction.getNextActionDateAsOf(); + AuthenticationUtil.runAsSystem(new RunAsWork() { - nodeService.setProperty(action, PROP_DISPOSITION_AS_OF, dispositionActionDate); - return null; - } - }); + @Override + public Void doWork() + { + nodeService.setProperty(action, PROP_DISPOSITION_AS_OF, dispositionActionDate); + return null; + } + }); + } } } From 76cd3109ea27d157e80f3bdeeef065a3b8a905a8 Mon Sep 17 00:00:00 2001 From: roxana Date: Tue, 1 Nov 2016 15:25:04 +0200 Subject: [PATCH 5/5] Changed the test a bit to check a more complex case. --- .../disposition/MultipleSchedulesTest.java | 35 ++++++++++++++++--- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/MultipleSchedulesTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/MultipleSchedulesTest.java index 26d119210d..fc23b3390a 100644 --- a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/MultipleSchedulesTest.java +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/MultipleSchedulesTest.java @@ -34,6 +34,10 @@ public class MultipleSchedulesTest extends BaseRMTestCase protected static final String CATEGORY_B_NAME = TEST_PREFIX + "CategoryB"; /** The name to use for the folder within the second category. */ protected static final String FOLDER_B_NAME = TEST_PREFIX + "FolderB"; + /** The name to use for the third category. */ + protected static final String CATEGORY_C_NAME = TEST_PREFIX + "CategoryC"; + /** The name to use for the folder within the third category. */ + protected static final String FOLDER_C_NAME = TEST_PREFIX + "FolderC"; /** The name to use for the record. */ protected static final String RECORD_NAME = TEST_PREFIX + "Record"; @@ -48,6 +52,10 @@ public class MultipleSchedulesTest extends BaseRMTestCase private NodeRef categoryB; /** The folder node within the second category. */ private NodeRef folderB; + /** The third category node. */ + private NodeRef categoryC; + /** The folder node within the third category. */ + private NodeRef folderC; /** The record node. */ private NodeRef record; @@ -82,6 +90,7 @@ public class MultipleSchedulesTest extends BaseRMTestCase // Create two categories. categoryA = filePlanService.createRecordCategory(filePlan, CATEGORY_A_NAME); categoryB = filePlanService.createRecordCategory(filePlan, CATEGORY_B_NAME); + categoryC = filePlanService.createRecordCategory(filePlan, CATEGORY_C_NAME); // Create a disposition schedule for category A (Cut off immediately, then Destroy immediately). DispositionSchedule dispSchedA = utils.createBasicDispositionSchedule(categoryA, "instructions", "authority", true, false); Map cutOffParamsA = ImmutableMap.of(PROP_DISPOSITION_ACTION_NAME, CutOffAction.NAME, @@ -103,9 +112,21 @@ public class MultipleSchedulesTest extends BaseRMTestCase PROP_DISPOSITION_PERIOD, CommonRMTestUtils.PERIOD_ONE_WEEK, PROP_DISPOSITION_PERIOD_PROPERTY, PROP_CUT_OFF_DATE); dispositionService.addDispositionActionDefinition(dispSchedB, destroyParamsB); + // Create a disposition schedule for category C (Cut off immediately, then Destroy one year after cutoff). + DispositionSchedule dispSchedC = utils.createBasicDispositionSchedule(categoryC, "instructions", "authority", true, false); + Map cutOffParamsC = ImmutableMap.of(PROP_DISPOSITION_ACTION_NAME, CutOffAction.NAME, + PROP_DISPOSITION_DESCRIPTION, "description", + PROP_DISPOSITION_PERIOD, CommonRMTestUtils.PERIOD_IMMEDIATELY); + dispositionService.addDispositionActionDefinition(dispSchedC, cutOffParamsC); + Map destroyParamsC = ImmutableMap.of(PROP_DISPOSITION_ACTION_NAME, DestroyAction.NAME, + PROP_DISPOSITION_DESCRIPTION, "description", + PROP_DISPOSITION_PERIOD, CommonRMTestUtils.PERIOD_ONE_YEAR, + PROP_DISPOSITION_PERIOD_PROPERTY, PROP_CUT_OFF_DATE); + dispositionService.addDispositionActionDefinition(dispSchedC, destroyParamsC); // Create a folder within each category. folderA = recordFolderService.createRecordFolder(categoryA, FOLDER_A_NAME); folderB = recordFolderService.createRecordFolder(categoryB, FOLDER_B_NAME); + folderC = recordFolderService.createRecordFolder(categoryC, FOLDER_C_NAME); } /** @@ -123,6 +144,7 @@ public class MultipleSchedulesTest extends BaseRMTestCase test() .given(() -> { setUpFilePlan(); + // Create a record filed under category A and linked to category B. record = fileFolderService.create(folderA, RECORD_NAME, ContentModel.TYPE_CONTENT).getNodeRef(); recordService.link(record, folderB); @@ -187,19 +209,22 @@ public class MultipleSchedulesTest extends BaseRMTestCase test() .given(() -> { setUpFilePlan(); - // Create a record filed under category A and linked to category B. + // Create a record filed under category A and linked to category B and C. record = fileFolderService.create(folderA, RECORD_NAME, ContentModel.TYPE_CONTENT).getNodeRef(); recordService.link(record, folderB); - }) - .when(() -> { + recordService.link(record, folderC); // Cut off the record. dispositionService.cutoffDisposableItem(record); + // Ensure the update has been applied to the record. + internalDispositionService.updateNextDispositionAction(record); + }) + .when(() -> { // Unlink the record from folder B. recordService.unlink(record, folderB); }) .then() - .expect(true) + .expect(false) .from(() -> dispositionService.isNextDispositionActionEligible(record)) - .because("Destroy action should be available, as the record should follow its origin disposition schedule."); + .because("Destroy action shouldn't be available, as the record should follow disposition schedule from category C."); } }