From 3be9d3718ce73f4609ef51726623fae8989e55da Mon Sep 17 00:00:00 2001 From: Roxana Lucanu-Ghetu Date: Fri, 17 Jun 2016 08:25:27 +0100 Subject: [PATCH 01/28] Merge branch 'feature-2.4/RM-3060' into 'release/V2.4' Feature 2.4/rm 3060 - Record linked to folder with the same disposition schedule couldn't be cut off Added search by name for current disposition action; when the record has multiple disposition schedules the current disposition action may not be found by id. See merge request !157 (cherry picked from commit 7345f55384cea4a1f958ce7565e8a31234ebbf13) --- .../UpdateNextDispositionActionTest.java | 148 ++++++++++++++++++ .../EditDispositionActionAsOfDateAction.java | 3 + .../disposition/DispositionServiceImpl.java | 7 + .../test/util/CommonRMTestUtils.java | 1 + 4 files changed, 159 insertions(+) create mode 100644 rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/UpdateNextDispositionActionTest.java diff --git a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/UpdateNextDispositionActionTest.java b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/UpdateNextDispositionActionTest.java new file mode 100644 index 0000000000..462198f5a7 --- /dev/null +++ b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/UpdateNextDispositionActionTest.java @@ -0,0 +1,148 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * 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 . + * #L% + */ +package org.alfresco.module.org_alfresco_module_rm.test.integration.disposition; + +import static org.alfresco.module.org_alfresco_module_rm.test.util.CommonRMTestUtils.DEFAULT_DISPOSITION_DESCRIPTION; +import static org.alfresco.module.org_alfresco_module_rm.test.util.CommonRMTestUtils.DEFAULT_DISPOSITION_INSTRUCTIONS; +import static org.alfresco.module.org_alfresco_module_rm.test.util.CommonRMTestUtils.DEFAULT_EVENT_NAME; +import static org.alfresco.module.org_alfresco_module_rm.test.util.CommonRMTestUtils.PERIOD_ONE_WEEK; +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; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; + +/** +* Update next disposition step integration tests. +* +* @author Roxana Lucanu +* @since 2.4.1 +*/ +public class UpdateNextDispositionActionTest extends BaseRMTestCase +{ + /** + * Given a record with multiple dispositions + * When updating the next step + * Then the action is available + *

+ * relates to https://issues.alfresco.com/jira/browse/RM-3060 + */ + public void testUpdateNextDispositionAction_RM3060() throws Exception + { + doBehaviourDrivenTest(new BehaviourDrivenTest() + { + NodeRef record; + NodeRef folder2; + + @Override + public void given() + { + // create category1 + NodeRef category1 = filePlanService.createRecordCategory(filePlan, generate()); + + // create disposition schedule for category1 + createDispositionSchedule(category1); + + // create category2 + NodeRef category2 = filePlanService.createRecordCategory(filePlan, generate()); + + // create disposition schedule for category2 + createDispositionSchedule(category2); + + // create folder2 inside category2 + folder2 = recordFolderService.createRecordFolder(category2, generate()); + + // create folder1 inside category1 + NodeRef folder1 = recordFolderService.createRecordFolder(category1, generate()); + + // create record inside folder1 + record = utils.createRecord(folder1, generate(), generate()); + + } + @Override + public void when() throws Exception + { + // link the record to folder2 + recordService.link(record, folder2); + + // complete record + utils.completeRecord(record); + + // set the disposition as of date to now on the record + rmActionService.executeRecordsManagementAction(record, + EditDispositionActionAsOfDateAction.NAME, + Collections.singletonMap(EditDispositionActionAsOfDateAction.PARAM_AS_OF_DATE, new Date())); + + // cut off + rmActionService.executeRecordsManagementAction(record, CutOffAction.NAME, null); + } + + @Override + public void then() throws Exception + { + assertTrue(nodeService.hasAspect(record, ASPECT_CUT_OFF)); + } + }); + } + + private void createDispositionSchedule(NodeRef category) + { + DispositionSchedule ds = utils.createDispositionSchedule(category, DEFAULT_DISPOSITION_INSTRUCTIONS, DEFAULT_DISPOSITION_DESCRIPTION, true, false, false); + + // create the properties for CUTOFF action and add it to the disposition action definition + Map cutOff = new HashMap(3); + cutOff.put(PROP_DISPOSITION_ACTION_NAME, CutOffAction.NAME); + cutOff.put(PROP_DISPOSITION_DESCRIPTION, generate()); + cutOff.put(PROP_DISPOSITION_PERIOD, PERIOD_ONE_WEEK); + dispositionService.addDispositionActionDefinition(ds, cutOff); + + // create the properties for TRANSFER action and add it to the disposition action definition + Map transfer = new HashMap(3); + transfer.put(PROP_DISPOSITION_ACTION_NAME, TransferAction.NAME); + transfer.put(PROP_DISPOSITION_DESCRIPTION, generate()); + transfer.put(PROP_DISPOSITION_EVENT, (Serializable)Collections.singletonList(DEFAULT_EVENT_NAME)); + dispositionService.addDispositionActionDefinition(ds, transfer); + + // create the properties for DESTROY action and add it to the disposition action definition + Map destroy = new HashMap(3); + destroy.put(PROP_DISPOSITION_ACTION_NAME, DestroyAction.NAME); + destroy.put(PROP_DISPOSITION_DESCRIPTION, generate()); + destroy.put(PROP_DISPOSITION_PERIOD, PERIOD_ONE_WEEK); + dispositionService.addDispositionActionDefinition(ds, destroy); + } +} \ No newline at end of file diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/EditDispositionActionAsOfDateAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/EditDispositionActionAsOfDateAction.java index 59976aa365..1d4ae1aba9 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/EditDispositionActionAsOfDateAction.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/EditDispositionActionAsOfDateAction.java @@ -38,6 +38,9 @@ public class EditDispositionActionAsOfDateAction extends RMActionExecuterAbstrac private static final String MSG_VALID_DATE_DISP_ASOF = "rm.action.valid-date-disp-asof"; private static final String MSG_DISP_ASOF_LIFECYCLE_APPLIED = "rm.action.disp-asof-lifecycle-applied"; + /** Action name */ + public static final String NAME = "editDispositionActionAsOfDate"; + /** Action parameters */ public static final String PARAM_AS_OF_DATE = "asOfDate"; 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 70f0a43957..75158f558d 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 @@ -880,6 +880,13 @@ public class DispositionServiceImpl extends ServiceBaseImpl // Get the current action String currentADId = (String) nodeService.getProperty(currentDispositionAction, PROP_DISPOSITION_ACTION_ID); currentDispositionActionDefinition = di.getDispositionActionDefinition(currentADId); + // When the record has multiple disposition schedules the current disposition action may not be found by id + // In this case it will be searched by name + if(currentDispositionActionDefinition == null) + { + String currentADName = (String) nodeService.getProperty(currentDispositionAction, PROP_DISPOSITION_ACTION); + currentDispositionActionDefinition = di.getDispositionActionDefinitionByName(currentADName); + } // Get the next disposition action int index = currentDispositionActionDefinition.getIndex(); diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/CommonRMTestUtils.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/CommonRMTestUtils.java index 3d66cb81bb..c8530a1ede 100644 --- a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/CommonRMTestUtils.java +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/CommonRMTestUtils.java @@ -76,6 +76,7 @@ public class CommonRMTestUtils implements RecordsManagementModel public static final String DEFAULT_EVENT_NAME = "case_closed"; public static final String PERIOD_NONE = "none|0"; public static final String PERIOD_IMMEDIATELY = "immediately|0"; + public static final String PERIOD_ONE_WEEK = "week|1"; /** * Constructor From 3d511fde4772d57aad5baaea93f4ad035a3981c4 Mon Sep 17 00:00:00 2001 From: roxana Date: Tue, 4 Oct 2016 10:34:08 +0300 Subject: [PATCH 02/28] Added implementation for handling multiple dispositions schedule record based. --- .../disposition/DispositionService.java | 11 + .../disposition/DispositionServiceImpl.java | 208 +++++++++++++++++- 2 files changed, 212 insertions(+), 7 deletions(-) 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 295397f1bd..b0e153b495 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 @@ -20,6 +20,7 @@ package org.alfresco.module.org_alfresco_module_rm.disposition; import java.io.Serializable; import java.util.Collection; +import java.util.Date; import java.util.List; import java.util.Map; @@ -232,4 +233,14 @@ public interface DispositionService * @param nodeRef node reference */ void refreshDispositionAction(NodeRef nodeRef); + + /** + * Gets date of the disposition action for the given + * disposition schedule with the given action name + * + * @param dispositionSchedule nodeRef + * @param dispositionActionName + * @return date + */ + Date getDispositionActionDate(NodeRef dispositionSchedule, String dispositionActionName); } 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 75158f558d..c4308d14ad 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 @@ -21,14 +21,19 @@ package org.alfresco.module.org_alfresco_module_rm.disposition; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; +import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.SortedSet; +import java.util.TreeSet; import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; import org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies; import org.alfresco.module.org_alfresco_module_rm.RecordsManagementServiceRegistry; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSelectionStrategy.DispositionableNodeRefComparator; import org.alfresco.module.org_alfresco_module_rm.disposition.property.DispositionProperty; import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEvent; import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanComponentKind; @@ -267,16 +272,37 @@ public class DispositionServiceImpl extends ServiceBaseImpl * @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService#getDispositionSchedule(org.alfresco.service.cmr.repository.NodeRef) */ @Override - public DispositionSchedule getDispositionSchedule(NodeRef nodeRef) + public DispositionSchedule getDispositionSchedule(final NodeRef nodeRef) { - DispositionSchedule di = null; + DispositionSchedule ds = null; NodeRef diNodeRef = null; if (isRecord(nodeRef)) { // Get the record folders for the record List recordFolders = recordFolderService.getRecordFolders(nodeRef); - // At this point, we may have disposition instruction objects from 1..n folders. - diNodeRef = dispositionSelectionStrategy.selectDispositionScheduleFrom(recordFolders); + + DispositionAction nextDispositionAction = getNextDispositionAction(nodeRef); + + // the record is not created yet or it doesn't have DS + if (nextDispositionAction == null) + { + // calculate nextAction to be the longest first action step + DispositionScheduleAndNextAction dsNodeRef = getNextDispositionActionFromParents(recordFolders); + if (dsNodeRef != null) + { + nextDispositionAction = dsNodeRef.getNextAction(); + diNodeRef = dsNodeRef.getDispositionNodeRef(); + } + } + else + { + diNodeRef = setRecordNextDispositionActionLatestDate(nodeRef, recordFolders, nextDispositionAction); + } + + if (nextDispositionAction == null) + { + return null; + } } else { @@ -286,12 +312,15 @@ public class DispositionServiceImpl extends ServiceBaseImpl if (diNodeRef != null) { - di = new DispositionScheduleImpl(serviceRegistry, nodeService, diNodeRef); + ds = new DispositionScheduleImpl(serviceRegistry, nodeService, diNodeRef); } - - return di; + + return ds; } + + + /** * This method returns a NodeRef * Gets the disposition instructions @@ -880,6 +909,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl // Get the current action String currentADId = (String) nodeService.getProperty(currentDispositionAction, PROP_DISPOSITION_ACTION_ID); currentDispositionActionDefinition = di.getDispositionActionDefinition(currentADId); + // When the record has multiple disposition schedules the current disposition action may not be found by id // In this case it will be searched by name if(currentDispositionActionDefinition == null) @@ -1030,6 +1060,31 @@ public class DispositionServiceImpl extends ServiceBaseImpl } } + public Date getDispositionActionDate(NodeRef dispositionSchedule, String dispositionActionName) + { + List assocs = nodeService.getChildAssocs(dispositionSchedule); + if (assocs != null && assocs.size() > 0) + { + for (ChildAssociationRef assoc : assocs) + { + if (assoc != null && assoc.getQName().getLocalName().contains(dispositionActionName)) + { + Date newAsOfDate = null; + Period dispositionPeriod = (Period) nodeService.getProperty(assoc.getChildRef(), PROP_DISPOSITION_PERIOD); + Date actionCreationDate = (Date)nodeService.getProperty(assoc.getChildRef(), ContentModel.PROP_CREATED); + + if (dispositionPeriod != null) + { + // calculate the new as of date as we have been provided a new period + newAsOfDate = dispositionPeriod.getNextDate(actionCreationDate); + } + return newAsOfDate; + } + } + } + return null;// throw exception? poate nu are actiuni cu numele dat ca param + } + /** * Helper method to determine if a node is frozen or has frozen children * @@ -1077,4 +1132,143 @@ public class DispositionServiceImpl extends ServiceBaseImpl } }); } + + class DispositionScheduleAndNextAction + { + private NodeRef dispositionNodeRef; + + private DispositionAction nextAction; + + public DispositionScheduleAndNextAction(NodeRef dispositionNodeRef, DispositionAction nextAction) + { + this.dispositionNodeRef = dispositionNodeRef; + this.nextAction = nextAction; + } + + public NodeRef getDispositionNodeRef() + { + return dispositionNodeRef; + } + + public void setDispositionNodeRef(NodeRef dispositionNodeRef) + { + this.dispositionNodeRef = dispositionNodeRef; + } + + public DispositionAction getNextAction() + { + return nextAction; + } + + public void setNextAction(DispositionAction nextAction) + { + this.nextAction = nextAction; + } + + } + + /** + * Get the longest period for the given action + * + * @param recordNodeRef + * @param recordFolders + * @param nextDispositionAction + * @return + */ + private NodeRef setRecordNextDispositionActionLatestDate(final NodeRef recordNodeRef, List recordFolders, DispositionAction nextDispositionAction) + { + String recordnextDispositionActionName = nextDispositionAction.getName(); + Date recordNextDispositionActionDate = nextDispositionAction.getAsOfDate(); + Date nextDispositionActionDate = null; + NodeRef dispositionNodeRef = null; + + for (NodeRef folder : recordFolders) + { + NodeRef dsNodeRef = getDispositionScheduleImpl(folder); + if (dsNodeRef != null) + { + Date dispActionDate = getDispositionActionDate(dsNodeRef, recordnextDispositionActionName); + if (nextDispositionActionDate == null || nextDispositionActionDate.before(dispActionDate)) + { + nextDispositionActionDate = dispActionDate; + dispositionNodeRef = dsNodeRef; + } + } + } + // set only on record creation or when linking + if (!nextDispositionActionDate.equals(recordNextDispositionActionDate) && recordNextDispositionActionDate.before(nextDispositionActionDate)) + { + final Date nextDADate = nextDispositionActionDate; + + AuthenticationUtil.runAsSystem(new RunAsWork() + { + @Override + public Void doWork() + { + nodeService.setProperty(recordNodeRef, PROP_DISPOSITION_AS_OF, nextDADate); + return null; + } + }); + } + return dispositionNodeRef; + } + + /** + * Calculate next disposition action for a record with the given parents + * + * @param recordFolders parent folders + * @return next disposition action and the disposition associated + */ + private DispositionScheduleAndNextAction getNextDispositionActionFromParents(List recordFolders) + { + NodeRef newAction = null; + String newDispositionActionName = null; + Date newDispositionActionDate = null; + NodeRef dispositionNodeRef = null; + for (NodeRef folder : recordFolders) + { + NodeRef folderDS = getDispositionScheduleImpl(folder); + if (folderDS != null) + { + List assocs = nodeService.getChildAssocs(folderDS); + if (assocs != null && assocs.size() > 0) + { + NodeRef firstAction = assocs.get(0).getChildRef(); + DispositionAction firstDispositionAction = new DispositionActionImpl(serviceRegistry, firstAction); + + if (newAction == null) + { + newAction = firstAction; + newDispositionActionName = (String)nodeService.getProperty(newAction, PROP_DISPOSITION_ACTION_NAME); + newDispositionActionDate = getDispositionActionDate(folderDS, newDispositionActionName); + dispositionNodeRef = folderDS; + } + else if (firstDispositionAction.getAsOfDate() != null && newDispositionActionDate.before(firstDispositionAction.getAsOfDate())) + { + newDispositionActionName = (String)nodeService.getProperty(firstAction, PROP_DISPOSITION_ACTION_NAME); + newDispositionActionDate = firstDispositionAction.getAsOfDate(); + dispositionNodeRef = folderDS; + } + } + } + } + if (newAction != null) { + final NodeRef action = newAction; + final String dispositionActionName = newDispositionActionName; + final Date dispositionActionDate = newDispositionActionDate; + AuthenticationUtil.runAsSystem(new RunAsWork() + { + @Override + public Void doWork() + { + nodeService.setProperty(action, PROP_DISPOSITION_ACTION_NAME, dispositionActionName); + nodeService.setProperty(action, PROP_DISPOSITION_AS_OF, dispositionActionDate); + return null; + } + }); + DispositionAction dsAction = new DispositionActionImpl(serviceRegistry, action); + return new DispositionScheduleAndNextAction(dispositionNodeRef, dsAction); + } + return null; + } } From 4163851b9dd13bd7cd3f2f6526b64eac7e50205d Mon Sep 17 00:00:00 2001 From: roxana Date: Fri, 7 Oct 2016 10:45:49 +0300 Subject: [PATCH 03/28] Changed a bit the methods to be more readable. --- .../disposition/DispositionServiceImpl.java | 281 ++++++++++-------- 1 file changed, 155 insertions(+), 126 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 c4308d14ad..22f3c6fc6f 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 @@ -21,19 +21,15 @@ package org.alfresco.module.org_alfresco_module_rm.disposition; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; -import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.SortedSet; -import java.util.TreeSet; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies; import org.alfresco.module.org_alfresco_module_rm.RecordsManagementServiceRegistry; -import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSelectionStrategy.DispositionableNodeRefComparator; import org.alfresco.module.org_alfresco_module_rm.disposition.property.DispositionProperty; import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEvent; import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanComponentKind; @@ -74,6 +70,8 @@ public class DispositionServiceImpl extends ServiceBaseImpl { /** Logger */ private static Log logger = LogFactory.getLog(DispositionServiceImpl.class); + + public enum WriteMode {ReadOnly, DateOnly, DateAndName}; /** Behaviour filter */ private BehaviourFilter behaviourFilter; @@ -275,44 +273,42 @@ public class DispositionServiceImpl extends ServiceBaseImpl public DispositionSchedule getDispositionSchedule(final NodeRef nodeRef) { DispositionSchedule ds = null; - NodeRef diNodeRef = null; + NodeRef dsNodeRef = null; if (isRecord(nodeRef)) { - // Get the record folders for the record - List recordFolders = recordFolderService.getRecordFolders(nodeRef); + final NextActionFromDisposition dsNextAction = getDispositionActionByNameForRecord(nodeRef); - DispositionAction nextDispositionAction = getNextDispositionAction(nodeRef); - - // the record is not created yet or it doesn't have DS - if (nextDispositionAction == null) + if (!dsNextAction.getWriteMode().equals(WriteMode.ReadOnly)) { - // calculate nextAction to be the longest first action step - DispositionScheduleAndNextAction dsNodeRef = getNextDispositionActionFromParents(recordFolders); - if (dsNodeRef != null) - { - nextDispositionAction = dsNodeRef.getNextAction(); - diNodeRef = dsNodeRef.getDispositionNodeRef(); - } + final NodeRef action = dsNextAction.getNextActionNodeRef(); + final String dispositionActionName = dsNextAction.getNextActionName(); + final Date dispositionActionDate = dsNextAction.getNextActionDateAsOf(); + + AuthenticationUtil.runAsSystem(new RunAsWork() + { + @Override + public Void doWork() + { + nodeService.setProperty(action, PROP_DISPOSITION_AS_OF, dispositionActionDate); + if (dsNextAction.getWriteMode().equals(WriteMode.DateAndName)) + { + nodeService.setProperty(action, PROP_DISPOSITION_ACTION_NAME, dispositionActionName); + } + return null; + } + }); } - else - { - diNodeRef = setRecordNextDispositionActionLatestDate(nodeRef, recordFolders, nextDispositionAction); - } - - if (nextDispositionAction == null) - { - return null; - } + dsNodeRef = dsNextAction.getDispositionNodeRef(); } else { // Get the disposition instructions for the node reference provided - diNodeRef = getDispositionScheduleImpl(nodeRef); + dsNodeRef = getDispositionScheduleImpl(nodeRef); } - if (diNodeRef != null) + if (dsNodeRef != null) { - ds = new DispositionScheduleImpl(serviceRegistry, nodeService, diNodeRef); + ds = new DispositionScheduleImpl(serviceRegistry, nodeService, dsNodeRef); } return ds; @@ -1082,7 +1078,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl } } } - return null;// throw exception? poate nu are actiuni cu numele dat ca param + return null; } /** @@ -1133,16 +1129,47 @@ public class DispositionServiceImpl extends ServiceBaseImpl }); } - class DispositionScheduleAndNextAction + class NextActionFromDisposition { + public NextActionFromDisposition(NodeRef dispositionNodeRef, NodeRef nextActionNodeRef, + String nextActionName, Date nextActionDateAsOf, WriteMode writeMode) + { + super(); + this.dispositionNodeRef = dispositionNodeRef; + this.nextActionNodeRef = nextActionNodeRef; + this.nextActionName = nextActionName; + this.nextActionDateAsOf = nextActionDateAsOf; + this.writeMode = writeMode; + } + private NodeRef dispositionNodeRef; - private DispositionAction nextAction; - - public DispositionScheduleAndNextAction(NodeRef dispositionNodeRef, DispositionAction nextAction) + private NodeRef nextActionNodeRef; + + private String nextActionName; + + private Date nextActionDateAsOf; + + private WriteMode writeMode; + + public WriteMode getWriteMode() { - this.dispositionNodeRef = dispositionNodeRef; - this.nextAction = nextAction; + return writeMode; + } + + public void setWriteMode(WriteMode writeMode) + { + this.writeMode = writeMode; + } + + public NodeRef getNextActionNodeRef() + { + return nextActionNodeRef; + } + + public void setNextActionNodeRef(NodeRef nextActionNodeRef) + { + this.nextActionNodeRef = nextActionNodeRef; } public NodeRef getDispositionNodeRef() @@ -1155,120 +1182,122 @@ public class DispositionServiceImpl extends ServiceBaseImpl this.dispositionNodeRef = dispositionNodeRef; } - public DispositionAction getNextAction() + public String getNextActionName() { - return nextAction; + return nextActionName; } - public void setNextAction(DispositionAction nextAction) + public void setNextActionName(String nextActionName) { - this.nextAction = nextAction; + this.nextActionName = nextActionName; + } + + public Date getNextActionDateAsOf() + { + return nextActionDateAsOf; + } + + public void setNextActionDateAsOf(Date nextActionDateAsOf) + { + this.nextActionDateAsOf = nextActionDateAsOf; } - } - /** - * Get the longest period for the given action - * - * @param recordNodeRef - * @param recordFolders - * @param nextDispositionAction - * @return - */ - private NodeRef setRecordNextDispositionActionLatestDate(final NodeRef recordNodeRef, List recordFolders, DispositionAction nextDispositionAction) + /** + * Calculate next disposition action for a record + * + * @param record + * @return next disposition action (name, date) and the disposition associated + */ + + protected NextActionFromDisposition getDispositionActionByNameForRecord(NodeRef record) { - String recordnextDispositionActionName = nextDispositionAction.getName(); - Date recordNextDispositionActionDate = nextDispositionAction.getAsOfDate(); - Date nextDispositionActionDate = null; - NodeRef dispositionNodeRef = null; + List recordFolders = recordFolderService.getRecordFolders(record); + DispositionAction nextDispositionAction = getNextDispositionAction(record); - for (NodeRef folder : recordFolders) + if (nextDispositionAction == null) { - NodeRef dsNodeRef = getDispositionScheduleImpl(folder); - if (dsNodeRef != null) + NodeRef newAction = null; + String newDispositionActionName = null; + Date newDispositionActionDateAsOf = null; + NodeRef dispositionNodeRef = null; + for (NodeRef folder : recordFolders) { - Date dispActionDate = getDispositionActionDate(dsNodeRef, recordnextDispositionActionName); - if (nextDispositionActionDate == null || nextDispositionActionDate.before(dispActionDate)) + NodeRef folderDS = getDispositionScheduleImpl(folder); + if (folderDS != null) { - nextDispositionActionDate = dispActionDate; - dispositionNodeRef = dsNodeRef; - } + List assocs = nodeService.getChildAssocs(folderDS); + if (assocs != null && assocs.size() > 0) + { + NodeRef firstAction = assocs.get(0).getChildRef(); + DispositionAction firstDispositionAction = new DispositionActionImpl(serviceRegistry, firstAction); + + if (newAction == null) + { + newAction = firstAction; + newDispositionActionName = (String)nodeService.getProperty(newAction, PROP_DISPOSITION_ACTION_NAME); + newDispositionActionDateAsOf = getDispositionActionDate(folderDS, newDispositionActionName); + } + else if (firstDispositionAction.getAsOfDate() != null && newDispositionActionDateAsOf.before(firstDispositionAction.getAsOfDate())) + { + newDispositionActionName = (String)nodeService.getProperty(firstAction, PROP_DISPOSITION_ACTION_NAME); + newDispositionActionDateAsOf = firstDispositionAction.getAsOfDate(); + } + dispositionNodeRef = folderDS; + } + } + } + if (newDispositionActionName != null && newDispositionActionDateAsOf != null + && dispositionNodeRef != null && newAction != null) + { + return new NextActionFromDisposition(dispositionNodeRef, newAction, + newDispositionActionName, newDispositionActionDateAsOf, WriteMode.DateAndName); + } + else + { + return null; } } - // set only on record creation or when linking - if (!nextDispositionActionDate.equals(recordNextDispositionActionDate) && recordNextDispositionActionDate.before(nextDispositionActionDate)) + else { - final Date nextDADate = nextDispositionActionDate; + // get the longest period for the given action name + String recordNextDispositionActionName = nextDispositionAction.getName(); + Date recordNextDispositionActionDate = nextDispositionAction.getAsOfDate(); + Date nextDispositionActionDate = null; + NodeRef dispositionNodeRef = null; - AuthenticationUtil.runAsSystem(new RunAsWork() + for (NodeRef folder : recordFolders) { - @Override - public Void doWork() + NodeRef dsNodeRef = getDispositionScheduleImpl(folder); + if (dsNodeRef != null) { - nodeService.setProperty(recordNodeRef, PROP_DISPOSITION_AS_OF, nextDADate); - return null; - } - }); - } - return dispositionNodeRef; - } - - /** - * Calculate next disposition action for a record with the given parents - * - * @param recordFolders parent folders - * @return next disposition action and the disposition associated - */ - private DispositionScheduleAndNextAction getNextDispositionActionFromParents(List recordFolders) - { - NodeRef newAction = null; - String newDispositionActionName = null; - Date newDispositionActionDate = null; - NodeRef dispositionNodeRef = null; - for (NodeRef folder : recordFolders) - { - NodeRef folderDS = getDispositionScheduleImpl(folder); - if (folderDS != null) - { - List assocs = nodeService.getChildAssocs(folderDS); - if (assocs != null && assocs.size() > 0) - { - NodeRef firstAction = assocs.get(0).getChildRef(); - DispositionAction firstDispositionAction = new DispositionActionImpl(serviceRegistry, firstAction); - - if (newAction == null) + Date dispActionDate = getDispositionActionDate(dsNodeRef, recordNextDispositionActionName); + if (nextDispositionActionDate == null || nextDispositionActionDate.before(dispActionDate)) { - newAction = firstAction; - newDispositionActionName = (String)nodeService.getProperty(newAction, PROP_DISPOSITION_ACTION_NAME); - newDispositionActionDate = getDispositionActionDate(folderDS, newDispositionActionName); - dispositionNodeRef = folderDS; - } - else if (firstDispositionAction.getAsOfDate() != null && newDispositionActionDate.before(firstDispositionAction.getAsOfDate())) - { - newDispositionActionName = (String)nodeService.getProperty(firstAction, PROP_DISPOSITION_ACTION_NAME); - newDispositionActionDate = firstDispositionAction.getAsOfDate(); - dispositionNodeRef = folderDS; + nextDispositionActionDate = dispActionDate; + dispositionNodeRef = dsNodeRef; } } } - } - if (newAction != null) { - final NodeRef action = newAction; - final String dispositionActionName = newDispositionActionName; - final Date dispositionActionDate = newDispositionActionDate; - AuthenticationUtil.runAsSystem(new RunAsWork() + if (nextDispositionActionDate != null && dispositionNodeRef != null) { - @Override - public Void doWork() + WriteMode mode = null; + if (!nextDispositionActionDate.equals(recordNextDispositionActionDate) + && recordNextDispositionActionDate.before(nextDispositionActionDate)) { - nodeService.setProperty(action, PROP_DISPOSITION_ACTION_NAME, dispositionActionName); - nodeService.setProperty(action, PROP_DISPOSITION_AS_OF, dispositionActionDate); - return null; + mode = WriteMode.DateOnly; } - }); - DispositionAction dsAction = new DispositionActionImpl(serviceRegistry, action); - return new DispositionScheduleAndNextAction(dispositionNodeRef, dsAction); + else + { + mode = WriteMode.ReadOnly; + } + return new NextActionFromDisposition(dispositionNodeRef, nextDispositionAction.getNodeRef(), + recordNextDispositionActionName, nextDispositionActionDate, mode); + } + else + { + return null; + } } - return null; } } From d98ae92dd5dc200596441b9a5177bfcca196140a Mon Sep 17 00:00:00 2001 From: roxana Date: Sun, 9 Oct 2016 20:50:28 +0300 Subject: [PATCH 04/28] Addresed code review comments. --- .../DispositionSelectionStrategy.java | 199 ------------- .../disposition/DispositionServiceImpl.java | 264 +++++++----------- .../NextActionFromDisposition.java | 80 ++++++ 3 files changed, 177 insertions(+), 366 deletions(-) delete mode 100644 rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionSelectionStrategy.java create mode 100644 rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/NextActionFromDisposition.java diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionSelectionStrategy.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionSelectionStrategy.java deleted file mode 100644 index d341c71295..0000000000 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionSelectionStrategy.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright (C) 2005-2014 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.disposition; - -import java.util.Comparator; -import java.util.Date; -import java.util.List; -import java.util.SortedSet; -import java.util.TreeSet; - -import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; -import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; -import org.alfresco.service.cmr.repository.NodeRef; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * This class offers the default implementation of a strategy for selection of - * disposition schedule for a record when there is more than one which is applicable. - * An example of where this strategy might be used would be in the case of a record - * which was multiply filed. - * - * @author neilm - */ -public class DispositionSelectionStrategy implements RecordsManagementModel -{ - /** Logger */ - private static Log logger = LogFactory.getLog(DispositionSelectionStrategy.class); - - /** Disposition service */ - private DispositionService dispositionService; - - /** - * Set the disposition service - * - * @param dispositionService disposition service - */ - public void setDispositionService(DispositionService dispositionService) - { - this.dispositionService = dispositionService; - } - - /** - * Select the disposition schedule to use given there is more than one - * - * @param recordFolders - * @return - */ - public NodeRef selectDispositionScheduleFrom(List recordFolders) - { - if (recordFolders == null || recordFolders.isEmpty()) - { - return null; - } - else - { - // 46 CHAPTER 2 - // Records assigned more than 1 disposition must be retained and linked to the record folder (category) with the longest - // retention period. - - // Assumption: an event-based disposition action has a longer retention - // period than a time-based one - as we cannot know when an event will occur - // TODO Automatic events? - - NodeRef recordFolder = null; - if (recordFolders.size() == 1) - { - recordFolder = recordFolders.get(0); - } - else - { - SortedSet sortedFolders = new TreeSet(new DispositionableNodeRefComparator()); - sortedFolders.addAll(recordFolders); - recordFolder = sortedFolders.first(); - } - - DispositionSchedule dispSchedule = dispositionService.getDispositionSchedule(recordFolder); - - if (logger.isDebugEnabled()) - { - logger.debug("Selected disposition schedule: " + dispSchedule); - } - - NodeRef result = null; - if (dispSchedule != null) - { - result = dispSchedule.getNodeRef(); - } - return result; - } - } - - /** - * This class defines a natural comparison order between NodeRefs that have - * the dispositionLifecycle aspect applied. - * This order has the following meaning: NodeRefs with a 'lesser' value are considered - * to have a shorter retention period, although the actual retention period may - * not be straightforwardly determined in all cases. - */ - class DispositionableNodeRefComparator implements Comparator - { - public int compare(final NodeRef f1, final NodeRef f2) - { - // Run as admin user - return AuthenticationUtil.runAs(new RunAsWork() - { - public Integer doWork() - { - return compareImpl(f1, f2); - } - }, AuthenticationUtil.getAdminUserName()); - } - - private int compareImpl(NodeRef f1, NodeRef f2) - { - // quick check to see if the node references are the same - if (f1.equals(f2)) - { - return 0; - } - - // get the disposition schedules for the folders - DispositionSchedule ds1 = dispositionService.getDispositionSchedule(f1); - DispositionSchedule ds2 = dispositionService.getDispositionSchedule(f2); - - // make sure each folder has a disposition schedule - if (ds1 == null && ds2 != null) - { - return 1; - } - else if (ds1 != null && ds2 == null) - { - return -1; - } - else if (ds1 == null && ds2 == null) - { - return 0; - } - - // TODO this won't work correctly if we are trying to compare schedules that are record based!! - DispositionAction da1 = dispositionService.getNextDispositionAction(f1); - DispositionAction da2 = dispositionService.getNextDispositionAction(f2); - - if (da1 != null && da2 != null) - { - Date asOfDate1 = da1.getAsOfDate(); - Date asOfDate2 = da2.getAsOfDate(); - // If both record(Folder)s have asOfDates, then use these to compare - if (asOfDate1 != null && asOfDate2 != null) - { - return asOfDate1.compareTo(asOfDate2); - } - // If one has a date and the other doesn't, the one with the date is "less". - // (Defined date is 'shorter' than undefined date as an undefined date means it may be retained forever - theoretically) - else if (asOfDate1 != null || asOfDate2 != null) - { - return asOfDate1 == null ? +1 : -1; - } - else - { - // Neither has an asOfDate. (Somewhat arbitrarily) we'll use the number of events to compare now. - DispositionActionDefinition dad1 = da1.getDispositionActionDefinition(); - DispositionActionDefinition dad2 = da2.getDispositionActionDefinition(); - int eventsCount1 = 0; - int eventsCount2 = 0; - - if (dad1 != null) - { - eventsCount1 = dad1.getEvents().size(); - } - if (dad2 != null) - { - eventsCount2 = dad2.getEvents().size(); - } - return Integer.valueOf(eventsCount1).compareTo(eventsCount2); - } - } - - return 0; - } - } -} \ No newline at end of file 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 22f3c6fc6f..79e86ec705 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 @@ -71,7 +71,8 @@ public class DispositionServiceImpl extends ServiceBaseImpl /** Logger */ private static Log logger = LogFactory.getLog(DispositionServiceImpl.class); - public enum WriteMode {ReadOnly, DateOnly, DateAndName}; + /** Transaction mode for setting next action */ + public enum WriteMode {READ_ONLY, DATE_ONLY, DATE_AND_NAME}; /** Behaviour filter */ private BehaviourFilter behaviourFilter; @@ -79,9 +80,6 @@ public class DispositionServiceImpl extends ServiceBaseImpl /** Records management service registry */ private RecordsManagementServiceRegistry serviceRegistry; - /** Disposition selection strategy */ - private DispositionSelectionStrategy dispositionSelectionStrategy; - /** File plan service */ private FilePlanService filePlanService; @@ -171,16 +169,6 @@ public class DispositionServiceImpl extends ServiceBaseImpl this.freezeService = freezeService; } - /** - * Set the dispositionSelectionStrategy bean. - * - * @param dispositionSelectionStrategy - */ - public void setDispositionSelectionStrategy(DispositionSelectionStrategy dispositionSelectionStrategy) - { - this.dispositionSelectionStrategy = dispositionSelectionStrategy; - } - /** * Behavior to initialize the disposition schedule of a newly filed record. * @@ -278,7 +266,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl { final NextActionFromDisposition dsNextAction = getDispositionActionByNameForRecord(nodeRef); - if (!dsNextAction.getWriteMode().equals(WriteMode.ReadOnly)) + if (dsNextAction != null && !dsNextAction.getWriteMode().equals(WriteMode.READ_ONLY)) { final NodeRef action = dsNextAction.getNextActionNodeRef(); final String dispositionActionName = dsNextAction.getNextActionName(); @@ -290,7 +278,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl public Void doWork() { nodeService.setProperty(action, PROP_DISPOSITION_AS_OF, dispositionActionDate); - if (dsNextAction.getWriteMode().equals(WriteMode.DateAndName)) + if (dsNextAction.getWriteMode().equals(WriteMode.DATE_AND_NAME)) { nodeService.setProperty(action, PROP_DISPOSITION_ACTION_NAME, dispositionActionName); } @@ -1129,79 +1117,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl }); } - class NextActionFromDisposition - { - public NextActionFromDisposition(NodeRef dispositionNodeRef, NodeRef nextActionNodeRef, - String nextActionName, Date nextActionDateAsOf, WriteMode writeMode) - { - super(); - this.dispositionNodeRef = dispositionNodeRef; - this.nextActionNodeRef = nextActionNodeRef; - this.nextActionName = nextActionName; - this.nextActionDateAsOf = nextActionDateAsOf; - this.writeMode = writeMode; - } - - private NodeRef dispositionNodeRef; - - private NodeRef nextActionNodeRef; - - private String nextActionName; - - private Date nextActionDateAsOf; - - private WriteMode writeMode; - - public WriteMode getWriteMode() - { - return writeMode; - } - - public void setWriteMode(WriteMode writeMode) - { - this.writeMode = writeMode; - } - - public NodeRef getNextActionNodeRef() - { - return nextActionNodeRef; - } - - public void setNextActionNodeRef(NodeRef nextActionNodeRef) - { - this.nextActionNodeRef = nextActionNodeRef; - } - - public NodeRef getDispositionNodeRef() - { - return dispositionNodeRef; - } - - public void setDispositionNodeRef(NodeRef dispositionNodeRef) - { - this.dispositionNodeRef = dispositionNodeRef; - } - - public String getNextActionName() - { - return nextActionName; - } - - public void setNextActionName(String nextActionName) - { - this.nextActionName = nextActionName; - } - - public Date getNextActionDateAsOf() - { - return nextActionDateAsOf; - } - - public void setNextActionDateAsOf(Date nextActionDateAsOf) - { - this.nextActionDateAsOf = nextActionDateAsOf; - } - } + /** * Calculate next disposition action for a record @@ -1217,87 +1133,101 @@ public class DispositionServiceImpl extends ServiceBaseImpl if (nextDispositionAction == null) { - NodeRef newAction = null; - String newDispositionActionName = null; - Date newDispositionActionDateAsOf = null; - NodeRef dispositionNodeRef = null; - for (NodeRef folder : recordFolders) - { - NodeRef folderDS = getDispositionScheduleImpl(folder); - if (folderDS != null) - { - List assocs = nodeService.getChildAssocs(folderDS); - if (assocs != null && assocs.size() > 0) - { - NodeRef firstAction = assocs.get(0).getChildRef(); - DispositionAction firstDispositionAction = new DispositionActionImpl(serviceRegistry, firstAction); - - if (newAction == null) - { - newAction = firstAction; - newDispositionActionName = (String)nodeService.getProperty(newAction, PROP_DISPOSITION_ACTION_NAME); - newDispositionActionDateAsOf = getDispositionActionDate(folderDS, newDispositionActionName); - } - else if (firstDispositionAction.getAsOfDate() != null && newDispositionActionDateAsOf.before(firstDispositionAction.getAsOfDate())) - { - newDispositionActionName = (String)nodeService.getProperty(firstAction, PROP_DISPOSITION_ACTION_NAME); - newDispositionActionDateAsOf = firstDispositionAction.getAsOfDate(); - } - dispositionNodeRef = folderDS; - } - } - } - if (newDispositionActionName != null && newDispositionActionDateAsOf != null - && dispositionNodeRef != null && newAction != null) - { - return new NextActionFromDisposition(dispositionNodeRef, newAction, - newDispositionActionName, newDispositionActionDateAsOf, WriteMode.DateAndName); - } - else - { - return null; - } + return getFirstDispositionAction(recordFolders); } else { - // get the longest period for the given action name - String recordNextDispositionActionName = nextDispositionAction.getName(); - Date recordNextDispositionActionDate = nextDispositionAction.getAsOfDate(); - Date nextDispositionActionDate = null; - NodeRef dispositionNodeRef = null; - - for (NodeRef folder : recordFolders) - { - NodeRef dsNodeRef = getDispositionScheduleImpl(folder); - if (dsNodeRef != null) - { - Date dispActionDate = getDispositionActionDate(dsNodeRef, recordNextDispositionActionName); - if (nextDispositionActionDate == null || nextDispositionActionDate.before(dispActionDate)) - { - nextDispositionActionDate = dispActionDate; - dispositionNodeRef = dsNodeRef; - } - } - } - if (nextDispositionActionDate != null && dispositionNodeRef != null) - { - WriteMode mode = null; - if (!nextDispositionActionDate.equals(recordNextDispositionActionDate) - && recordNextDispositionActionDate.before(nextDispositionActionDate)) - { - mode = WriteMode.DateOnly; - } - else - { - mode = WriteMode.ReadOnly; - } - return new NextActionFromDisposition(dispositionNodeRef, nextDispositionAction.getNodeRef(), - recordNextDispositionActionName, nextDispositionActionDate, mode); - } - else - { - return null; - } + return getNextDispositionAction(recordFolders, nextDispositionAction); } } + + /** + * Calculate next disposition action when the record already has one + * @param recordFolders + * @param nextDispositionAction + * @return next disposition action and the associated disposition schedule + */ + private NextActionFromDisposition getNextDispositionAction(List recordFolders, DispositionAction nextDispositionAction) + { + String recordNextDispositionActionName = nextDispositionAction.getName(); + Date recordNextDispositionActionDate = nextDispositionAction.getAsOfDate(); + Date nextDispositionActionDate = null; + NodeRef dispositionNodeRef = null; + + for (NodeRef folder : recordFolders) + { + NodeRef dsNodeRef = getDispositionScheduleImpl(folder); + if (dsNodeRef != null) + { + Date dispActionDate = getDispositionActionDate(dsNodeRef, recordNextDispositionActionName); + if (nextDispositionActionDate == null || nextDispositionActionDate.before(dispActionDate)) + { + nextDispositionActionDate = dispActionDate; + dispositionNodeRef = dsNodeRef; + } + } + } + if (nextDispositionActionDate == null || dispositionNodeRef == null) + { + return null; + } + WriteMode mode = null; + if (!nextDispositionActionDate.equals(recordNextDispositionActionDate) + && recordNextDispositionActionDate.before(nextDispositionActionDate)) + { + mode = WriteMode.DATE_ONLY; + } + else + { + mode = WriteMode.READ_ONLY; + } + return new NextActionFromDisposition(dispositionNodeRef, nextDispositionAction.getNodeRef(), + recordNextDispositionActionName, nextDispositionActionDate, mode); + } + + /** + * Calculate first disposition action when the record doesn't have one + * @param recordFolders + * @return next disposition action and the associated disposition schedule + */ + private NextActionFromDisposition getFirstDispositionAction(List recordFolders) + { + NodeRef newAction = null; + String newDispositionActionName = null; + Date newDispositionActionDateAsOf = null; + NodeRef dispositionNodeRef = null; + for (NodeRef folder : recordFolders) + { + NodeRef folderDS = getDispositionScheduleImpl(folder); + if (folderDS != null) + { + List assocs = nodeService.getChildAssocs(folderDS); + if (assocs != null && assocs.size() > 0) + { + NodeRef firstAction = assocs.get(0).getChildRef(); + DispositionAction firstDispositionAction = new DispositionActionImpl(serviceRegistry, firstAction); + + if (newAction == null) + { + newAction = firstAction; + newDispositionActionName = (String)nodeService.getProperty(newAction, PROP_DISPOSITION_ACTION_NAME); + newDispositionActionDateAsOf = getDispositionActionDate(folderDS, newDispositionActionName); + } + else if (firstDispositionAction.getAsOfDate() != null && newDispositionActionDateAsOf.before(firstDispositionAction.getAsOfDate())) + { + newDispositionActionName = (String)nodeService.getProperty(firstAction, PROP_DISPOSITION_ACTION_NAME); + newDispositionActionDateAsOf = firstDispositionAction.getAsOfDate(); + } + dispositionNodeRef = folderDS; + } + } + } + if (newDispositionActionName == null || newDispositionActionDateAsOf == null + || dispositionNodeRef == null || newAction == null) + { + return null; + } + return new NextActionFromDisposition(dispositionNodeRef, newAction, + newDispositionActionName, newDispositionActionDateAsOf, WriteMode.DATE_AND_NAME); + } } diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/NextActionFromDisposition.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/NextActionFromDisposition.java new file mode 100644 index 0000000000..377b0a23fd --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/NextActionFromDisposition.java @@ -0,0 +1,80 @@ +package org.alfresco.module.org_alfresco_module_rm.disposition; + +import java.util.Date; + +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionServiceImpl.WriteMode; +import org.alfresco.service.cmr.repository.NodeRef; + +public class NextActionFromDisposition +{ + public NextActionFromDisposition(NodeRef dispositionNodeRef, NodeRef nextActionNodeRef, String nextActionName, Date nextActionDateAsOf, + WriteMode writeMode) + { + super(); + this.dispositionNodeRef = dispositionNodeRef; + this.nextActionNodeRef = nextActionNodeRef; + this.nextActionName = nextActionName; + this.nextActionDateAsOf = nextActionDateAsOf; + this.writeMode = writeMode; + } + + private NodeRef dispositionNodeRef; + + private NodeRef nextActionNodeRef; + + private String nextActionName; + + private Date nextActionDateAsOf; + + private WriteMode writeMode; + + public WriteMode getWriteMode() + { + return writeMode; + } + + public void setWriteMode(WriteMode writeMode) + { + this.writeMode = writeMode; + } + + public NodeRef getNextActionNodeRef() + { + return nextActionNodeRef; + } + + public void setNextActionNodeRef(NodeRef nextActionNodeRef) + { + this.nextActionNodeRef = nextActionNodeRef; + } + + public NodeRef getDispositionNodeRef() + { + return dispositionNodeRef; + } + + public void setDispositionNodeRef(NodeRef dispositionNodeRef) + { + this.dispositionNodeRef = dispositionNodeRef; + } + + public String getNextActionName() + { + return nextActionName; + } + + public void setNextActionName(String nextActionName) + { + this.nextActionName = nextActionName; + } + + public Date getNextActionDateAsOf() + { + return nextActionDateAsOf; + } + + public void setNextActionDateAsOf(Date nextActionDateAsOf) + { + this.nextActionDateAsOf = nextActionDateAsOf; + } +} From 8ac88ca766c6556e0466665535da7323979f8f53 Mon Sep 17 00:00:00 2001 From: roxana Date: Sun, 9 Oct 2016 21:51:09 +0300 Subject: [PATCH 05/28] Removed disposition selection strategy. --- .../module/org_alfresco_module_rm/rm-service-context.xml | 9 --------- 1 file changed, 9 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 0ed6c73149..c94fdde3ae 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 @@ -39,12 +39,6 @@ - - - - - @@ -96,9 +90,6 @@ - - - From cb77ed71b5fa531ac1c899ebd40c67b28c32aecb Mon Sep 17 00:00:00 2001 From: Tom Page Date: Mon, 10 Oct 2016 14:45:06 +0100 Subject: [PATCH 06/28] RM-2526 Fix NPE found by integration tests. --- .../disposition/DispositionServiceImpl.java | 66 +++++++++---------- 1 file changed, 33 insertions(+), 33 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 141a2cc5f6..e6995670cf 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 @@ -70,7 +70,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl { /** Logger */ private static final Logger LOGGER = LoggerFactory.getLogger(DispositionServiceImpl.class); - + /** Transaction mode for setting next action */ public enum WriteMode {READ_ONLY, DATE_ONLY, DATE_AND_NAME}; @@ -265,28 +265,28 @@ public class DispositionServiceImpl extends ServiceBaseImpl if (isRecord(nodeRef)) { final NextActionFromDisposition dsNextAction = getDispositionActionByNameForRecord(nodeRef); - + if (dsNextAction != null && !dsNextAction.getWriteMode().equals(WriteMode.READ_ONLY)) { final NodeRef action = dsNextAction.getNextActionNodeRef(); final String dispositionActionName = dsNextAction.getNextActionName(); final Date dispositionActionDate = dsNextAction.getNextActionDateAsOf(); - - AuthenticationUtil.runAsSystem(new RunAsWork() + + AuthenticationUtil.runAsSystem(new RunAsWork() + { + @Override + public Void doWork() { - @Override - public Void doWork() + nodeService.setProperty(action, PROP_DISPOSITION_AS_OF, dispositionActionDate); + if (dsNextAction.getWriteMode().equals(WriteMode.DATE_AND_NAME)) { - nodeService.setProperty(action, PROP_DISPOSITION_AS_OF, dispositionActionDate); - if (dsNextAction.getWriteMode().equals(WriteMode.DATE_AND_NAME)) - { - nodeService.setProperty(action, PROP_DISPOSITION_ACTION_NAME, dispositionActionName); - } - return null; + nodeService.setProperty(action, PROP_DISPOSITION_ACTION_NAME, dispositionActionName); } - }); + return null; + } + }); + dsNodeRef = dsNextAction.getDispositionNodeRef(); } - dsNodeRef = dsNextAction.getDispositionNodeRef(); } else { @@ -298,12 +298,12 @@ public class DispositionServiceImpl extends ServiceBaseImpl { ds = new DispositionScheduleImpl(serviceRegistry, nodeService, dsNodeRef); } - + return ds; } - - + + /** * This method returns a NodeRef @@ -909,7 +909,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl // Get the current action String currentADId = (String) nodeService.getProperty(currentDispositionAction, PROP_DISPOSITION_ACTION_ID); currentDispositionActionDefinition = di.getDispositionActionDefinition(currentADId); - + // When the record has multiple disposition schedules the current disposition action may not be found by id // In this case it will be searched by name if(currentDispositionActionDefinition == null) @@ -1076,21 +1076,21 @@ public class DispositionServiceImpl extends ServiceBaseImpl } }); } - - - + + + /** * Calculate next disposition action for a record - * + * * @param record * @return next disposition action (name, date) and the disposition associated */ - - protected NextActionFromDisposition getDispositionActionByNameForRecord(NodeRef record) + + protected NextActionFromDisposition getDispositionActionByNameForRecord(NodeRef record) { List recordFolders = recordFolderService.getRecordFolders(record); DispositionAction nextDispositionAction = getNextDispositionAction(record); - + if (nextDispositionAction == null) { return getFirstDispositionAction(recordFolders); @@ -1113,7 +1113,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl Date recordNextDispositionActionDate = nextDispositionAction.getAsOfDate(); Date nextDispositionActionDate = null; NodeRef dispositionNodeRef = null; - + for (NodeRef folder : recordFolders) { NodeRef dsNodeRef = getDispositionScheduleImpl(folder); @@ -1132,7 +1132,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl return null; } WriteMode mode = null; - if (!nextDispositionActionDate.equals(recordNextDispositionActionDate) + if (!nextDispositionActionDate.equals(recordNextDispositionActionDate) && recordNextDispositionActionDate.before(nextDispositionActionDate)) { mode = WriteMode.DATE_ONLY; @@ -1141,7 +1141,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl { mode = WriteMode.READ_ONLY; } - return new NextActionFromDisposition(dispositionNodeRef, nextDispositionAction.getNodeRef(), + return new NextActionFromDisposition(dispositionNodeRef, nextDispositionAction.getNodeRef(), recordNextDispositionActionName, nextDispositionActionDate, mode); } @@ -1156,7 +1156,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl String newDispositionActionName = null; Date newDispositionActionDateAsOf = null; NodeRef dispositionNodeRef = null; - for (NodeRef folder : recordFolders) + for (NodeRef folder : recordFolders) { NodeRef folderDS = getDispositionScheduleImpl(folder); if (folderDS != null) @@ -1166,13 +1166,13 @@ public class DispositionServiceImpl extends ServiceBaseImpl { NodeRef firstAction = assocs.get(0).getChildRef(); DispositionAction firstDispositionAction = new DispositionActionImpl(serviceRegistry, firstAction); - + if (newAction == null) { newAction = firstAction; newDispositionActionName = (String)nodeService.getProperty(newAction, PROP_DISPOSITION_ACTION_NAME); newDispositionActionDateAsOf = getDispositionActionDate(folderDS, newDispositionActionName); - } + } else if (firstDispositionAction.getAsOfDate() != null && newDispositionActionDateAsOf.before(firstDispositionAction.getAsOfDate())) { newDispositionActionName = (String)nodeService.getProperty(firstAction, PROP_DISPOSITION_ACTION_NAME); @@ -1182,12 +1182,12 @@ public class DispositionServiceImpl extends ServiceBaseImpl } } } - if (newDispositionActionName == null || newDispositionActionDateAsOf == null + if (newDispositionActionName == null || newDispositionActionDateAsOf == null || dispositionNodeRef == null || newAction == null) { return null; } - return new NextActionFromDisposition(dispositionNodeRef, newAction, + return new NextActionFromDisposition(dispositionNodeRef, newAction, newDispositionActionName, newDispositionActionDateAsOf, WriteMode.DATE_AND_NAME); } } From 0144aa10bf4a3104dfa512e831e0a52bbf1fc3e3 Mon Sep 17 00:00:00 2001 From: roxana Date: Mon, 10 Oct 2016 16:50:21 +0300 Subject: [PATCH 07/28] Moved UpdateNextDispositionActionTest/ --- .../disposition/DispositionServiceImpl.java | 2 +- .../UpdateNextDispositionActionTest.java | 140 ++++++++++++++++++ 2 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/UpdateNextDispositionActionTest.java 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 79e86ec705..920bc24eb3 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 @@ -285,8 +285,8 @@ public class DispositionServiceImpl extends ServiceBaseImpl return null; } }); + dsNodeRef = dsNextAction.getDispositionNodeRef(); } - dsNodeRef = dsNextAction.getDispositionNodeRef(); } else { 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 new file mode 100644 index 0000000000..81e5043c9f --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/UpdateNextDispositionActionTest.java @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2005-2014 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.integration.disposition; + +import static org.alfresco.module.org_alfresco_module_rm.test.util.CommonRMTestUtils.DEFAULT_DISPOSITION_DESCRIPTION; +import static org.alfresco.module.org_alfresco_module_rm.test.util.CommonRMTestUtils.DEFAULT_DISPOSITION_INSTRUCTIONS; +import static org.alfresco.module.org_alfresco_module_rm.test.util.CommonRMTestUtils.DEFAULT_EVENT_NAME; +import static org.alfresco.module.org_alfresco_module_rm.test.util.CommonRMTestUtils.PERIOD_ONE_WEEK; +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; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; + +/** +* Update next disposition step integration tests. +* +* @author Roxana Lucanu +* @since 2.3.1 +*/ +public class UpdateNextDispositionActionTest extends BaseRMTestCase +{ + /** + * Given a record with multiple dispositions + * When updating the next step + * Then the action is available + *

+ * relates to https://issues.alfresco.com/jira/browse/RM-3060 + */ + public void testUpdateNextDispositionAction_RM3060() throws Exception + { + doBehaviourDrivenTest(new BehaviourDrivenTest() + { + NodeRef record; + NodeRef folder2; + + @Override + public void given() + { + // create category1 + NodeRef category1 = filePlanService.createRecordCategory(filePlan, generate()); + + // create disposition schedule for category1 + createDispositionSchedule(category1); + + // create category2 + NodeRef category2 = filePlanService.createRecordCategory(filePlan, generate()); + + // create disposition schedule for category2 + createDispositionSchedule(category2); + + // create folder2 inside category2 + folder2 = recordFolderService.createRecordFolder(category2, generate()); + + // create folder1 inside category1 + NodeRef folder1 = recordFolderService.createRecordFolder(category1, generate()); + + // create record inside folder1 + record = utils.createRecord(folder1, generate(), generate()); + + } + @Override + public void when() throws Exception + { + // link the record to folder2 + recordService.link(record, folder2); + + // complete record + utils.completeRecord(record); + + // set the disposition as of date to now on the record + rmActionService.executeRecordsManagementAction(record, + EditDispositionActionAsOfDateAction.NAME, + Collections.singletonMap(EditDispositionActionAsOfDateAction.PARAM_AS_OF_DATE, new Date())); + + // cut off + rmActionService.executeRecordsManagementAction(record, CutOffAction.NAME, null); + } + + @Override + public void then() throws Exception + { + assertTrue("Record " + record + " doesn't have the cutOff aspect.", nodeService.hasAspect(record, ASPECT_CUT_OFF)); + } + }); + } + + private void createDispositionSchedule(NodeRef category) + { + DispositionSchedule ds = utils.createDispositionSchedule(category, DEFAULT_DISPOSITION_INSTRUCTIONS, DEFAULT_DISPOSITION_DESCRIPTION, true, false, false); + + // create the properties for CUTOFF action and add it to the disposition action definition + Map cutOff = new HashMap(3); + cutOff.put(PROP_DISPOSITION_ACTION_NAME, CutOffAction.NAME); + cutOff.put(PROP_DISPOSITION_DESCRIPTION, generate()); + cutOff.put(PROP_DISPOSITION_PERIOD, PERIOD_ONE_WEEK); + dispositionService.addDispositionActionDefinition(ds, cutOff); + + // create the properties for TRANSFER action and add it to the disposition action definition + Map transfer = new HashMap(3); + transfer.put(PROP_DISPOSITION_ACTION_NAME, TransferAction.NAME); + transfer.put(PROP_DISPOSITION_DESCRIPTION, generate()); + transfer.put(PROP_DISPOSITION_EVENT, (Serializable)Collections.singletonList(DEFAULT_EVENT_NAME)); + dispositionService.addDispositionActionDefinition(ds, transfer); + + // create the properties for DESTROY action and add it to the disposition action definition + Map destroy = new HashMap(3); + destroy.put(PROP_DISPOSITION_ACTION_NAME, DestroyAction.NAME); + destroy.put(PROP_DISPOSITION_DESCRIPTION, generate()); + destroy.put(PROP_DISPOSITION_PERIOD, PERIOD_ONE_WEEK); + dispositionService.addDispositionActionDefinition(ds, destroy); + } +} \ No newline at end of file From d80be797b82b5d9e1cef571a407d8fc10a3399e8 Mon Sep 17 00:00:00 2001 From: Tom Page Date: Mon, 10 Oct 2016 15:20:45 +0100 Subject: [PATCH 08/28] RM-2526 Remove unwanted extra copy of cherry-picked test file. --- .../UpdateNextDispositionActionTest.java | 148 ------------------ 1 file changed, 148 deletions(-) delete mode 100644 rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/UpdateNextDispositionActionTest.java diff --git a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/UpdateNextDispositionActionTest.java b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/UpdateNextDispositionActionTest.java deleted file mode 100644 index 462198f5a7..0000000000 --- a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/UpdateNextDispositionActionTest.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * #%L - * Alfresco Records Management Module - * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited - * %% - * This file is part of the Alfresco software. - * - - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - - * 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 . - * #L% - */ -package org.alfresco.module.org_alfresco_module_rm.test.integration.disposition; - -import static org.alfresco.module.org_alfresco_module_rm.test.util.CommonRMTestUtils.DEFAULT_DISPOSITION_DESCRIPTION; -import static org.alfresco.module.org_alfresco_module_rm.test.util.CommonRMTestUtils.DEFAULT_DISPOSITION_INSTRUCTIONS; -import static org.alfresco.module.org_alfresco_module_rm.test.util.CommonRMTestUtils.DEFAULT_EVENT_NAME; -import static org.alfresco.module.org_alfresco_module_rm.test.util.CommonRMTestUtils.PERIOD_ONE_WEEK; -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; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.namespace.QName; - -/** -* Update next disposition step integration tests. -* -* @author Roxana Lucanu -* @since 2.4.1 -*/ -public class UpdateNextDispositionActionTest extends BaseRMTestCase -{ - /** - * Given a record with multiple dispositions - * When updating the next step - * Then the action is available - *

- * relates to https://issues.alfresco.com/jira/browse/RM-3060 - */ - public void testUpdateNextDispositionAction_RM3060() throws Exception - { - doBehaviourDrivenTest(new BehaviourDrivenTest() - { - NodeRef record; - NodeRef folder2; - - @Override - public void given() - { - // create category1 - NodeRef category1 = filePlanService.createRecordCategory(filePlan, generate()); - - // create disposition schedule for category1 - createDispositionSchedule(category1); - - // create category2 - NodeRef category2 = filePlanService.createRecordCategory(filePlan, generate()); - - // create disposition schedule for category2 - createDispositionSchedule(category2); - - // create folder2 inside category2 - folder2 = recordFolderService.createRecordFolder(category2, generate()); - - // create folder1 inside category1 - NodeRef folder1 = recordFolderService.createRecordFolder(category1, generate()); - - // create record inside folder1 - record = utils.createRecord(folder1, generate(), generate()); - - } - @Override - public void when() throws Exception - { - // link the record to folder2 - recordService.link(record, folder2); - - // complete record - utils.completeRecord(record); - - // set the disposition as of date to now on the record - rmActionService.executeRecordsManagementAction(record, - EditDispositionActionAsOfDateAction.NAME, - Collections.singletonMap(EditDispositionActionAsOfDateAction.PARAM_AS_OF_DATE, new Date())); - - // cut off - rmActionService.executeRecordsManagementAction(record, CutOffAction.NAME, null); - } - - @Override - public void then() throws Exception - { - assertTrue(nodeService.hasAspect(record, ASPECT_CUT_OFF)); - } - }); - } - - private void createDispositionSchedule(NodeRef category) - { - DispositionSchedule ds = utils.createDispositionSchedule(category, DEFAULT_DISPOSITION_INSTRUCTIONS, DEFAULT_DISPOSITION_DESCRIPTION, true, false, false); - - // create the properties for CUTOFF action and add it to the disposition action definition - Map cutOff = new HashMap(3); - cutOff.put(PROP_DISPOSITION_ACTION_NAME, CutOffAction.NAME); - cutOff.put(PROP_DISPOSITION_DESCRIPTION, generate()); - cutOff.put(PROP_DISPOSITION_PERIOD, PERIOD_ONE_WEEK); - dispositionService.addDispositionActionDefinition(ds, cutOff); - - // create the properties for TRANSFER action and add it to the disposition action definition - Map transfer = new HashMap(3); - transfer.put(PROP_DISPOSITION_ACTION_NAME, TransferAction.NAME); - transfer.put(PROP_DISPOSITION_DESCRIPTION, generate()); - transfer.put(PROP_DISPOSITION_EVENT, (Serializable)Collections.singletonList(DEFAULT_EVENT_NAME)); - dispositionService.addDispositionActionDefinition(ds, transfer); - - // create the properties for DESTROY action and add it to the disposition action definition - Map destroy = new HashMap(3); - destroy.put(PROP_DISPOSITION_ACTION_NAME, DestroyAction.NAME); - destroy.put(PROP_DISPOSITION_DESCRIPTION, generate()); - destroy.put(PROP_DISPOSITION_PERIOD, PERIOD_ONE_WEEK); - dispositionService.addDispositionActionDefinition(ds, destroy); - } -} \ No newline at end of file From dd52a516c8d728223060ab01ad323b1f4c42c21c Mon Sep 17 00:00:00 2001 From: Tom Page Date: Mon, 10 Oct 2016 15:29:38 +0100 Subject: [PATCH 09/28] RM-2526 Add cherry-picked test to test suite. Nb. The test currently fails at the moment. --- .../test/integration/disposition/DispositionTestSuite.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/DispositionTestSuite.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/DispositionTestSuite.java index e1938d1d28..a2bbacb70d 100644 --- a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/DispositionTestSuite.java +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/DispositionTestSuite.java @@ -32,7 +32,8 @@ import org.junit.runners.Suite.SuiteClasses; @SuiteClasses( { CutOffTest.class, - UpdateDispositionScheduleTest.class + UpdateDispositionScheduleTest.class, + UpdateNextDispositionActionTest.class }) public class DispositionTestSuite { From e109482ffc1cfa246eb852d09a0315309703c538 Mon Sep 17 00:00:00 2001 From: roxana Date: Tue, 11 Oct 2016 12:28:07 +0300 Subject: [PATCH 10/28] I've changed the method that calculates the as of date to take in consideration the record creation date instead of the action creation date. --- .../disposition/DispositionService.java | 3 ++- .../disposition/DispositionServiceImpl.java | 18 +++++++++--------- .../UpdateNextDispositionActionTest.java | 2 +- 3 files changed, 12 insertions(+), 11 deletions(-) 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 f5f6489cc8..4c0136733c 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 @@ -238,11 +238,12 @@ public interface DispositionService * Gets date of the disposition action for the given * disposition schedule with the given action name * + * @param record * @param dispositionSchedule nodeRef * @param dispositionActionName * @return date */ - Date getDispositionActionDate(NodeRef dispositionSchedule, String dispositionActionName); + Date getDispositionActionDate(NodeRef record, NodeRef dispositionSchedule, String dispositionActionName); /** * Compute the "disposition as of" date (if necessary) for a disposition action and a node. 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 e6995670cf..c18528b397 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 @@ -1004,7 +1004,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl } } - public Date getDispositionActionDate(NodeRef dispositionSchedule, String dispositionActionName) + public Date getDispositionActionDate(NodeRef record, NodeRef dispositionSchedule, String dispositionActionName) { List assocs = nodeService.getChildAssocs(dispositionSchedule); if (assocs != null && assocs.size() > 0) @@ -1015,12 +1015,12 @@ public class DispositionServiceImpl extends ServiceBaseImpl { Date newAsOfDate = null; Period dispositionPeriod = (Period) nodeService.getProperty(assoc.getChildRef(), PROP_DISPOSITION_PERIOD); - Date actionCreationDate = (Date)nodeService.getProperty(assoc.getChildRef(), ContentModel.PROP_CREATED); + Date recordCreationDate = (Date)nodeService.getProperty(record, ContentModel.PROP_CREATED); if (dispositionPeriod != null) { // calculate the new as of date as we have been provided a new period - newAsOfDate = dispositionPeriod.getNextDate(actionCreationDate); + newAsOfDate = dispositionPeriod.getNextDate(recordCreationDate); } return newAsOfDate; } @@ -1093,11 +1093,11 @@ public class DispositionServiceImpl extends ServiceBaseImpl if (nextDispositionAction == null) { - return getFirstDispositionAction(recordFolders); + return getFirstDispositionAction(record, recordFolders); } else { - return getNextDispositionAction(recordFolders, nextDispositionAction); + return getNextDispositionAction(record, recordFolders, nextDispositionAction); } } @@ -1107,7 +1107,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl * @param nextDispositionAction * @return next disposition action and the associated disposition schedule */ - private NextActionFromDisposition getNextDispositionAction(List recordFolders, DispositionAction nextDispositionAction) + private NextActionFromDisposition getNextDispositionAction(NodeRef record, List recordFolders, DispositionAction nextDispositionAction) { String recordNextDispositionActionName = nextDispositionAction.getName(); Date recordNextDispositionActionDate = nextDispositionAction.getAsOfDate(); @@ -1119,7 +1119,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl NodeRef dsNodeRef = getDispositionScheduleImpl(folder); if (dsNodeRef != null) { - Date dispActionDate = getDispositionActionDate(dsNodeRef, recordNextDispositionActionName); + Date dispActionDate = getDispositionActionDate(record, dsNodeRef, recordNextDispositionActionName); if (nextDispositionActionDate == null || nextDispositionActionDate.before(dispActionDate)) { nextDispositionActionDate = dispActionDate; @@ -1150,7 +1150,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl * @param recordFolders * @return next disposition action and the associated disposition schedule */ - private NextActionFromDisposition getFirstDispositionAction(List recordFolders) + private NextActionFromDisposition getFirstDispositionAction(NodeRef record, List recordFolders) { NodeRef newAction = null; String newDispositionActionName = null; @@ -1171,7 +1171,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl { newAction = firstAction; newDispositionActionName = (String)nodeService.getProperty(newAction, PROP_DISPOSITION_ACTION_NAME); - newDispositionActionDateAsOf = getDispositionActionDate(folderDS, newDispositionActionName); + newDispositionActionDateAsOf = getDispositionActionDate(record, folderDS, newDispositionActionName); } else if (firstDispositionAction.getAsOfDate() != null && newDispositionActionDateAsOf.before(firstDispositionAction.getAsOfDate())) { 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 81e5043c9f..b8d6dcb618 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 @@ -98,7 +98,7 @@ public class UpdateNextDispositionActionTest extends BaseRMTestCase // set the disposition as of date to now on the record rmActionService.executeRecordsManagementAction(record, EditDispositionActionAsOfDateAction.NAME, - Collections.singletonMap(EditDispositionActionAsOfDateAction.PARAM_AS_OF_DATE, new Date())); + Collections.singletonMap(EditDispositionActionAsOfDateAction.PARAM_AS_OF_DATE, (Serializable)new Date())); // cut off rmActionService.executeRecordsManagementAction(record, CutOffAction.NAME, null); From a3586737f07457ecfe62597027c8952d5bbf160f Mon Sep 17 00:00:00 2001 From: roxana Date: Wed, 12 Oct 2016 02:13:53 +0300 Subject: [PATCH 11/28] Changed calculating as of date --- .../disposition/DispositionServiceImpl.java | 57 ++++++++++--------- 1 file changed, 30 insertions(+), 27 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 c18528b397..317733d922 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 @@ -266,25 +266,28 @@ public class DispositionServiceImpl extends ServiceBaseImpl { final NextActionFromDisposition dsNextAction = getDispositionActionByNameForRecord(nodeRef); - if (dsNextAction != null && !dsNextAction.getWriteMode().equals(WriteMode.READ_ONLY)) + if (dsNextAction != null) { - final NodeRef action = dsNextAction.getNextActionNodeRef(); - final String dispositionActionName = dsNextAction.getNextActionName(); - final Date dispositionActionDate = dsNextAction.getNextActionDateAsOf(); - - AuthenticationUtil.runAsSystem(new RunAsWork() + if (!dsNextAction.getWriteMode().equals(WriteMode.READ_ONLY)) { - @Override - public Void doWork() + final NodeRef action = dsNextAction.getNextActionNodeRef(); + final String dispositionActionName = dsNextAction.getNextActionName(); + final Date dispositionActionDate = dsNextAction.getNextActionDateAsOf(); + + AuthenticationUtil.runAsSystem(new RunAsWork() { - nodeService.setProperty(action, PROP_DISPOSITION_AS_OF, dispositionActionDate); - if (dsNextAction.getWriteMode().equals(WriteMode.DATE_AND_NAME)) + @Override + public Void doWork() { - nodeService.setProperty(action, PROP_DISPOSITION_ACTION_NAME, dispositionActionName); + nodeService.setProperty(action, PROP_DISPOSITION_AS_OF, dispositionActionDate); + if (dsNextAction.getWriteMode().equals(WriteMode.DATE_AND_NAME)) + { + nodeService.setProperty(action, PROP_DISPOSITION_ACTION_NAME, dispositionActionName); + } + return null; } - return null; - } - }); + }); + } dsNodeRef = dsNextAction.getDispositionNodeRef(); } } @@ -696,9 +699,16 @@ public class DispositionServiceImpl extends ServiceBaseImpl } else { - // for now use 'NOW' as the default context date - // TODO set the default period property ... cut off date or last disposition date depending on context - contextDate = new Date(); + if (period.getPeriodType().equals("immediately")) + { + contextDate = (Date)nodeService.getProperty(nodeRef, ContentModel.PROP_CREATED); + } + else + { + // for now use 'NOW' as the default context date + // TODO set the default period property ... cut off date or last disposition date depending on context + contextDate = new Date(); + } } // Calculate the as of date @@ -1006,6 +1016,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl public Date getDispositionActionDate(NodeRef record, NodeRef dispositionSchedule, String dispositionActionName) { + DispositionSchedule ds = new DispositionScheduleImpl(serviceRegistry, nodeService, dispositionSchedule); List assocs = nodeService.getChildAssocs(dispositionSchedule); if (assocs != null && assocs.size() > 0) { @@ -1013,16 +1024,8 @@ public class DispositionServiceImpl extends ServiceBaseImpl { if (assoc != null && assoc.getQName().getLocalName().contains(dispositionActionName)) { - Date newAsOfDate = null; - Period dispositionPeriod = (Period) nodeService.getProperty(assoc.getChildRef(), PROP_DISPOSITION_PERIOD); - Date recordCreationDate = (Date)nodeService.getProperty(record, ContentModel.PROP_CREATED); - - if (dispositionPeriod != null) - { - // calculate the new as of date as we have been provided a new period - newAsOfDate = dispositionPeriod.getNextDate(recordCreationDate); - } - return newAsOfDate; + DispositionActionDefinition actionDefinition = ds.getDispositionActionDefinition(assoc.getChildRef().getId()); + return calculateAsOfDate(record, actionDefinition, true); } } } From e20aa4bf2399bbf7b1643c42457c979e7b6afa3e Mon Sep 17 00:00:00 2001 From: Tom Page Date: Fri, 14 Oct 2016 15:23:46 +0100 Subject: [PATCH 12/28] RM-3386 Persist the fact that an "as of" date has been manually set. If a "disposition as of" date is manually set using "Edit Retention Date" then don't update it when changing the disposition schedule steps. Use a new property to store the fact that the "disposition as of" date has been manually set. --- .../module/org_alfresco_module_rm/model/recordsModel.xml | 5 +++++ .../BroadcastDispositionActionDefinitionUpdateAction.java | 5 ++++- .../action/impl/EditDispositionActionAsOfDateAction.java | 1 + .../disposition/property/DispositionProperty.java | 8 +++++++- .../model/RecordsManagementModel.java | 2 ++ 5 files changed, 19 insertions(+), 2 deletions(-) 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 6ade285156..8cbdbed4ff 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 @@ -392,6 +392,11 @@ d:date false + + Manually Set Disposition Date Flag + d:boolean + false + Disposition Events Eligible d:boolean diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/BroadcastDispositionActionDefinitionUpdateAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/BroadcastDispositionActionDefinitionUpdateAction.java index cf262d2ca3..162510bb01 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/BroadcastDispositionActionDefinitionUpdateAction.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/BroadcastDispositionActionDefinitionUpdateAction.java @@ -18,6 +18,8 @@ */ package org.alfresco.module.org_alfresco_module_rm.action.impl; +import static org.apache.commons.lang3.BooleanUtils.isNotTrue; + import java.io.Serializable; import java.util.ArrayList; import java.util.Date; @@ -185,7 +187,8 @@ public class BroadcastDispositionActionDefinitionUpdateAction extends RMActionEx { // the change does effect the nextAction for this node // so go ahead and determine what needs updating - if (changedProps.contains(PROP_DISPOSITION_PERIOD)) + if (changedProps.contains(PROP_DISPOSITION_PERIOD) + && isNotTrue((Boolean) getNodeService().getProperty(nextAction.getNodeRef(), PROP_MANUALLY_SET_AS_OF))) { persistPeriodChanges(dispositionActionDef, nextAction); } diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/EditDispositionActionAsOfDateAction.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/EditDispositionActionAsOfDateAction.java index 59976aa365..5ce2bfb6b1 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/EditDispositionActionAsOfDateAction.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/EditDispositionActionAsOfDateAction.java @@ -62,6 +62,7 @@ public class EditDispositionActionAsOfDateAction extends RMActionExecuterAbstrac if (da != null) { getNodeService().setProperty(da.getNodeRef(), PROP_DISPOSITION_AS_OF, asOfDate); + getNodeService().setProperty(da.getNodeRef(), PROP_MANUALLY_SET_AS_OF, true); } } else diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/property/DispositionProperty.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/property/DispositionProperty.java index 642efc6265..02ef7aabff 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/property/DispositionProperty.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/property/DispositionProperty.java @@ -18,6 +18,8 @@ */ package org.alfresco.module.org_alfresco_module_rm.disposition.property; +import static org.apache.commons.lang3.BooleanUtils.isNotTrue; + import java.io.Serializable; import java.util.Date; import java.util.Map; @@ -211,7 +213,11 @@ public class DispositionProperty extends BaseBehaviourBean // update asOf date on the disposition action based on the new property value NodeRef daNodeRef = dispositionAction.getNodeRef(); - nodeService.setProperty(daNodeRef, PROP_DISPOSITION_AS_OF, updatedAsOf); + // Don't overwrite a manually set "disposition as of" date. + if (isNotTrue((Boolean) nodeService.getProperty(daNodeRef, PROP_MANUALLY_SET_AS_OF))) + { + nodeService.setProperty(daNodeRef, PROP_DISPOSITION_AS_OF, updatedAsOf); + } } } } 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 406282d648..d57cc2f818 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 @@ -149,6 +149,8 @@ public interface RecordsManagementModel extends RecordsManagementCustomModel QName PROP_DISPOSITION_ACTION_ID = QName.createQName(RM_URI, "dispositionActionId"); QName PROP_DISPOSITION_ACTION = QName.createQName(RM_URI, "dispositionAction"); QName PROP_DISPOSITION_AS_OF = QName.createQName(RM_URI, "dispositionAsOf"); + /** A flag indicating that the "disposition as of" date has been manually set and shouldn't be changed. */ + QName PROP_MANUALLY_SET_AS_OF = QName.createQName(RM_URI, "manuallySetAsOf"); QName PROP_DISPOSITION_EVENTS_ELIGIBLE = QName.createQName(RM_URI, "dispositionEventsEligible"); QName PROP_DISPOSITION_ACTION_STARTED_AT = QName.createQName(RM_URI, "dispositionActionStartedAt"); QName PROP_DISPOSITION_ACTION_STARTED_BY = QName.createQName(RM_URI, "dispositionActionStartedBy"); From 3dd736e279ad725a31a6ed4c698d6258689bb7ee Mon Sep 17 00:00:00 2001 From: roxana Date: Mon, 17 Oct 2016 11:01:22 +0300 Subject: [PATCH 13/28] Fixed tests (for disposition schedules with event based actions). --- .../disposition/DispositionServiceImpl.java | 66 ++++++++++++++----- .../UpdateNextDispositionActionTest.java | 8 +-- .../service/DispositionServiceImplTest.java | 2 +- 3 files changed, 51 insertions(+), 25 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 317733d922..f46a83d810 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 @@ -70,6 +70,8 @@ public class DispositionServiceImpl extends ServiceBaseImpl { /** Logger */ private static final Logger LOGGER = LoggerFactory.getLogger(DispositionServiceImpl.class); + + private static final String PERIOD_IMMEDIATELY = "immediately"; /** Transaction mode for setting next action */ public enum WriteMode {READ_ONLY, DATE_ONLY, DATE_AND_NAME}; @@ -636,8 +638,14 @@ public class DispositionServiceImpl extends ServiceBaseImpl * @param dispositionActionDefinition disposition action definition * @param allowContextFromAsOf true if the context date is allowed to be obtained from the disposition "as of" property. */ - private void initialiseDispositionAction(NodeRef nodeRef, DispositionActionDefinition dispositionActionDefinition, boolean allowContextFromAsOf) + private DispositionAction initialiseDispositionAction(NodeRef nodeRef, DispositionActionDefinition dispositionActionDefinition, boolean allowContextFromAsOf) { + List childAssocs = nodeService.getChildAssocs(nodeRef, ASSOC_NEXT_DISPOSITION_ACTION, ASSOC_NEXT_DISPOSITION_ACTION, 1, true); + if (childAssocs != null && childAssocs.size() > 0) + { + return new DispositionActionImpl(serviceRegistry, childAssocs.get(0).getChildRef()); + } + // Create the properties Map props = new HashMap(10); @@ -667,6 +675,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl // For every event create an entry on the action da.addEventCompletionDetails(event); } + return da; } /** @@ -699,7 +708,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl } else { - if (period.getPeriodType().equals("immediately")) + if (period.getPeriodType().equals(PERIOD_IMMEDIATELY)) { contextDate = (Date)nodeService.getProperty(nodeRef, ContentModel.PROP_CREATED); } @@ -1130,20 +1139,36 @@ public class DispositionServiceImpl extends ServiceBaseImpl } } } - if (nextDispositionActionDate == null || dispositionNodeRef == null) + if (dispositionNodeRef == null) { return null; } WriteMode mode = null; - if (!nextDispositionActionDate.equals(recordNextDispositionActionDate) - && recordNextDispositionActionDate.before(nextDispositionActionDate)) + if (recordNextDispositionActionDate != null) { - mode = WriteMode.DATE_ONLY; + if ((nextDispositionActionDate == null) + || (!nextDispositionActionDate.equals(recordNextDispositionActionDate) + && recordNextDispositionActionDate.before(nextDispositionActionDate))) + { + mode = WriteMode.DATE_ONLY; + } + else + { + mode = WriteMode.READ_ONLY; + } } else { - mode = WriteMode.READ_ONLY; + if (nextDispositionActionDate != null) + { + mode = WriteMode.DATE_ONLY; + } + else + { + mode = WriteMode.READ_ONLY; + } } + return new NextActionFromDisposition(dispositionNodeRef, nextDispositionAction.getNodeRef(), recordNextDispositionActionName, nextDispositionActionDate, mode); } @@ -1164,28 +1189,33 @@ public class DispositionServiceImpl extends ServiceBaseImpl NodeRef folderDS = getDispositionScheduleImpl(folder); if (folderDS != null) { - List assocs = nodeService.getChildAssocs(folderDS); - if (assocs != null && assocs.size() > 0) + DispositionSchedule ds = new DispositionScheduleImpl(serviceRegistry, nodeService, folderDS); + List dispositionActionDefinitions = ds.getDispositionActionDefinitions(); + + if (dispositionActionDefinitions != null && dispositionActionDefinitions.size() > 0) { - NodeRef firstAction = assocs.get(0).getChildRef(); - DispositionAction firstDispositionAction = new DispositionActionImpl(serviceRegistry, firstAction); + DispositionActionDefinition firstDispositionActionDef = dispositionActionDefinitions.get(0); if (newAction == null) { - newAction = firstAction; + DispositionAction firstDispositionAction = initialiseDispositionAction(record, firstDispositionActionDef, true); + newAction = firstDispositionAction.getNodeRef(); newDispositionActionName = (String)nodeService.getProperty(newAction, PROP_DISPOSITION_ACTION_NAME); - newDispositionActionDateAsOf = getDispositionActionDate(record, folderDS, newDispositionActionName); - } - else if (firstDispositionAction.getAsOfDate() != null && newDispositionActionDateAsOf.before(firstDispositionAction.getAsOfDate())) - { - newDispositionActionName = (String)nodeService.getProperty(firstAction, PROP_DISPOSITION_ACTION_NAME); newDispositionActionDateAsOf = firstDispositionAction.getAsOfDate(); } + else if (firstDispositionActionDef.getPeriod() != null) { + Date firstActionDate = calculateAsOfDate(record, firstDispositionActionDef, true); + if (newDispositionActionDateAsOf.before(firstActionDate)) + { + newDispositionActionName =firstDispositionActionDef.getName(); + newDispositionActionDateAsOf = firstActionDate; + } + } dispositionNodeRef = folderDS; } } } - if (newDispositionActionName == null || newDispositionActionDateAsOf == null + if (newDispositionActionName == null || dispositionNodeRef == null || newAction == null) { return null; 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 b8d6dcb618..d8dbb0b078 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 @@ -36,6 +36,7 @@ import org.alfresco.module.org_alfresco_module_rm.action.impl.EditDispositionAct 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; +import org.alfresco.module.org_alfresco_module_rm.test.util.CommonRMTestUtils; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.QName; @@ -95,11 +96,6 @@ public class UpdateNextDispositionActionTest extends BaseRMTestCase // complete record utils.completeRecord(record); - // set the disposition as of date to now on the record - rmActionService.executeRecordsManagementAction(record, - EditDispositionActionAsOfDateAction.NAME, - Collections.singletonMap(EditDispositionActionAsOfDateAction.PARAM_AS_OF_DATE, (Serializable)new Date())); - // cut off rmActionService.executeRecordsManagementAction(record, CutOffAction.NAME, null); } @@ -120,7 +116,7 @@ public class UpdateNextDispositionActionTest extends BaseRMTestCase Map cutOff = new HashMap(3); cutOff.put(PROP_DISPOSITION_ACTION_NAME, CutOffAction.NAME); cutOff.put(PROP_DISPOSITION_DESCRIPTION, generate()); - cutOff.put(PROP_DISPOSITION_PERIOD, PERIOD_ONE_WEEK); + cutOff.put(PROP_DISPOSITION_PERIOD, CommonRMTestUtils.PERIOD_IMMEDIATELY); dispositionService.addDispositionActionDefinition(ds, cutOff); // create the properties for TRANSFER action and add it to the disposition action definition diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/legacy/service/DispositionServiceImplTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/legacy/service/DispositionServiceImplTest.java index e3853bfc30..29ab764296 100644 --- a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/legacy/service/DispositionServiceImplTest.java +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/legacy/service/DispositionServiceImplTest.java @@ -657,7 +657,7 @@ public class DispositionServiceImplTest extends BaseRMTestCase checkDisposableItemChanged(mhRecordFolder42); checkDisposableItemChanged(record43); checkDisposableItemUnchanged(mhRecordFolder44); - checkDisposableItemUnchanged(record45);; + checkDisposableItemUnchanged(record45); } }); From a2909b41802a06d5473b6b30ca517e546133d1d0 Mon Sep 17 00:00:00 2001 From: Tom Page Date: Mon, 17 Oct 2016 15:27:43 +0100 Subject: [PATCH 14/28] RM-2526 Treat null as the largest date possible. If a disposition step has an "as of" date of null then it requires human interaction. Consequently there is no length of time we can wait before the step triggers - so we treat it as the latest step if there are multiple disposition schedules. --- .../disposition/DispositionServiceImpl.java | 110 +++++++++++------- 1 file changed, 69 insertions(+), 41 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 f46a83d810..7d841d2c49 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 @@ -39,6 +39,7 @@ 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.recordfolder.RecordFolderService; import org.alfresco.module.org_alfresco_module_rm.util.ServiceBaseImpl; +import org.alfresco.repo.dictionary.types.period.Immediately; import org.alfresco.repo.policy.BehaviourFilter; import org.alfresco.repo.policy.annotation.Behaviour; import org.alfresco.repo.policy.annotation.BehaviourBean; @@ -70,11 +71,20 @@ public class DispositionServiceImpl extends ServiceBaseImpl { /** Logger */ private static final Logger LOGGER = LoggerFactory.getLogger(DispositionServiceImpl.class); - - private static final String PERIOD_IMMEDIATELY = "immediately"; /** Transaction mode for setting next action */ - public enum WriteMode {READ_ONLY, DATE_ONLY, DATE_AND_NAME}; + public enum WriteMode + { + /** Do not update any data. */ + READ_ONLY, + /** Only set the "disposition as of" date. */ + DATE_ONLY, + /** + * Set the "disposition as of" date and the name of the next action. This only happens during the creation of a + * disposition schedule impl node under a record or folder. + */ + DATE_AND_NAME + }; /** Behaviour filter */ private BehaviourFilter behaviourFilter; @@ -645,7 +655,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl { return new DispositionActionImpl(serviceRegistry, childAssocs.get(0).getChildRef()); } - + // Create the properties Map props = new HashMap(10); @@ -708,7 +718,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl } else { - if (period.getPeriodType().equals(PERIOD_IMMEDIATELY)) + if (period.getPeriodType().equals(Immediately.PERIOD_TYPE)) { contextDate = (Date)nodeService.getProperty(nodeRef, ContentModel.PROP_CREATED); } @@ -1123,19 +1133,27 @@ public class DispositionServiceImpl extends ServiceBaseImpl { String recordNextDispositionActionName = nextDispositionAction.getName(); Date recordNextDispositionActionDate = nextDispositionAction.getAsOfDate(); - Date nextDispositionActionDate = null; + // We're looking for the latest date, so initially start with a very early one. + Date nextDispositionActionDate = new Date(Long.MIN_VALUE); NodeRef dispositionNodeRef = null; + // Find the latest "disposition as of" date from all the schedules this record is subject to. for (NodeRef folder : recordFolders) { NodeRef dsNodeRef = getDispositionScheduleImpl(folder); if (dsNodeRef != null) { Date dispActionDate = getDispositionActionDate(record, dsNodeRef, recordNextDispositionActionName); - if (nextDispositionActionDate == null || nextDispositionActionDate.before(dispActionDate)) + if (dispActionDate == null || (nextDispositionActionDate != null + && nextDispositionActionDate.before(dispActionDate))) { nextDispositionActionDate = dispActionDate; dispositionNodeRef = dsNodeRef; + if (dispActionDate == null) + { + // Treat null as the latest date possible (so stop searching further). + break; + } } } } @@ -1143,34 +1161,37 @@ public class DispositionServiceImpl extends ServiceBaseImpl { return null; } - WriteMode mode = null; - if (recordNextDispositionActionDate != null) + WriteMode mode = determineWriteMode(recordNextDispositionActionDate, nextDispositionActionDate); + + return new NextActionFromDisposition(dispositionNodeRef, nextDispositionAction.getNodeRef(), + recordNextDispositionActionName, nextDispositionActionDate, mode); + } + + /** + * Determine what should be updated for an existing disposition schedule impl. We only update the date if the + * existing date is earlier than the calculated one. + * + * @param recordNextDispositionActionDate The next action date found on the record node (or folder node). + * @param nextDispositionActionDate The next action date calculated from the current disposition schedule(s) + * affecting the node. + * @return READ_ONLY if nothing should be updated, or DATE_ONLY if the date needs updating. + */ + private WriteMode determineWriteMode(Date recordNextDispositionActionDate, Date nextDispositionActionDate) + { + // Treat null dates as being the latest possible date. + Date maxDate = new Date(Long.MAX_VALUE); + Date recordDate = (recordNextDispositionActionDate != null ? recordNextDispositionActionDate : maxDate); + Date calculatedDate = (nextDispositionActionDate != null ? recordNextDispositionActionDate : maxDate); + + // We only need to update the date if the current one is too early. + if (recordDate.before(calculatedDate)) { - if ((nextDispositionActionDate == null) - || (!nextDispositionActionDate.equals(recordNextDispositionActionDate) - && recordNextDispositionActionDate.before(nextDispositionActionDate))) - { - mode = WriteMode.DATE_ONLY; - } - else - { - mode = WriteMode.READ_ONLY; - } + return WriteMode.DATE_ONLY; } else { - if (nextDispositionActionDate != null) - { - mode = WriteMode.DATE_ONLY; - } - else - { - mode = WriteMode.READ_ONLY; - } + return WriteMode.READ_ONLY; } - - return new NextActionFromDisposition(dispositionNodeRef, nextDispositionAction.getNodeRef(), - recordNextDispositionActionName, nextDispositionActionDate, mode); } /** @@ -1182,7 +1203,8 @@ public class DispositionServiceImpl extends ServiceBaseImpl { NodeRef newAction = null; String newDispositionActionName = null; - Date newDispositionActionDateAsOf = null; + // We're looking for the latest date, so start with a very early one. + Date newDispositionActionDateAsOf = new Date(Long.MIN_VALUE); NodeRef dispositionNodeRef = null; for (NodeRef folder : recordFolders) { @@ -1191,10 +1213,11 @@ public class DispositionServiceImpl extends ServiceBaseImpl { DispositionSchedule ds = new DispositionScheduleImpl(serviceRegistry, nodeService, folderDS); List dispositionActionDefinitions = ds.getDispositionActionDefinitions(); - + if (dispositionActionDefinitions != null && dispositionActionDefinitions.size() > 0) { DispositionActionDefinition firstDispositionActionDef = dispositionActionDefinitions.get(0); + dispositionNodeRef = folderDS; if (newAction == null) { @@ -1203,20 +1226,25 @@ public class DispositionServiceImpl extends ServiceBaseImpl newDispositionActionName = (String)nodeService.getProperty(newAction, PROP_DISPOSITION_ACTION_NAME); newDispositionActionDateAsOf = firstDispositionAction.getAsOfDate(); } - else if (firstDispositionActionDef.getPeriod() != null) { + else if (firstDispositionActionDef.getPeriod() != null) + { Date firstActionDate = calculateAsOfDate(record, firstDispositionActionDef, true); - if (newDispositionActionDateAsOf.before(firstActionDate)) + if (firstActionDate == null || (newDispositionActionDateAsOf != null + && newDispositionActionDateAsOf.before(firstActionDate))) { - newDispositionActionName =firstDispositionActionDef.getName(); + newDispositionActionName = firstDispositionActionDef.getName(); newDispositionActionDateAsOf = firstActionDate; + if (firstActionDate == null) + { + // Treat null as the latest date possible, so there's no point searching further. + break; + } } } - dispositionNodeRef = folderDS; - } - } - } - if (newDispositionActionName == null - || dispositionNodeRef == null || newAction == null) + } + } + } + if (newDispositionActionName == null || dispositionNodeRef == null || newAction == null) { return null; } From 9ca060330fbb681d0829bf42b7aa647daba112b9 Mon Sep 17 00:00:00 2001 From: roxana Date: Mon, 17 Oct 2016 17:50:12 +0300 Subject: [PATCH 15/28] Added check for adding disposition lifecycle aspect. --- .../disposition/DispositionServiceImpl.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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 7d841d2c49..58198244e1 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 @@ -1221,7 +1221,12 @@ public class DispositionServiceImpl extends ServiceBaseImpl if (newAction == null) { - DispositionAction firstDispositionAction = initialiseDispositionAction(record, firstDispositionActionDef, true); + NodeRef recordOrFolder = record; + if (!ds.isRecordLevelDisposition()) + { + recordOrFolder = folder; + } + DispositionAction firstDispositionAction = initialiseDispositionAction(recordOrFolder, firstDispositionActionDef, true); newAction = firstDispositionAction.getNodeRef(); newDispositionActionName = (String)nodeService.getProperty(newAction, PROP_DISPOSITION_ACTION_NAME); newDispositionActionDateAsOf = firstDispositionAction.getAsOfDate(); From 9f30a7278509cd63e60f497cac36a54772fa4ed8 Mon Sep 17 00:00:00 2001 From: Tom Page Date: Mon, 17 Oct 2016 16:46:51 +0100 Subject: [PATCH 16/28] RM-2526 Fix copy/paste error. --- .../disposition/DispositionServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 58198244e1..332f06e6ba 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 @@ -1181,7 +1181,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl // Treat null dates as being the latest possible date. Date maxDate = new Date(Long.MAX_VALUE); Date recordDate = (recordNextDispositionActionDate != null ? recordNextDispositionActionDate : maxDate); - Date calculatedDate = (nextDispositionActionDate != null ? recordNextDispositionActionDate : maxDate); + Date calculatedDate = (nextDispositionActionDate != null ? nextDispositionActionDate : maxDate); // We only need to update the date if the current one is too early. if (recordDate.before(calculatedDate)) From 121967ed7eb0c4a48fe6e2bb52e8f97ae201829c Mon Sep 17 00:00:00 2001 From: roxana Date: Tue, 18 Oct 2016 10:57:08 +0300 Subject: [PATCH 17/28] Modified validation for link conditions to compare the original disposition of the record with the target one. --- .../rm-service-context.xml | 1 + .../disposition/DispositionService.java | 9 ++++++++ .../disposition/DispositionServiceImpl.java | 22 +++++++++++++++++++ .../record/RecordServiceImpl.java | 18 ++++++++++++++- 4 files changed, 49 insertions(+), 1 deletion(-) 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 2e2b192760..97f6a97480 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 @@ -130,6 +130,7 @@ org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService.registerDispositionProperty=RM_ALLOW org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService.getDispositionProperties=RM_ALLOW org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService.getDispositionSchedule=RM.Read.0 + org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService.getOriginDispositionSchedule=RM.Read.0 org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService.getAssociatedDispositionSchedule=RM.Read.0 org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService.getAssociatedRecordsManagementContainer=RM_ALLOW org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService.hasDisposableItems=RM_ALLOW 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 4c0136733c..ed2ed79ecd 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 @@ -255,4 +255,13 @@ public interface DispositionService */ Date calculateAsOfDate(NodeRef nodeRef, DispositionActionDefinition dispositionActionDefinition, boolean allowContextFromAsOf); + + /** + * Gets the origin disposition schedule for the record, not the calculated one + * in case of multiple dispositions applied to record + * + * @param nodeRef record + * @return the initial disposition + */ + DispositionSchedule getOriginDispositionSchedule(NodeRef nodeRef); } 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 332f06e6ba..3cce643ec3 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 @@ -341,6 +341,28 @@ public class DispositionServiceImpl extends ServiceBaseImpl } return result; } + + public DispositionSchedule getOriginDispositionSchedule(NodeRef nodeRef) + { + NodeRef parent = this.nodeService.getPrimaryParent(nodeRef).getParentRef(); + if (parent != null) + { + if (filePlanService.isRecordCategory(parent)) + { + NodeRef result = getAssociatedDispositionScheduleImpl(parent); + if (result == null) + { + return null; + } + return new DispositionScheduleImpl(serviceRegistry, nodeService, result); + } + else + { + return getOriginDispositionSchedule(parent); + } + } + return null; + } /** * @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService#getAssociatedDispositionSchedule(org.alfresco.service.cmr.repository.NodeRef) 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 f4fa00e1d0..0a256eea17 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,12 +34,14 @@ 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; @@ -228,6 +230,9 @@ public class RecordServiceImpl extends BaseBehaviourBean /** recordable version service */ private RecordableVersionService recordableVersionService; + + /** Records management service registry */ + private RecordsManagementServiceRegistry serviceRegistry; /** list of available record meta-data aspects and the file plan types the are applicable to */ private Map> recordMetaDataAspects; @@ -385,6 +390,14 @@ public class RecordServiceImpl extends BaseBehaviourBean { this.recordableVersionService = recordableVersionService; } + + /** + * @param serviceRegistry records management registry service + */ + public void setRecordsManagementServiceRegistry(RecordsManagementServiceRegistry serviceRegistry) + { + this.serviceRegistry = serviceRegistry; + } /** * Init method @@ -1732,7 +1745,10 @@ public class RecordServiceImpl extends BaseBehaviourBean private void validateLinkConditions(NodeRef record, NodeRef recordFolder) { // ensure that the linking record folders have compatible disposition schedules - DispositionSchedule recordDispositionSchedule = dispositionService.getDispositionSchedule(record); + + // get the origin disposition schedule for the record, not the calculated one + DispositionSchedule recordDispositionSchedule = dispositionService.getOriginDispositionSchedule(record); + if (recordDispositionSchedule != null) { DispositionSchedule recordFolderDispositionSchedule = dispositionService.getDispositionSchedule(recordFolder); From dc62e20d31bf1740861cc84094c3298f87a12b61 Mon Sep 17 00:00:00 2001 From: roxana Date: Tue, 18 Oct 2016 11:49:15 +0300 Subject: [PATCH 18/28] Fixed unit test for linking. --- .../record/RecordServiceImpl.java | 11 ----------- .../record/RecordServiceImplUnitTest.java | 2 +- 2 files changed, 1 insertion(+), 12 deletions(-) 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 0a256eea17..a523ac93b6 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 @@ -231,9 +231,6 @@ public class RecordServiceImpl extends BaseBehaviourBean /** recordable version service */ private RecordableVersionService recordableVersionService; - /** Records management service registry */ - private RecordsManagementServiceRegistry serviceRegistry; - /** list of available record meta-data aspects and the file plan types the are applicable to */ private Map> recordMetaDataAspects; @@ -391,14 +388,6 @@ public class RecordServiceImpl extends BaseBehaviourBean this.recordableVersionService = recordableVersionService; } - /** - * @param serviceRegistry records management registry service - */ - public void setRecordsManagementServiceRegistry(RecordsManagementServiceRegistry serviceRegistry) - { - this.serviceRegistry = serviceRegistry; - } - /** * Init method */ diff --git a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImplUnitTest.java b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImplUnitTest.java index 76b0f0cf11..38e0a3e4f6 100755 --- a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImplUnitTest.java +++ b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImplUnitTest.java @@ -263,7 +263,7 @@ public class RecordServiceImplUnitTest extends BaseUnitTest DispositionSchedule recordDispositionSchedule = mock(DispositionSchedule.class); when(recordDispositionSchedule.isRecordLevelDisposition()) .thenReturn(true); - when(mockedDispositionService.getDispositionSchedule(record)) + when(mockedDispositionService.getOriginDispositionSchedule(record)) .thenReturn(recordDispositionSchedule); DispositionSchedule recordFolderDispositionSchedule = mock(DispositionSchedule.class); From a0e5ed3693bdf9384511cdc56a889c9d6c625f56 Mon Sep 17 00:00:00 2001 From: Tom Page Date: Tue, 18 Oct 2016 11:29:10 +0100 Subject: [PATCH 19/28] RM-2526 Fix license headers. --- .../NextActionFromDisposition.java | 26 +++++++++++++++++++ .../UpdateNextDispositionActionTest.java | 20 +++++++++----- 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/NextActionFromDisposition.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/NextActionFromDisposition.java index 377b0a23fd..3054d39a09 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/NextActionFromDisposition.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/NextActionFromDisposition.java @@ -1,3 +1,29 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * 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 . + * #L% + */ package org.alfresco.module.org_alfresco_module_rm.disposition; import java.util.Date; diff --git a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/UpdateNextDispositionActionTest.java b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/UpdateNextDispositionActionTest.java index d8dbb0b078..355d9c7eef 100644 --- a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/UpdateNextDispositionActionTest.java +++ b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/UpdateNextDispositionActionTest.java @@ -1,20 +1,28 @@ /* - * Copyright (C) 2005-2014 Alfresco Software Limited. - * - * This file is part of Alfresco - * + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - * 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 . + * #L% */ package org.alfresco.module.org_alfresco_module_rm.test.integration.disposition; From e664784ce5ee6fbcd42c68391185690fd7a33852 Mon Sep 17 00:00:00 2001 From: Tom Page Date: Thu, 20 Oct 2016 11:14:16 +0100 Subject: [PATCH 20/28] RM-2526 Integration test for linking record to longer schedule. --- .../disposition/DispositionTestSuite.java | 1 + .../disposition/MultipleSchedulesTest.java | 138 ++++++++++++++++++ 2 files changed, 139 insertions(+) create mode 100644 rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/MultipleSchedulesTest.java diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/DispositionTestSuite.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/DispositionTestSuite.java index a2bbacb70d..913b961fc1 100644 --- a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/DispositionTestSuite.java +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/DispositionTestSuite.java @@ -32,6 +32,7 @@ import org.junit.runners.Suite.SuiteClasses; @SuiteClasses( { CutOffTest.class, + MultipleSchedulesTest.class, UpdateDispositionScheduleTest.class, UpdateNextDispositionActionTest.class }) 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 new file mode 100644 index 0000000000..67bf854533 --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/MultipleSchedulesTest.java @@ -0,0 +1,138 @@ +package org.alfresco.module.org_alfresco_module_rm.test.integration.disposition; + +import static org.alfresco.module.org_alfresco_module_rm.test.util.bdt.BehaviourTest.test; + +import java.io.Serializable; +import java.util.Date; +import java.util.Map; + +import com.google.common.collect.ImmutableMap; + +import org.alfresco.model.ContentModel; +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.disposition.DispositionSchedule; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService; +import org.alfresco.module.org_alfresco_module_rm.job.publish.DispositionActionDefinitionPublishExecutor; +import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase; +import org.alfresco.module.org_alfresco_module_rm.test.util.CommonRMTestUtils; +import org.alfresco.module.org_alfresco_module_rm.test.util.bdt.BehaviourTest; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.ApplicationContextHelper; +import org.springframework.extensions.webscripts.GUID; + +public class MultipleSchedulesTest extends BaseRMTestCase +{ + /** A unique prefix for the constants in this test. */ + protected static final String TEST_PREFIX = MultipleSchedulesTest.class.getName() + GUID.generate() + "_"; + /** The name to use for the first category. */ + protected static final String CATEGORY_A_NAME = TEST_PREFIX + "CategoryA"; + /** The name to use for the folder within the first category. */ + protected static final String FOLDER_A_NAME = TEST_PREFIX + "FolderA"; + /** The name to use for the second category. */ + 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 record. */ + protected static final String RECORD_NAME = TEST_PREFIX + "Record"; + + /** The executor for the disposition update job. */ + private DispositionActionDefinitionPublishExecutor dispositionActionDefinitionPublishExecutor; + /** The internal disposition service is used to avoid permissions issues when updating the record. */ + private DispositionService internalDispositionService; + + /** The first category node. */ + private NodeRef categoryA; + /** The folder node within the first category. */ + private NodeRef folderA; + /** The second category node. */ + private NodeRef categoryB; + /** The folder node within the second category. */ + private NodeRef folderB; + /** The record node. */ + private NodeRef record; + + @Override + protected void setUp() throws Exception + { + super.setUp(); + + BehaviourTest.initBehaviourTests(retryingTransactionHelper); + + // Get the application context + applicationContext = ApplicationContextHelper.getApplicationContext(getConfigLocations()); + dispositionActionDefinitionPublishExecutor = applicationContext.getBean(DispositionActionDefinitionPublishExecutor.class); + internalDispositionService = (DispositionService) applicationContext.getBean("dispositionService"); + } + + /** + * RM-2526 + *

+     * Given a record subject to a disposition schedule
+     * And it is linked to a disposition schedule with the same step order, but a longer destroy step
+     * When the record is moved onto the destroy step
+     * Then the "as of" date is calculated using the longer period.
+     * 
+ */ + public void testLinkedToLongerSchedule() + { + test() + .given(() -> { + // Create two categories. + categoryA = filePlanService.createRecordCategory(filePlan, CATEGORY_A_NAME); + categoryB = filePlanService.createRecordCategory(filePlan, CATEGORY_B_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, + PROP_DISPOSITION_DESCRIPTION, "description", + PROP_DISPOSITION_PERIOD, CommonRMTestUtils.PERIOD_IMMEDIATELY); + dispositionService.addDispositionActionDefinition(dispSchedA, cutOffParamsA); + Map destroyParamsA = ImmutableMap.of(PROP_DISPOSITION_ACTION_NAME, DestroyAction.NAME, + PROP_DISPOSITION_DESCRIPTION, "description", + PROP_DISPOSITION_PERIOD, CommonRMTestUtils.PERIOD_IMMEDIATELY); + dispositionService.addDispositionActionDefinition(dispSchedA, destroyParamsA); + // Create a disposition schedule for category B (Cut off immediately, then Destroy one week after cutoff). + DispositionSchedule dispSchedB = utils.createBasicDispositionSchedule(categoryB, "instructions", "authority", true, false); + Map cutOffParamsB = ImmutableMap.of(PROP_DISPOSITION_ACTION_NAME, CutOffAction.NAME, + PROP_DISPOSITION_DESCRIPTION, "description", + PROP_DISPOSITION_PERIOD, CommonRMTestUtils.PERIOD_IMMEDIATELY); + dispositionService.addDispositionActionDefinition(dispSchedB, cutOffParamsB); + Map destroyParamsB = ImmutableMap.of(PROP_DISPOSITION_ACTION_NAME, DestroyAction.NAME, + PROP_DISPOSITION_DESCRIPTION, "description", + PROP_DISPOSITION_PERIOD, CommonRMTestUtils.PERIOD_ONE_WEEK, + PROP_DISPOSITION_PERIOD_PROPERTY, PROP_CUT_OFF_DATE); + dispositionService.addDispositionActionDefinition(dispSchedB, destroyParamsB); + // Create a folder within each category. + folderA = recordFolderService.createRecordFolder(categoryA, FOLDER_A_NAME); + folderB = recordFolderService.createRecordFolder(categoryB, FOLDER_B_NAME); + // 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); + // Ensure the update has been applied to the record. + internalDispositionService.updateNextDispositionAction(record); + }) + .then() + .expect(7 * 24 * 60 * 60 * 1000L) + .from(() -> dispositionService.getNextDispositionAction(record).getAsOfDate().getTime() + - ((Date) nodeService.getProperty(record, PROP_CUT_OFF_DATE)).getTime()) + .because("Record should follow largest rentention schedule period, which is one week."); + } + + /** + * RM-2526 + *

+     * Given a record subject to a disposition schedule
+     * And it is linked to a disposition schedule with the same step order, but a shorter destroy step
+     * When the record is moved onto the destroy step
+     * Then the "as of" date is calculated using the longer period.
+     * 
+ */ + public void testLinkedToShorterSchedule() + { + } +} From 92ea5c3b58264d8d3a5f7ec954de4961d57085cc Mon Sep 17 00:00:00 2001 From: Tom Page Date: Thu, 20 Oct 2016 11:36:47 +0100 Subject: [PATCH 21/28] RM-2526 Add a second test for linking to a shorter schedule. --- .../disposition/MultipleSchedulesTest.java | 92 ++++++++++++------- 1 file changed, 61 insertions(+), 31 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 67bf854533..dd0326670e 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 @@ -13,7 +13,6 @@ 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.disposition.DispositionSchedule; import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService; -import org.alfresco.module.org_alfresco_module_rm.job.publish.DispositionActionDefinitionPublishExecutor; import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase; import org.alfresco.module.org_alfresco_module_rm.test.util.CommonRMTestUtils; import org.alfresco.module.org_alfresco_module_rm.test.util.bdt.BehaviourTest; @@ -37,8 +36,6 @@ public class MultipleSchedulesTest extends BaseRMTestCase /** The name to use for the record. */ protected static final String RECORD_NAME = TEST_PREFIX + "Record"; - /** The executor for the disposition update job. */ - private DispositionActionDefinitionPublishExecutor dispositionActionDefinitionPublishExecutor; /** The internal disposition service is used to avoid permissions issues when updating the record. */ private DispositionService internalDispositionService; @@ -62,8 +59,51 @@ public class MultipleSchedulesTest extends BaseRMTestCase // Get the application context applicationContext = ApplicationContextHelper.getApplicationContext(getConfigLocations()); - dispositionActionDefinitionPublishExecutor = applicationContext.getBean(DispositionActionDefinitionPublishExecutor.class); internalDispositionService = (DispositionService) applicationContext.getBean("dispositionService"); + + // Set up the file plan if it hasn't already been done. + if (categoryA == null) + { + setUpFilePlan(); + } + // Ensure different records are used for each test. + record = null; + } + + /** + * Create two categories each containing a folder. Set up a schedule on category A that applies to records (cutoff + * immediately, destroy immediately). Set up a schedule on category B that is the same, but with a week delay before + * destroy becomes eligible. + */ + private void setUpFilePlan() + { + // Create two categories. + categoryA = filePlanService.createRecordCategory(filePlan, CATEGORY_A_NAME); + categoryB = filePlanService.createRecordCategory(filePlan, CATEGORY_B_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, + PROP_DISPOSITION_DESCRIPTION, "description", + PROP_DISPOSITION_PERIOD, CommonRMTestUtils.PERIOD_IMMEDIATELY); + dispositionService.addDispositionActionDefinition(dispSchedA, cutOffParamsA); + Map destroyParamsA = ImmutableMap.of(PROP_DISPOSITION_ACTION_NAME, DestroyAction.NAME, + PROP_DISPOSITION_DESCRIPTION, "description", + PROP_DISPOSITION_PERIOD, CommonRMTestUtils.PERIOD_IMMEDIATELY); + dispositionService.addDispositionActionDefinition(dispSchedA, destroyParamsA); + // Create a disposition schedule for category B (Cut off immediately, then Destroy one week after cutoff). + DispositionSchedule dispSchedB = utils.createBasicDispositionSchedule(categoryB, "instructions", "authority", true, false); + Map cutOffParamsB = ImmutableMap.of(PROP_DISPOSITION_ACTION_NAME, CutOffAction.NAME, + PROP_DISPOSITION_DESCRIPTION, "description", + PROP_DISPOSITION_PERIOD, CommonRMTestUtils.PERIOD_IMMEDIATELY); + dispositionService.addDispositionActionDefinition(dispSchedB, cutOffParamsB); + Map destroyParamsB = ImmutableMap.of(PROP_DISPOSITION_ACTION_NAME, DestroyAction.NAME, + PROP_DISPOSITION_DESCRIPTION, "description", + PROP_DISPOSITION_PERIOD, CommonRMTestUtils.PERIOD_ONE_WEEK, + PROP_DISPOSITION_PERIOD_PROPERTY, PROP_CUT_OFF_DATE); + dispositionService.addDispositionActionDefinition(dispSchedB, destroyParamsB); + // Create a folder within each category. + folderA = recordFolderService.createRecordFolder(categoryA, FOLDER_A_NAME); + folderB = recordFolderService.createRecordFolder(categoryB, FOLDER_B_NAME); } /** @@ -79,33 +119,6 @@ public class MultipleSchedulesTest extends BaseRMTestCase { test() .given(() -> { - // Create two categories. - categoryA = filePlanService.createRecordCategory(filePlan, CATEGORY_A_NAME); - categoryB = filePlanService.createRecordCategory(filePlan, CATEGORY_B_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, - PROP_DISPOSITION_DESCRIPTION, "description", - PROP_DISPOSITION_PERIOD, CommonRMTestUtils.PERIOD_IMMEDIATELY); - dispositionService.addDispositionActionDefinition(dispSchedA, cutOffParamsA); - Map destroyParamsA = ImmutableMap.of(PROP_DISPOSITION_ACTION_NAME, DestroyAction.NAME, - PROP_DISPOSITION_DESCRIPTION, "description", - PROP_DISPOSITION_PERIOD, CommonRMTestUtils.PERIOD_IMMEDIATELY); - dispositionService.addDispositionActionDefinition(dispSchedA, destroyParamsA); - // Create a disposition schedule for category B (Cut off immediately, then Destroy one week after cutoff). - DispositionSchedule dispSchedB = utils.createBasicDispositionSchedule(categoryB, "instructions", "authority", true, false); - Map cutOffParamsB = ImmutableMap.of(PROP_DISPOSITION_ACTION_NAME, CutOffAction.NAME, - PROP_DISPOSITION_DESCRIPTION, "description", - PROP_DISPOSITION_PERIOD, CommonRMTestUtils.PERIOD_IMMEDIATELY); - dispositionService.addDispositionActionDefinition(dispSchedB, cutOffParamsB); - Map destroyParamsB = ImmutableMap.of(PROP_DISPOSITION_ACTION_NAME, DestroyAction.NAME, - PROP_DISPOSITION_DESCRIPTION, "description", - PROP_DISPOSITION_PERIOD, CommonRMTestUtils.PERIOD_ONE_WEEK, - PROP_DISPOSITION_PERIOD_PROPERTY, PROP_CUT_OFF_DATE); - dispositionService.addDispositionActionDefinition(dispSchedB, destroyParamsB); - // Create a folder within each category. - folderA = recordFolderService.createRecordFolder(categoryA, FOLDER_A_NAME); - folderB = recordFolderService.createRecordFolder(categoryB, FOLDER_B_NAME); // 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); @@ -134,5 +147,22 @@ public class MultipleSchedulesTest extends BaseRMTestCase */ public void testLinkedToShorterSchedule() { + test() + .given(() -> { + // Create a record filed under category B and linked to category A. + record = fileFolderService.create(folderB, RECORD_NAME, ContentModel.TYPE_CONTENT).getNodeRef(); + recordService.link(record, folderA); + }) + .when(() -> { + // Cut off the record. + dispositionService.cutoffDisposableItem(record); + // Ensure the update has been applied to the record. + internalDispositionService.updateNextDispositionAction(record); + }) + .then() + .expect(7 * 24 * 60 * 60 * 1000L) + .from(() -> dispositionService.getNextDispositionAction(record).getAsOfDate().getTime() + - ((Date) nodeService.getProperty(record, PROP_CUT_OFF_DATE)).getTime()) + .because("Record should follow largest rentention schedule period, which is one week."); } } From 66def3bd88bf7b840c40bd65a2529d9938b94b92 Mon Sep 17 00:00:00 2001 From: Tom Page Date: Thu, 20 Oct 2016 12:35:32 +0100 Subject: [PATCH 22/28] RM-2526 Make sure we set up the file plan from within a transaction. --- .../disposition/MultipleSchedulesTest.java | 13 ++++++++----- 1 file changed, 8 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 dd0326670e..7dae0fd715 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 @@ -61,11 +61,6 @@ public class MultipleSchedulesTest extends BaseRMTestCase applicationContext = ApplicationContextHelper.getApplicationContext(getConfigLocations()); internalDispositionService = (DispositionService) applicationContext.getBean("dispositionService"); - // Set up the file plan if it hasn't already been done. - if (categoryA == null) - { - setUpFilePlan(); - } // Ensure different records are used for each test. record = null; } @@ -77,6 +72,12 @@ public class MultipleSchedulesTest extends BaseRMTestCase */ private void setUpFilePlan() { + // Only set up the file plan if it hasn't already been done. + if (categoryA == null) + { + return; + } + // Create two categories. categoryA = filePlanService.createRecordCategory(filePlan, CATEGORY_A_NAME); categoryB = filePlanService.createRecordCategory(filePlan, CATEGORY_B_NAME); @@ -119,6 +120,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); @@ -149,6 +151,7 @@ public class MultipleSchedulesTest extends BaseRMTestCase { test() .given(() -> { + setUpFilePlan(); // Create a record filed under category B and linked to category A. record = fileFolderService.create(folderB, RECORD_NAME, ContentModel.TYPE_CONTENT).getNodeRef(); recordService.link(record, folderA); From f4874eb118412a44ef70fc8665393b880e65c6e8 Mon Sep 17 00:00:00 2001 From: Tom Page Date: Thu, 20 Oct 2016 14:06:52 +0100 Subject: [PATCH 23/28] RM-2526 Fix typo. --- .../test/integration/disposition/MultipleSchedulesTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 7dae0fd715..f97a7fee3a 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 @@ -73,7 +73,7 @@ public class MultipleSchedulesTest extends BaseRMTestCase private void setUpFilePlan() { // Only set up the file plan if it hasn't already been done. - if (categoryA == null) + if (categoryA != null) { return; } From 05d14de22b55215466780584dbd25c235a4315a5 Mon Sep 17 00:00:00 2001 From: Tom Page Date: Mon, 24 Oct 2016 13:57:12 +0100 Subject: [PATCH 24/28] RM-2526 Fix location of MultipleSchedulesTest. --- .../disposition/MultipleSchedulesTest.java | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) rename {rm-server => rm-community/rm-community-repo}/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/MultipleSchedulesTest.java (88%) diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/MultipleSchedulesTest.java b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/MultipleSchedulesTest.java similarity index 88% rename from rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/MultipleSchedulesTest.java rename to rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/MultipleSchedulesTest.java index f97a7fee3a..cf2f957dae 100644 --- a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/MultipleSchedulesTest.java +++ b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/MultipleSchedulesTest.java @@ -1,3 +1,29 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * 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 . + * #L% + */ package org.alfresco.module.org_alfresco_module_rm.test.integration.disposition; import static org.alfresco.module.org_alfresco_module_rm.test.util.bdt.BehaviourTest.test; @@ -21,6 +47,12 @@ import org.alfresco.service.namespace.QName; import org.alfresco.util.ApplicationContextHelper; import org.springframework.extensions.webscripts.GUID; +/** + * Integration tests for records linked to multiple disposition schedules. + * + * @author Tom Page + * @since 2.3.1 + */ public class MultipleSchedulesTest extends BaseRMTestCase { /** A unique prefix for the constants in this test. */ From 67ad6b4214d89f11d35ffa266d95044d3e061656 Mon Sep 17 00:00:00 2001 From: roxana Date: Fri, 21 Oct 2016 11:13:51 +0300 Subject: [PATCH 25/28] Checking if disposition as of date was changed manually. (cherry picked from commit fd6fcd8e1442102a446971f691e78780cd576674) --- .../disposition/DispositionServiceImpl.java | 34 +++++++++++-------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImpl.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImpl.java index b4b1ec1d4a..38ff47a0af 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImpl.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImpl.java @@ -27,6 +27,8 @@ package org.alfresco.module.org_alfresco_module_rm.disposition; +import static org.apache.commons.lang3.BooleanUtils.isNotTrue; + import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; @@ -289,26 +291,30 @@ public class DispositionServiceImpl extends ServiceBaseImpl if (dsNextAction != null) { - if (!dsNextAction.getWriteMode().equals(WriteMode.READ_ONLY)) + final NodeRef action = dsNextAction.getNextActionNodeRef(); + if (isNotTrue((Boolean)nodeService.getProperty(action, PROP_MANUALLY_SET_AS_OF))) { - final NodeRef action = dsNextAction.getNextActionNodeRef(); - final String dispositionActionName = dsNextAction.getNextActionName(); - final Date dispositionActionDate = dsNextAction.getNextActionDateAsOf(); - - AuthenticationUtil.runAsSystem(new RunAsWork() + if (!dsNextAction.getWriteMode().equals(WriteMode.READ_ONLY)) { - @Override - public Void doWork() + final String dispositionActionName = dsNextAction.getNextActionName(); + final Date dispositionActionDate = dsNextAction.getNextActionDateAsOf(); + + AuthenticationUtil.runAsSystem(new RunAsWork() { - nodeService.setProperty(action, PROP_DISPOSITION_AS_OF, dispositionActionDate); - if (dsNextAction.getWriteMode().equals(WriteMode.DATE_AND_NAME)) + @Override + public Void doWork() { - nodeService.setProperty(action, PROP_DISPOSITION_ACTION_NAME, dispositionActionName); + nodeService.setProperty(action, PROP_DISPOSITION_AS_OF, dispositionActionDate); + if (dsNextAction.getWriteMode().equals(WriteMode.DATE_AND_NAME)) + { + nodeService.setProperty(action, PROP_DISPOSITION_ACTION_NAME, dispositionActionName); + } + return null; } - return null; - } - }); + }); + } } + dsNodeRef = dsNextAction.getDispositionNodeRef(); } } From 4a912391dff7cc483abb3e479832362e278bfec8 Mon Sep 17 00:00:00 2001 From: roxana Date: Mon, 24 Oct 2016 10:16:58 +0300 Subject: [PATCH 26/28] When the last step of the current disposition is completed, no more calculation is needed. (cherry picked from commit 606e91545c0725b5523770b1c3cb1cffd38c0ce0) --- .../disposition/DispositionServiceImpl.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImpl.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImpl.java index 38ff47a0af..918446f742 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImpl.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImpl.java @@ -287,6 +287,13 @@ public class DispositionServiceImpl extends ServiceBaseImpl NodeRef dsNodeRef = null; if (isRecord(nodeRef)) { + DispositionSchedule originDispositionSchedule = getOriginDispositionSchedule(nodeRef); + // if the initial disposition schedule of the record is folder based + if (isNotTrue(originDispositionSchedule.isRecordLevelDisposition())) + { + return null; + } + final NextActionFromDisposition dsNextAction = getDispositionActionByNameForRecord(nodeRef); if (dsNextAction != null) @@ -1152,6 +1159,13 @@ public class DispositionServiceImpl extends ServiceBaseImpl if (nextDispositionAction == null) { + DispositionAction lastCompletedDispositionAction = getLastCompletedDispostionAction(record); + if (lastCompletedDispositionAction != null) + { + // all disposition actions upon the given record were completed + return null; + } + return getFirstDispositionAction(record, recordFolders); } else From ad995fe26321cda560467a4b7401b353c233c1e3 Mon Sep 17 00:00:00 2001 From: roxana Date: Mon, 24 Oct 2016 11:09:32 +0300 Subject: [PATCH 27/28] Added null check (cherry picked from commit 72dec6f5ef58ab5d64e49e2c2e7d00a1b198354a) --- .../disposition/DispositionServiceImpl.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImpl.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImpl.java index 918446f742..71f5ce9237 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImpl.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImpl.java @@ -289,7 +289,8 @@ public class DispositionServiceImpl extends ServiceBaseImpl { DispositionSchedule originDispositionSchedule = getOriginDispositionSchedule(nodeRef); // if the initial disposition schedule of the record is folder based - if (isNotTrue(originDispositionSchedule.isRecordLevelDisposition())) + if (originDispositionSchedule!= null && + isNotTrue(originDispositionSchedule.isRecordLevelDisposition())) { return null; } From 8f8b0268f2f2206162b844076116ea5f028b96fb Mon Sep 17 00:00:00 2001 From: roxana Date: Mon, 24 Oct 2016 15:30:37 +0300 Subject: [PATCH 28/28] I've tweaked the tests a bit to handle daylight savings. (cherry picked from commit 4aa3909035df63537ea17b101117394250225960) --- .../disposition/DispositionServiceImpl.java | 2 +- .../disposition/MultipleSchedulesTest.java | 21 ++++++++++++------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImpl.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImpl.java index 71f5ce9237..88ee38e567 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImpl.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImpl.java @@ -289,7 +289,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl { DispositionSchedule originDispositionSchedule = getOriginDispositionSchedule(nodeRef); // if the initial disposition schedule of the record is folder based - if (originDispositionSchedule!= null && + if (originDispositionSchedule == null || isNotTrue(originDispositionSchedule.isRecordLevelDisposition())) { return null; diff --git a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/MultipleSchedulesTest.java b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/MultipleSchedulesTest.java index cf2f957dae..daa9ab1818 100644 --- a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/MultipleSchedulesTest.java +++ b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/disposition/MultipleSchedulesTest.java @@ -29,11 +29,10 @@ package org.alfresco.module.org_alfresco_module_rm.test.integration.disposition; import static org.alfresco.module.org_alfresco_module_rm.test.util.bdt.BehaviourTest.test; import java.io.Serializable; +import java.util.Calendar; import java.util.Date; import java.util.Map; -import com.google.common.collect.ImmutableMap; - import org.alfresco.model.ContentModel; import org.alfresco.module.org_alfresco_module_rm.action.impl.CutOffAction; import org.alfresco.module.org_alfresco_module_rm.action.impl.DestroyAction; @@ -47,6 +46,8 @@ import org.alfresco.service.namespace.QName; import org.alfresco.util.ApplicationContextHelper; import org.springframework.extensions.webscripts.GUID; +import com.google.common.collect.ImmutableMap; + /** * Integration tests for records linked to multiple disposition schedules. * @@ -150,6 +151,7 @@ public class MultipleSchedulesTest extends BaseRMTestCase */ public void testLinkedToLongerSchedule() { + Calendar calendar = Calendar.getInstance(); test() .given(() -> { setUpFilePlan(); @@ -162,11 +164,12 @@ public class MultipleSchedulesTest extends BaseRMTestCase dispositionService.cutoffDisposableItem(record); // Ensure the update has been applied to the record. internalDispositionService.updateNextDispositionAction(record); + calendar.setTime((Date) nodeService.getProperty(record, PROP_CUT_OFF_DATE)); + calendar.add(Calendar.WEEK_OF_YEAR, 1); }) .then() - .expect(7 * 24 * 60 * 60 * 1000L) - .from(() -> dispositionService.getNextDispositionAction(record).getAsOfDate().getTime() - - ((Date) nodeService.getProperty(record, PROP_CUT_OFF_DATE)).getTime()) + .expect(calendar.getTime()) + .from(() -> dispositionService.getNextDispositionAction(record).getAsOfDate()) .because("Record should follow largest rentention schedule period, which is one week."); } @@ -181,6 +184,7 @@ public class MultipleSchedulesTest extends BaseRMTestCase */ public void testLinkedToShorterSchedule() { + Calendar calendar = Calendar.getInstance(); test() .given(() -> { setUpFilePlan(); @@ -193,11 +197,12 @@ public class MultipleSchedulesTest extends BaseRMTestCase dispositionService.cutoffDisposableItem(record); // Ensure the update has been applied to the record. internalDispositionService.updateNextDispositionAction(record); + calendar.setTime((Date) nodeService.getProperty(record, PROP_CUT_OFF_DATE)); + calendar.add(Calendar.WEEK_OF_YEAR, 1); }) .then() - .expect(7 * 24 * 60 * 60 * 1000L) - .from(() -> dispositionService.getNextDispositionAction(record).getAsOfDate().getTime() - - ((Date) nodeService.getProperty(record, PROP_CUT_OFF_DATE)).getTime()) + .expect(calendar.getTime()) + .from(() -> dispositionService.getNextDispositionAction(record).getAsOfDate()) .because("Record should follow largest rentention schedule period, which is one week."); } }