From 4139f2514bd09da352ad15a07813e1edcf0174a4 Mon Sep 17 00:00:00 2001 From: Ana Bozianu Date: Wed, 31 May 2017 16:55:56 +0300 Subject: [PATCH 01/16] RM-5244 - make sure the incomplete node tagger is run after the record aspect is added (workaround) --- .../org_alfresco_module_rm/rm-service-context.xml | 1 + .../record/RecordServiceImpl.java | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml index 6682d0a617..c2fd343d4b 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml @@ -1075,6 +1075,7 @@ + diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImpl.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImpl.java index 65d4582634..92b6e66744 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImpl.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImpl.java @@ -80,6 +80,7 @@ import org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionModel import org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionService; import org.alfresco.repo.content.ContentServicePolicies; import org.alfresco.repo.node.NodeServicePolicies; +import org.alfresco.repo.node.integrity.IncompleteNodeTagger; import org.alfresco.repo.policy.ClassPolicyDelegate; import org.alfresco.repo.policy.PolicyComponent; import org.alfresco.repo.policy.Behaviour.NotificationFrequency; @@ -270,6 +271,8 @@ public class RecordServiceImpl extends BaseBehaviourBean private ClassPolicyDelegate beforeRecordRejectionDelegate; private ClassPolicyDelegate onRecordRejectionDelegate; + private IncompleteNodeTagger incompleteNodeTagger; + /** * @param identifierService identifier service */ @@ -414,6 +417,11 @@ public class RecordServiceImpl extends BaseBehaviourBean this.recordableVersionService = recordableVersionService; } + public void setIncompleteNodeTagger(IncompleteNodeTagger incompleteNodeTagger) + { + this.incompleteNodeTagger = incompleteNodeTagger; + } + /** * Init method */ @@ -448,6 +456,8 @@ public class RecordServiceImpl extends BaseBehaviourBean if (nodeService.exists(nodeRef) && nodeService.hasAspect(nodeRef, ASPECT_RECORD)) { generateRecordIdentifier(nodeService, identifierService, nodeRef); + // RM-5244 - workaround to make sure the incomplete aspect is removed + incompleteNodeTagger.beforeCommit(false); } return null; } @@ -471,6 +481,8 @@ public class RecordServiceImpl extends BaseBehaviourBean if (ContentData.hasContent(contentData) && contentData.getSize() > 0) { appendIdentifierToName(nodeService, nodeRef); + // RM-5244 - workaround to make sure the incomplete aspect is removed + incompleteNodeTagger.beforeCommit(false); } } } @@ -1835,6 +1847,8 @@ public class RecordServiceImpl extends BaseBehaviourBean if (nodeService.exists(nodeRef) && !nodeService.hasAspect(nodeRef, ContentModel.ASPECT_HIDDEN) && !nodeService.hasAspect(nodeRef, ContentModel.ASPECT_LOCKABLE)) { generateRecordIdentifier(nodeService, identifierService, nodeRef); + // RM-5244 - workaround to make sure the incomplete aspect is removed + incompleteNodeTagger.beforeCommit(false); } } From 953a5c3d96cf7f4ff6d0829958fb993e1a9efe7b Mon Sep 17 00:00:00 2001 From: Ana Bozianu Date: Wed, 31 May 2017 17:10:43 +0300 Subject: [PATCH 02/16] RM-5244 - added extra check for efficiency --- .../record/RecordServiceImpl.java | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImpl.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImpl.java index 92b6e66744..85f3a01d5e 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImpl.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImpl.java @@ -456,8 +456,7 @@ public class RecordServiceImpl extends BaseBehaviourBean if (nodeService.exists(nodeRef) && nodeService.hasAspect(nodeRef, ASPECT_RECORD)) { generateRecordIdentifier(nodeService, identifierService, nodeRef); - // RM-5244 - workaround to make sure the incomplete aspect is removed - incompleteNodeTagger.beforeCommit(false); + reevaluateIncompleteTag(nodeRef); } return null; } @@ -481,8 +480,7 @@ public class RecordServiceImpl extends BaseBehaviourBean if (ContentData.hasContent(contentData) && contentData.getSize() > 0) { appendIdentifierToName(nodeService, nodeRef); - // RM-5244 - workaround to make sure the incomplete aspect is removed - incompleteNodeTagger.beforeCommit(false); + reevaluateIncompleteTag(nodeRef); } } } @@ -1847,8 +1845,7 @@ public class RecordServiceImpl extends BaseBehaviourBean if (nodeService.exists(nodeRef) && !nodeService.hasAspect(nodeRef, ContentModel.ASPECT_HIDDEN) && !nodeService.hasAspect(nodeRef, ContentModel.ASPECT_LOCKABLE)) { generateRecordIdentifier(nodeService, identifierService, nodeRef); - // RM-5244 - workaround to make sure the incomplete aspect is removed - incompleteNodeTagger.beforeCommit(false); + reevaluateIncompleteTag(nodeRef); } } @@ -1907,4 +1904,22 @@ public class RecordServiceImpl extends BaseBehaviourBean OnRecordRejection policy = onRecordRejectionDelegate.get(qnames); policy.onRecordRejection(nodeRef); } + + /** + * RM-5244 - workaround to make sure the incomplete aspect is removed + * + * @param nodeRef the node to reevaluate for + */ + private void reevaluateIncompleteTag(NodeRef nodeRef) + { + /* + * Check if the node has the aspect because the reevaluation is expensive. + * If the node doesn't have the aspect it means IncompleteNodeTagger didn't load before TransactionBehaviourQueue + * and we don't need to reevaluate. + */ + if(nodeService.hasAspect(nodeRef, ContentModel.ASPECT_INCOMPLETE)) + { + incompleteNodeTagger.beforeCommit(false); + } + } } From a7fddef34baf715437f9ad82ffa755532409b7be Mon Sep 17 00:00:00 2001 From: Sara Aspery Date: Tue, 6 Jun 2017 16:21:49 +0100 Subject: [PATCH 03/16] RM-5175_Complete_record_with_missing_metadata --- .../action/impl/DeclareRecordAction.java | 22 + .../record/CompleteRecordTest.java | 671 ++++++++++++++---- .../test/util/CommonRMTestUtils.java | 37 + 3 files changed, 585 insertions(+), 145 deletions(-) diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/DeclareRecordAction.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/DeclareRecordAction.java index 40698f75e4..83329105a4 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/DeclareRecordAction.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/DeclareRecordAction.java @@ -29,6 +29,7 @@ package org.alfresco.module.org_alfresco_module_rm.action.impl; import java.io.Serializable; import java.util.ArrayList; +import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.List; @@ -193,6 +194,27 @@ public class DeclareRecordAction extends RMActionExecuterAbstractBase } } + // check for missing mandatory metadata from custom aspect definitions + if (result) + { + Collection aspects = this.getDictionaryService().getAspects(RM_CUSTOM_MODEL); + for (QName aspect : aspects) + { + // TODO should not apply record custom metadata for non-electronic and vice versa + AspectDefinition aspectDef = this.getDictionaryService().getAspect(aspect); + for (PropertyDefinition propDef : aspectDef.getProperties().values()) + { + if (propDef.isMandatory() && nodeRefProps.get(propDef.getName()) == null) + { + logMissingProperty(propDef, missingProperties); + + result = false; + break; + } + } + } + } + return result; } diff --git a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/record/CompleteRecordTest.java b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/record/CompleteRecordTest.java index dc31c9ba14..0079cc3829 100644 --- a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/record/CompleteRecordTest.java +++ b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/record/CompleteRecordTest.java @@ -33,210 +33,591 @@ import java.util.Map; import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionResult; import org.alfresco.module.org_alfresco_module_rm.action.impl.DeclareRecordAction; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementCustomModel; import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.QName; /** * Complete record tests. - * + * * @author Roy Wetherall * @since 2.2.1 */ public class CompleteRecordTest extends BaseRMTestCase -{ - private static final QName ASPECT_TEST = QName.createQName("http://www.alfresco.org/model/rmtest/1.0", "recordMetaDataWithProperty"); - private static final QName PROP_TEST = QName.createQName("http://www.alfresco.org/model/rmtest/1.0", "customMandatoryProperty"); - - /** complete record action */ - private DeclareRecordAction action; - - /** - * @see org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase#initServices() - */ - @Override - protected void initServices() - { - super.initServices(); - - // get the action - action = (DeclareRecordAction)applicationContext.getBean("declareRecord"); - } - - /** - * @see org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase#tearDownImpl() - */ - @Override - protected void tearDownImpl() - { - super.tearDownImpl(); - - // ensure action is returned to original state - action.setCheckMandatoryPropertiesEnabled(true); - } - - /** - * Given the the application is configured to check for mandatory values before complete - * And a filed record is missing mandatory values - * When I try to complete the record - * Then the missing properties parameter of the action will be populated - * And the record will not be complete - */ +{ + private static final QName ASPECT_TEST = QName.createQName("http://www.alfresco.org/model/rmtest/1.0", "recordMetaDataWithProperty"); + private static final QName PROP_TEST = QName.createQName("http://www.alfresco.org/model/rmtest/1.0", "customMandatoryProperty"); + private static final QName CUSTOM_ELECTRONIC_TEST = QName.createQName(RecordsManagementCustomModel.RM_CUSTOM_URI, "rmarecordCustomProperties"); + private static final QName CUSTOM_NON_ELECTRONIC_TEST = QName.createQName(RecordsManagementCustomModel.RM_CUSTOM_URI, "rmanonElectronicDocumentCustomProperties"); + private static final boolean MANDATORY_METADATA = true; + private static final boolean OPTIONAL_METADATA = false; + + /** + * complete record action + */ + private DeclareRecordAction action; + + /** + * @see org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase#initServices() + */ + @Override + protected void initServices() + { + super.initServices(); + + // get the action + action = (DeclareRecordAction) applicationContext.getBean("declareRecord"); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase#tearDownImpl() + */ + @Override + protected void tearDownImpl() + { + super.tearDownImpl(); + + // ensure action is returned to original state + action.setCheckMandatoryPropertiesEnabled(true); + } + + /** + * Given the the application is configured to check for mandatory values before complete + * And a filed record is missing mandatory values + * When I try to complete the record + * Then the missing properties parameter of the action will be populated + * And the record will not be complete + */ public void testCheckForMandatoryValuesMissing() throws Exception { doBehaviourDrivenTest(new BehaviourDrivenTest() - { - private NodeRef record; - private RecordsManagementActionResult result; - + { + private NodeRef record; + private RecordsManagementActionResult result; + public void given() { - // enable mandatory parameter check - action.setCheckMandatoryPropertiesEnabled(true); - - // create a record - record = utils.createRecord(rmFolder, "record.txt", "title"); - - // add the record aspect (that has a mandatory property) - nodeService.addAspect(record, ASPECT_TEST, null); + // enable mandatory parameter check + action.setCheckMandatoryPropertiesEnabled(true); + + // create a record + record = utils.createRecord(rmFolder, "record.txt", "title"); + + // add the record aspect (that has a mandatory property) + nodeService.addAspect(record, ASPECT_TEST, null); } - + public void when() { - // complete record - result = rmActionService.executeRecordsManagementAction(record, "declareRecord"); + // complete record + result = rmActionService.executeRecordsManagementAction(record, "declareRecord"); } - - public void then() + + public void then() { - assertNotNull(result); - assertNotNull(result.getValue()); - assertFalse(nodeService.hasAspect(record, ASPECT_DECLARED_RECORD)); + assertNotNull(result); + assertNotNull(result.getValue()); + assertFalse(nodeService.hasAspect(record, ASPECT_DECLARED_RECORD)); } - }); + }); } - + /** - * Given the the application is configured to check for mandatory values before complete - * And a filed record has all mandatory values - * When I try to complete the record - * Then the record is completed - */ + * Given the the application is configured to check for mandatory values before complete + * And a filed record has all mandatory values + * When I try to complete the record + * Then the record is completed + */ public void testCheckForMandatoryValuePresent() throws Exception { doBehaviourDrivenTest(new BehaviourDrivenTest() - { - private NodeRef record; - private RecordsManagementActionResult result; - + { + private NodeRef record; + private RecordsManagementActionResult result; + public void given() { - // enable mandatory parameter check - action.setCheckMandatoryPropertiesEnabled(true); - - // create a record - record = utils.createRecord(rmFolder, "record.txt", "title"); - - // add the record aspect (that has a mandatory property) - Map properties = new HashMap(1); - properties.put(PROP_TEST, "something"); - nodeService.addAspect(record, ASPECT_TEST, properties); + // enable mandatory parameter check + action.setCheckMandatoryPropertiesEnabled(true); + + // create a record + record = utils.createRecord(rmFolder, "record.txt", "title"); + + // add the record aspect (that has a mandatory property) + Map properties = new HashMap<>(1); + properties.put(PROP_TEST, "something"); + nodeService.addAspect(record, ASPECT_TEST, properties); } - + public void when() { - // complete record - result = rmActionService.executeRecordsManagementAction(record, "declareRecord"); + // complete record + result = rmActionService.executeRecordsManagementAction(record, "declareRecord"); } - - public void then() + + public void then() { - assertNotNull(result); - assertNull(result.getValue()); - assertTrue(nodeService.hasAspect(record, ASPECT_DECLARED_RECORD)); + assertNotNull(result); + assertNull(result.getValue()); + assertTrue(nodeService.hasAspect(record, ASPECT_DECLARED_RECORD)); } - }); + }); } - + /** - * Given the the application is configured not to check for mandatory values before complete - * And a filed record is missing mandatory values - * When I try to complete the record - * Then the record is completed - */ + * Given the the application is configured to check for mandatory values before complete + * And a filed record is missing custom mandatory values + * When I try to complete the record + * Then the missing properties parameter of the action will be populated + * And the record will not be complete + */ + public void testCheckForCustomMandatoryValuesMissing() throws Exception + { + doBehaviourDrivenTest(new BehaviourDrivenTest() + { + private NodeRef record; + private RecordsManagementActionResult result; + + public void given() throws Exception + { + // enable mandatory parameter check + action.setCheckMandatoryPropertiesEnabled(true); + + // create the custom metadata definition (that has a mandatory property) for electronic records + defineCustomMetadata(CUSTOM_ELECTRONIC_TEST, ASPECT_RECORD, MANDATORY_METADATA); + + // create an electronic record + record = utils.createRecord(rmFolder, "electronicRecord.txt", "title"); + } + + public void when() + { + // complete record + result = rmActionService.executeRecordsManagementAction(record, "declareRecord"); + } + + public void then() + { + assertNotNull(result); + assertNotNull(result.getValue()); + assertFalse(nodeService.hasAspect(record, ASPECT_DECLARED_RECORD)); + } + + public void after() + { + // remove the custom metadata definition + removeCustomMetadata(CUSTOM_ELECTRONIC_TEST); + } + }); + } + + /** + * Given the the application is configured to check for mandatory values before complete + * And a filed record has all custom mandatory values + * When I try to complete the record + * Then the record is completed + */ + public void testCheckForCustomMandatoryValuePresent() throws Exception + { + doBehaviourDrivenTest(new BehaviourDrivenTest() + { + private NodeRef record; + private RecordsManagementActionResult result; + + public void given() throws Exception + { + // enable mandatory parameter check + action.setCheckMandatoryPropertiesEnabled(true); + + // define the custom metadata definition (that has a mandatory property) + defineCustomMetadata(CUSTOM_ELECTRONIC_TEST, ASPECT_RECORD, MANDATORY_METADATA); + + // create a record + record = utils.createRecord(rmFolder, "customrecord.txt", "title"); + + // populate the custom metadata mandatory property for the record + populateCustomMetadata(record, CUSTOM_ELECTRONIC_TEST); + } + + public void when() + { + // complete record + result = rmActionService.executeRecordsManagementAction(record, "declareRecord"); + } + + public void then() + { + assertNotNull(result); + assertNull(result.getValue()); + assertTrue(nodeService.hasAspect(record, ASPECT_DECLARED_RECORD)); + } + + public void after() + { + // remove the custom metadata definition + removeCustomMetadata(CUSTOM_ELECTRONIC_TEST); + } + }); + } + + /** + * Given the the application is configured to check for mandatory values before complete + * And a non-electronic record is missing custom mandatory values + * When I try to complete the record + * Then the missing properties parameter of the action will be populated + * And the record will not be complete + */ + public void testCheckForCustomMandatoryValuesMissingInNonElectronicRecord() throws Exception + { + doBehaviourDrivenTest(new BehaviourDrivenTest() + { + private NodeRef nonElectronicRecord; + private RecordsManagementActionResult result; + + public void given() throws Exception + { + // enable mandatory parameter check + action.setCheckMandatoryPropertiesEnabled(true); + + // create the custom metadata definition (that has a mandatory property) for non-electronic records + defineCustomMetadata(CUSTOM_NON_ELECTRONIC_TEST, TYPE_NON_ELECTRONIC_DOCUMENT, MANDATORY_METADATA); + + // create a non-electronic record + nonElectronicRecord = utils.createRecord(rmFolder, "non-electronicRecord.txt", "title"); + } + + public void when() + { + // complete non-electronic record + result = rmActionService.executeRecordsManagementAction(nonElectronicRecord, + "declareRecord"); + } + + public void then() + { + assertNotNull(result); + assertNotNull(result.getValue()); + assertFalse(nodeService.hasAspect(nonElectronicRecord, ASPECT_DECLARED_RECORD)); + } + + public void after() + { + // remove the custom metadata definition + removeCustomMetadata(CUSTOM_NON_ELECTRONIC_TEST); + } + }); + } + + /** + * Given the the application is configured to check for mandatory values before complete + * And a non-electronic record has all custom mandatory values + * When I try to complete the record + * Then the record is completed + */ + public void testCheckForCustomMandatoryValuePresentInNonElectronicRecord() throws Exception + { + doBehaviourDrivenTest(new BehaviourDrivenTest() + { + private NodeRef nonElectronicRecord; + private RecordsManagementActionResult result; + + public void given() throws Exception + { + // enable mandatory parameter check + action.setCheckMandatoryPropertiesEnabled(true); + + // create the custom metadata definition (that has a mandatory property) + defineCustomMetadata(CUSTOM_NON_ELECTRONIC_TEST, ASPECT_RECORD, MANDATORY_METADATA); + + // create a non-electronic record + nonElectronicRecord = utils.createRecord(rmFolder, "non-electronicRecord.txt", "title"); + + // populate the custom metadata mandatory property for the record + populateCustomMetadata(nonElectronicRecord, CUSTOM_NON_ELECTRONIC_TEST); + } + + public void when() + { + // complete record + result = rmActionService.executeRecordsManagementAction(nonElectronicRecord, "declareRecord"); + } + + public void then() + { + assertNotNull(result); + assertNull(result.getValue()); + assertTrue(nodeService.hasAspect(nonElectronicRecord, ASPECT_DECLARED_RECORD)); + } + + public void after() + { + // remove the custom metadata definition + removeCustomMetadata(CUSTOM_NON_ELECTRONIC_TEST); + } + }); + } + + /** + * Given the the application is configured to check for mandatory values before complete + * And a filed record is missing custom non-mandatory values + * When I try to complete the record + * Then the record is completed + */ + public void testCheckForCustomOptionalValuesMissing() throws Exception + { + doBehaviourDrivenTest(new BehaviourDrivenTest() + { + private NodeRef record; + private RecordsManagementActionResult result; + + public void given() throws Exception + { + // enable mandatory parameter check + action.setCheckMandatoryPropertiesEnabled(true); + + // create the custom metadata definition (that does not have a mandatory property) + defineCustomMetadata(CUSTOM_ELECTRONIC_TEST, ASPECT_RECORD, OPTIONAL_METADATA); + + // create a record + record = utils.createRecord(rmFolder, "customrecord.txt", "title"); + } + + public void when() + { + // complete record + result = rmActionService.executeRecordsManagementAction(record, "declareRecord"); + } + + public void then() + { + assertNotNull(result); + assertNull(result.getValue()); + assertTrue(nodeService.hasAspect(record, ASPECT_DECLARED_RECORD)); + } + + public void after() + { + // remove the custom metadata definition + removeCustomMetadata(CUSTOM_ELECTRONIC_TEST); + } + }); + } + + /** + * Given the the application is configured to check for mandatory values before complete + * And custom mandatory metadata is required for electronic records + * And a non-electronic record has no custom mandatory values + * When I try to complete the non-electronic record + * Then the record is completed + */ + public void testElectronicRecordCustomMandatoryNotAppliedToNonElectronicRecord() throws Exception + { + doBehaviourDrivenTest(new BehaviourDrivenTest() + { + private NodeRef nonElectronicRecord; + private RecordsManagementActionResult result; + + public void given() throws Exception + { + // enable mandatory parameter check + action.setCheckMandatoryPropertiesEnabled(true); + + // create the custom metadata definition (that has a mandatory property) + defineCustomMetadata(CUSTOM_ELECTRONIC_TEST, ASPECT_RECORD, MANDATORY_METADATA); + + // create a non-electronic record + nonElectronicRecord = utils.createRecord(rmFolder, "non-electronicRecord.txt", "title"); + } + + public void when() + { + // complete record + result = rmActionService.executeRecordsManagementAction(nonElectronicRecord, "declareRecord"); + } + + public void then() + { + assertNotNull(result); + assertNull(result.getValue()); + assertTrue(nodeService.hasAspect(nonElectronicRecord, ASPECT_DECLARED_RECORD)); + } + + public void after() + { + // remove the custom metadata definition + removeCustomMetadata(CUSTOM_ELECTRONIC_TEST); + } + }); + } + + /** + * Given the the application is configured to check for mandatory values before complete + * And custom mandatory metadata is required for non-electronic records + * And an electronic record has no custom mandatory values + * When I try to complete the electronic record + * Then the record is completed + */ + public void testNonElectronicRecordCustomMandatoryNotAppliedToElectronicRecord() throws Exception + { + doBehaviourDrivenTest(new BehaviourDrivenTest() + { + private NodeRef record; + private RecordsManagementActionResult result; + + public void given() throws Exception + { + // enable mandatory parameter check + action.setCheckMandatoryPropertiesEnabled(true); + + // create the custom metadata definition (that has a mandatory property) + defineCustomMetadata(CUSTOM_NON_ELECTRONIC_TEST, ASPECT_RECORD, MANDATORY_METADATA); + + // create a electronic record + record = utils.createRecord(rmFolder, "electronicRecord.txt", "title"); + } + + public void when() + { + // complete record + result = rmActionService.executeRecordsManagementAction(record, "declareRecord"); + } + + public void then() + { + assertNotNull(result); + assertNull(result.getValue()); + assertTrue(nodeService.hasAspect(record, ASPECT_DECLARED_RECORD)); + } + + public void after() + { + // remove the custom metadata definition + removeCustomMetadata(CUSTOM_NON_ELECTRONIC_TEST); + } + }); + } + + /** + * Given the the application is configured not to check for mandatory values before complete + * And a filed record is missing mandatory values + * When I try to complete the record + * Then the record is completed + */ public void testDontCheckForMandatoryValuesMissing() throws Exception { doBehaviourDrivenTest(new BehaviourDrivenTest() - { - private NodeRef record; - private RecordsManagementActionResult result; - + { + private NodeRef record; + private RecordsManagementActionResult result; + public void given() { - // disable mandatory parameter check - action.setCheckMandatoryPropertiesEnabled(false); - - // create a record - record = utils.createRecord(rmFolder, "record.txt", "title"); - - // add the record aspect (that has a mandatory property) - nodeService.addAspect(record, ASPECT_TEST, null); + // disable mandatory parameter check + action.setCheckMandatoryPropertiesEnabled(false); + + // create a record + record = utils.createRecord(rmFolder, "record.txt", "title"); + + // add the record aspect (that has a mandatory property) + nodeService.addAspect(record, ASPECT_TEST, null); } - + public void when() { - // complete record - result = rmActionService.executeRecordsManagementAction(record, "declareRecord"); + // complete record + result = rmActionService.executeRecordsManagementAction(record, "declareRecord"); } - - public void then() + + public void then() { - assertNotNull(result); - assertNull(result.getValue()); - assertTrue(nodeService.hasAspect(record, ASPECT_DECLARED_RECORD)); + assertNotNull(result); + assertNull(result.getValue()); + assertTrue(nodeService.hasAspect(record, ASPECT_DECLARED_RECORD)); } - }); + }); } - + /** - * Given the the application is configured to not to check for mandatory values before complete - * And a filed record has all mandatory values - * When I try to complete the record - * Then the record is completed - */ + * Given the the application is configured to not to check for mandatory values before complete + * And a filed record has all mandatory values + * When I try to complete the record + * Then the record is completed + */ public void testDontCheckForMandatoryValuePresent() throws Exception { doBehaviourDrivenTest(new BehaviourDrivenTest() - { - private NodeRef record; - private RecordsManagementActionResult result; - + { + private NodeRef record; + private RecordsManagementActionResult result; + public void given() { - // enable mandatory parameter check - action.setCheckMandatoryPropertiesEnabled(false); - - // create a record - record = utils.createRecord(rmFolder, "record.txt", "title"); - - // add the record aspect (that has a mandatory property) - Map properties = new HashMap(1); - properties.put(PROP_TEST, "something"); - nodeService.addAspect(record, ASPECT_TEST, properties); + // enable mandatory parameter check + action.setCheckMandatoryPropertiesEnabled(false); + + // create a record + record = utils.createRecord(rmFolder, "record.txt", "title"); + + // add the record aspect (that has a mandatory property) + Map properties = new HashMap<>(1); + properties.put(PROP_TEST, "something"); + nodeService.addAspect(record, ASPECT_TEST, properties); } - + public void when() { - // complete record - result = rmActionService.executeRecordsManagementAction(record, "declareRecord"); + // complete record + result = rmActionService.executeRecordsManagementAction(record, "declareRecord"); } - - public void then() + + public void then() { - assertNotNull(result); - assertNull(result.getValue()); - assertTrue(nodeService.hasAspect(record, ASPECT_DECLARED_RECORD)); + assertNotNull(result); + assertNull(result.getValue()); + assertTrue(nodeService.hasAspect(record, ASPECT_DECLARED_RECORD)); } - }); + }); + } + + /** + * Helper method to create a Custom Metadata definition in the RM custom model + * + * @param mandatory specifies if metadata is mandatory + */ + private void defineCustomMetadata(QName propId, QName typeName, boolean mandatory) throws Exception + { + rmAdminService.addCustomPropertyDefinition( + propId, + typeName, + "SomeCustomDefLabel", + DataTypeDefinition.TEXT, + null, + null, + null, + false, + mandatory, + false, + null + ); + } + + /** + * Helper method to populate the Custom Metadata for the record + */ + private void populateCustomMetadata(NodeRef record, QName propId) + { + Map properties = new HashMap<>(1); + properties.put(propId, "SomeCustomValue"); + nodeService.addAspect(record, propId, properties); + } + + /** + * Helper method to remove the Custom Metadata definition in the RM custom model + */ + private void removeCustomMetadata(QName propId) + { + rmAdminService.removeCustomPropertyDefinition(propId); } } diff --git a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/CommonRMTestUtils.java b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/CommonRMTestUtils.java index 20b8947d0a..f0b8e2829f 100644 --- a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/CommonRMTestUtils.java +++ b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/CommonRMTestUtils.java @@ -298,6 +298,43 @@ public class CommonRMTestUtils implements RecordsManagementModel return record; } + /** + * Helper method to create a non-electronic record in a record folder. + * + * @param recordFolder record folder + * @param name name of the non-electronic record + * @param title title of the non-electronic record + * @return {@link NodeRef} non-electronic record node reference + */ + public NodeRef createNonElectronicRecord(NodeRef recordFolder, String name, String title) + { + Map props = new HashMap(1); + props.put(ContentModel.PROP_TITLE, title); + return createNonElectronicRecordImpl(recordFolder, name, props); + } + + /** + * Helper to consolidate creation of non-electronic record + */ + private NodeRef createNonElectronicRecordImpl(NodeRef recordFolder, String name, Map properties) + { + // Create the document + if (properties == null) + { + properties = new HashMap(1); + } + if (!properties.containsKey(ContentModel.PROP_NAME)) + { + properties.put(ContentModel.PROP_NAME, name); + } + NodeRef record = nodeService.createNode(recordFolder, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, name), + RecordsManagementModel.TYPE_NON_ELECTRONIC_DOCUMENT, + properties).getChildRef(); + return record; + } + /** * Helper method to complete record. */ From 11f9c6c3e7d691ca2567ebd9708c789c24fd2c48 Mon Sep 17 00:00:00 2001 From: Sara Aspery Date: Tue, 6 Jun 2017 16:49:56 +0100 Subject: [PATCH 04/16] RM-5175_Complete_record_with_missing_metadata --- .../org_alfresco_module_rm/action/impl/DeclareRecordAction.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/DeclareRecordAction.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/DeclareRecordAction.java index 83329105a4..a57d965306 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/DeclareRecordAction.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/DeclareRecordAction.java @@ -195,12 +195,12 @@ public class DeclareRecordAction extends RMActionExecuterAbstractBase } // check for missing mandatory metadata from custom aspect definitions + // TODO should not apply record custom metadata for non-electronic and vice versa if (result) { Collection aspects = this.getDictionaryService().getAspects(RM_CUSTOM_MODEL); for (QName aspect : aspects) { - // TODO should not apply record custom metadata for non-electronic and vice versa AspectDefinition aspectDef = this.getDictionaryService().getAspect(aspect); for (PropertyDefinition propDef : aspectDef.getProperties().values()) { From c290e0db59eccde86b98c49aa1e9087eb778a96f Mon Sep 17 00:00:00 2001 From: Tuna Aksoy Date: Tue, 6 Jun 2017 18:11:16 +0100 Subject: [PATCH 05/16] Add the overlays folder for rm-community-rest-api-explorer and rm-enterprise-rest-api-explorer to gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index db2278ab66..b9745c75ff 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,6 @@ test-output /rm-enterprise/rm-automation-enterprise/root rm-automation/src/test/resources/webdriver.properties + +/rm-community/rm-community-rest-api-explorer/overlays +/rm-enterprise/rm-enterprise-rest-api-explorer/overlays \ No newline at end of file From d05f1f89e2906b7f0a874221a6027fec96fd733a Mon Sep 17 00:00:00 2001 From: Tuna Aksoy Date: Wed, 22 Feb 2017 15:16:44 +0000 Subject: [PATCH 06/16] Updated source code management tags after moving to the new gitlab server (cherry picked from commit 42489cda9e096fa0515bdf982562ce2ab5721e13) --- pom.xml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index 2de2a93ed7..9d76551124 100644 --- a/pom.xml +++ b/pom.xml @@ -21,10 +21,10 @@ - scm:git:https://gitlab.alfresco.com/records-management/records-management.git - scm:git:https://gitlab.alfresco.com/records-management/records-management.git - https://gitlab.alfresco.com/records-management/records-management - V2.5.0 + scm:git:https://git.alfresco.com/records-management/records-management.git + scm:git:https://git.alfresco.com/records-management/records-management.git + https://git.alfresco.com/records-management/records-management + HEAD @@ -97,9 +97,9 @@ UTF-8 -Xmx1024m -XX:MaxPermSize=256m -Duser.language=en -Dcom.sun.management.jmxremote - - true - false + + true + false @@ -313,7 +313,7 @@ - + org.codehaus.mojo license-maven-plugin From fc6de3bad4c204e1b84bd99637966849cae8c187 Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Thu, 8 Jun 2017 10:27:39 +0100 Subject: [PATCH 07/16] [maven-release-plugin] prepare release V2.5.0.2 --- pom.xml | 4 ++-- rm-automation/pom.xml | 2 +- rm-community/pom.xml | 2 +- rm-community/rm-community-repo/pom.xml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 9d76551124..5826f6fbc5 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.alfresco alfresco-rm pom - 2.5.0.2-SNAPSHOT + 2.5.0.2 Alfresco Records Management @@ -24,7 +24,7 @@ scm:git:https://git.alfresco.com/records-management/records-management.git scm:git:https://git.alfresco.com/records-management/records-management.git https://git.alfresco.com/records-management/records-management - HEAD + V2.5.0.2 diff --git a/rm-automation/pom.xml b/rm-automation/pom.xml index d866a57b37..f15411cc91 100644 --- a/rm-automation/pom.xml +++ b/rm-automation/pom.xml @@ -8,7 +8,7 @@ org.alfresco alfresco-rm - 2.5.0.2-SNAPSHOT + 2.5.0.2 diff --git a/rm-community/pom.xml b/rm-community/pom.xml index c7e2c8efbf..dbc6bc217f 100644 --- a/rm-community/pom.xml +++ b/rm-community/pom.xml @@ -8,7 +8,7 @@ org.alfresco alfresco-rm - 2.5.0.2-SNAPSHOT + 2.5.0.2 diff --git a/rm-community/rm-community-repo/pom.xml b/rm-community/rm-community-repo/pom.xml index 6f867d21c0..be017c956d 100644 --- a/rm-community/rm-community-repo/pom.xml +++ b/rm-community/rm-community-repo/pom.xml @@ -9,7 +9,7 @@ org.alfresco alfresco-rm-community - 2.5.0.2-SNAPSHOT + 2.5.0.2 From 939d9a60ffb43b01eb1c94bb8ac8c0436921e703 Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Thu, 8 Jun 2017 10:27:43 +0100 Subject: [PATCH 08/16] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- rm-automation/pom.xml | 2 +- rm-community/pom.xml | 2 +- rm-community/rm-community-repo/pom.xml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 5826f6fbc5..a3e618d68f 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.alfresco alfresco-rm pom - 2.5.0.2 + 2.5.0.3-SNAPSHOT Alfresco Records Management @@ -24,7 +24,7 @@ scm:git:https://git.alfresco.com/records-management/records-management.git scm:git:https://git.alfresco.com/records-management/records-management.git https://git.alfresco.com/records-management/records-management - V2.5.0.2 + HEAD diff --git a/rm-automation/pom.xml b/rm-automation/pom.xml index f15411cc91..1efa9b7889 100644 --- a/rm-automation/pom.xml +++ b/rm-automation/pom.xml @@ -8,7 +8,7 @@ org.alfresco alfresco-rm - 2.5.0.2 + 2.5.0.3-SNAPSHOT diff --git a/rm-community/pom.xml b/rm-community/pom.xml index dbc6bc217f..a75c9eb640 100644 --- a/rm-community/pom.xml +++ b/rm-community/pom.xml @@ -8,7 +8,7 @@ org.alfresco alfresco-rm - 2.5.0.2 + 2.5.0.3-SNAPSHOT diff --git a/rm-community/rm-community-repo/pom.xml b/rm-community/rm-community-repo/pom.xml index be017c956d..98406408a2 100644 --- a/rm-community/rm-community-repo/pom.xml +++ b/rm-community/rm-community-repo/pom.xml @@ -9,7 +9,7 @@ org.alfresco alfresco-rm-community - 2.5.0.2 + 2.5.0.3-SNAPSHOT From a3747f27b14c42629be543a1676e2995a8d7a755 Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Fri, 9 Jun 2017 08:36:30 +0100 Subject: [PATCH 09/16] [maven-release-plugin] prepare release V2.5.1.1 --- pom.xml | 4 ++-- rm-automation/pom.xml | 2 +- rm-community/pom.xml | 2 +- rm-community/rm-community-repo/pom.xml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 5c8ce751b6..1d857858c3 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.alfresco alfresco-rm pom - 2.5.1.1-SNAPSHOT + 2.5.1.1 Alfresco Records Management @@ -24,7 +24,7 @@ scm:git:https://git.alfresco.com/records-management/records-management.git scm:git:https://git.alfresco.com/records-management/records-management.git https://git.alfresco.com/records-management/records-management - HEAD + V2.5.1.1 diff --git a/rm-automation/pom.xml b/rm-automation/pom.xml index c20b54295a..6351a18a4f 100644 --- a/rm-automation/pom.xml +++ b/rm-automation/pom.xml @@ -8,7 +8,7 @@ org.alfresco alfresco-rm - 2.5.1.1-SNAPSHOT + 2.5.1.1 diff --git a/rm-community/pom.xml b/rm-community/pom.xml index 1290c86b62..e08d1a0111 100644 --- a/rm-community/pom.xml +++ b/rm-community/pom.xml @@ -8,7 +8,7 @@ org.alfresco alfresco-rm - 2.5.1.1-SNAPSHOT + 2.5.1.1 diff --git a/rm-community/rm-community-repo/pom.xml b/rm-community/rm-community-repo/pom.xml index 719ec4407d..216bccc851 100644 --- a/rm-community/rm-community-repo/pom.xml +++ b/rm-community/rm-community-repo/pom.xml @@ -9,7 +9,7 @@ org.alfresco alfresco-rm-community - 2.5.1.1-SNAPSHOT + 2.5.1.1 From 56bf07c511c7945ffe3acdd2c2765849da379b37 Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Fri, 9 Jun 2017 08:36:33 +0100 Subject: [PATCH 10/16] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- rm-automation/pom.xml | 2 +- rm-community/pom.xml | 2 +- rm-community/rm-community-repo/pom.xml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 1d857858c3..aaadd74c68 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.alfresco alfresco-rm pom - 2.5.1.1 + 2.5.1.2-SNAPSHOT Alfresco Records Management @@ -24,7 +24,7 @@ scm:git:https://git.alfresco.com/records-management/records-management.git scm:git:https://git.alfresco.com/records-management/records-management.git https://git.alfresco.com/records-management/records-management - V2.5.1.1 + HEAD diff --git a/rm-automation/pom.xml b/rm-automation/pom.xml index 6351a18a4f..f3d3c0105f 100644 --- a/rm-automation/pom.xml +++ b/rm-automation/pom.xml @@ -8,7 +8,7 @@ org.alfresco alfresco-rm - 2.5.1.1 + 2.5.1.2-SNAPSHOT diff --git a/rm-community/pom.xml b/rm-community/pom.xml index e08d1a0111..17d9f594a6 100644 --- a/rm-community/pom.xml +++ b/rm-community/pom.xml @@ -8,7 +8,7 @@ org.alfresco alfresco-rm - 2.5.1.1 + 2.5.1.2-SNAPSHOT diff --git a/rm-community/rm-community-repo/pom.xml b/rm-community/rm-community-repo/pom.xml index 216bccc851..29ada5811a 100644 --- a/rm-community/rm-community-repo/pom.xml +++ b/rm-community/rm-community-repo/pom.xml @@ -9,7 +9,7 @@ org.alfresco alfresco-rm-community - 2.5.1.1 + 2.5.1.2-SNAPSHOT From d98d3e9c69205c8bab61d8c70ea33c2db3b96951 Mon Sep 17 00:00:00 2001 From: Sara Aspery Date: Sat, 10 Jun 2017 23:50:15 +0100 Subject: [PATCH 11/16] RM-5175_Complete_record_with_missing_metadata --- .../action/impl/DeclareRecordAction.java | 41 ++++++++++++------- .../record/CompleteRecordTest.java | 14 +++---- 2 files changed, 33 insertions(+), 22 deletions(-) diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/DeclareRecordAction.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/DeclareRecordAction.java index a57d965306..2ccfee90cf 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/DeclareRecordAction.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/DeclareRecordAction.java @@ -28,8 +28,8 @@ package org.alfresco.module.org_alfresco_module_rm.action.impl; import java.io.Serializable; +import java.text.MessageFormat; import java.util.ArrayList; -import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.List; @@ -91,7 +91,7 @@ public class DeclareRecordAction extends RMActionExecuterAbstractBase { if (!getRecordService().isDeclared(actionedUponNodeRef)) { - List missingProperties = new ArrayList(5); + List missingProperties = new ArrayList<>(5); // Aspect not already defined - check mandatory properties then add if (!checkMandatoryPropertiesEnabled || mandatoryPropertiesSet(actionedUponNodeRef, missingProperties)) @@ -100,7 +100,7 @@ public class DeclareRecordAction extends RMActionExecuterAbstractBase try { // Add the declared aspect - Map declaredProps = new HashMap(2); + Map declaredProps = new HashMap<>(2); declaredProps.put(PROP_DECLARED_AT, new Date()); declaredProps.put(PROP_DECLARED_BY, AuthenticationUtil.getRunAsUser()); this.getNodeService().addAspect(actionedUponNodeRef, ASPECT_DECLARED_RECORD, declaredProps); @@ -195,22 +195,33 @@ public class DeclareRecordAction extends RMActionExecuterAbstractBase } // check for missing mandatory metadata from custom aspect definitions - // TODO should not apply record custom metadata for non-electronic and vice versa if (result) { - Collection aspects = this.getDictionaryService().getAspects(RM_CUSTOM_MODEL); - for (QName aspect : aspects) + QName aspect; + if (nodeRefType.equals(TYPE_NON_ELECTRONIC_DOCUMENT)) + //if (nodeRefType.getLocalName())) { - AspectDefinition aspectDef = this.getDictionaryService().getAspect(aspect); - for (PropertyDefinition propDef : aspectDef.getProperties().values()) - { - if (propDef.isMandatory() && nodeRefProps.get(propDef.getName()) == null) - { - logMissingProperty(propDef, missingProperties); + aspect = TYPE_NON_ELECTRONIC_DOCUMENT; + } + else + { + aspect = ASPECT_RECORD; + } + // get customAspectImpl + String localName = aspect.toPrefixString(getNamespaceService()).replace(":", ""); + localName = MessageFormat.format("{0}CustomProperties", localName); + QName customAspect = QName.createQName(RM_CUSTOM_URI, localName); - result = false; - break; - } + AspectDefinition aspectDef = this.getDictionaryService().getAspect(customAspect); + + for (PropertyDefinition propDef : aspectDef.getProperties().values()) + { + if (propDef.isMandatory() && nodeRefProps.get(propDef.getName()) == null) + { + logMissingProperty(propDef, missingProperties); + + result = false; + break; } } } diff --git a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/record/CompleteRecordTest.java b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/record/CompleteRecordTest.java index 0079cc3829..b73ecc6f78 100644 --- a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/record/CompleteRecordTest.java +++ b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/record/CompleteRecordTest.java @@ -285,7 +285,7 @@ public class CompleteRecordTest extends BaseRMTestCase defineCustomMetadata(CUSTOM_NON_ELECTRONIC_TEST, TYPE_NON_ELECTRONIC_DOCUMENT, MANDATORY_METADATA); // create a non-electronic record - nonElectronicRecord = utils.createRecord(rmFolder, "non-electronicRecord.txt", "title"); + nonElectronicRecord = utils.createNonElectronicRecord(rmFolder, "non-electronicRecord.txt", "title"); } public void when() @@ -329,10 +329,10 @@ public class CompleteRecordTest extends BaseRMTestCase action.setCheckMandatoryPropertiesEnabled(true); // create the custom metadata definition (that has a mandatory property) - defineCustomMetadata(CUSTOM_NON_ELECTRONIC_TEST, ASPECT_RECORD, MANDATORY_METADATA); + defineCustomMetadata(CUSTOM_NON_ELECTRONIC_TEST, TYPE_NON_ELECTRONIC_DOCUMENT, MANDATORY_METADATA); // create a non-electronic record - nonElectronicRecord = utils.createRecord(rmFolder, "non-electronicRecord.txt", "title"); + nonElectronicRecord = utils.createNonElectronicRecord(rmFolder, "non-electronicRecord.txt", "title"); // populate the custom metadata mandatory property for the record populateCustomMetadata(nonElectronicRecord, CUSTOM_NON_ELECTRONIC_TEST); @@ -424,11 +424,11 @@ public class CompleteRecordTest extends BaseRMTestCase // enable mandatory parameter check action.setCheckMandatoryPropertiesEnabled(true); - // create the custom metadata definition (that has a mandatory property) + // create the record custom metadata definition (that has a mandatory property) defineCustomMetadata(CUSTOM_ELECTRONIC_TEST, ASPECT_RECORD, MANDATORY_METADATA); // create a non-electronic record - nonElectronicRecord = utils.createRecord(rmFolder, "non-electronicRecord.txt", "title"); + nonElectronicRecord = utils.createNonElectronicRecord(rmFolder, "non-electronicRecord.txt", "title"); } public void when() @@ -471,8 +471,8 @@ public class CompleteRecordTest extends BaseRMTestCase // enable mandatory parameter check action.setCheckMandatoryPropertiesEnabled(true); - // create the custom metadata definition (that has a mandatory property) - defineCustomMetadata(CUSTOM_NON_ELECTRONIC_TEST, ASPECT_RECORD, MANDATORY_METADATA); + // create the non-electronic record custom metadata definition (that has a mandatory property) + defineCustomMetadata(CUSTOM_NON_ELECTRONIC_TEST, TYPE_NON_ELECTRONIC_DOCUMENT, MANDATORY_METADATA); // create a electronic record record = utils.createRecord(rmFolder, "electronicRecord.txt", "title"); From 7b837f9da1c4077294f70ecf2f49c3f9c102c3dc Mon Sep 17 00:00:00 2001 From: Andy Healey Date: Sun, 11 Jun 2017 08:23:46 +0100 Subject: [PATCH 12/16] Update gs-core-api.yaml for DOCS-2764 - UA review --- .../main/webapp/definitions/gs-core-api.yaml | 207 +++++++++--------- 1 file changed, 103 insertions(+), 104 deletions(-) diff --git a/rm-community/rm-community-rest-api-explorer/src/main/webapp/definitions/gs-core-api.yaml b/rm-community/rm-community-rest-api-explorer/src/main/webapp/definitions/gs-core-api.yaml index d4fc3b5d81..b875f2dd2b 100644 --- a/rm-community/rm-community-rest-api-explorer/src/main/webapp/definitions/gs-core-api.yaml +++ b/rm-community/rm-community-rest-api-explorer/src/main/webapp/definitions/gs-core-api.yaml @@ -25,7 +25,7 @@ tags: - name: record-folders description: Retrieve and manage record folders - name: gs-sites - description: Retrieve and manage the RM site + description: Retrieve and manage the Records Management site - name: records description: Perform record specific operations - name: files @@ -44,18 +44,18 @@ paths: post: tags: - gs-sites - summary: Create the RM site + summary: Create the Records Management (RM) site description: | - **Note:** this endpoint is available in RM 2.6 and newer versions. + **Note:** this endpoint is available in Records Management 2.6 and newer versions. Creates the RM site with the given details. **Note:** the id of a site cannot be updated once the site has been created. - For example, to create the RM site named "Records Management Title" with "Records Management Description" as description, the following body could be used: + For example, to create an RM site named "Records Management" with "Records Management Description" as description, the following body could be used: ```JSON { - "title": "Records Management Title", + "title": "Records Management", "description": "Records Management Description" } ``` @@ -100,9 +100,9 @@ paths: get: tags: - gs-sites - summary: Get the RM site + summary: Get the Records Management (RM) site description: | - **Note:** this endpoint is available in RM 2.6 and newer versions. + **Note:** this endpoint is available in Records Management 2.6 and newer versions. Gets information for RM site. @@ -118,7 +118,7 @@ paths: $ref: '#/definitions/RMSiteEntry' '400': description: | - Invalid parameter: GET request is suported only for the RM site + Invalid parameter: GET request is only supported for the RM site '401': description: Authentication failed '404': @@ -131,9 +131,9 @@ paths: delete: tags: - gs-sites - summary: Delete the RM site + summary: Delete the Records Management (RM) site description: | - **Note:** this endpoint is available in RM 2.6 and newer versions. + **Note:** this endpoint is available in Records Management 2.6 and newer versions. Deletes the RM site. operationId: deleteRMSite @@ -144,7 +144,7 @@ paths: description: Successful response '400': description: | - Invalid parameter: DELETE request is suported only for the RM site + Invalid parameter: DELETE request is only supported for the RM site '401': description: Authentication failed '403': @@ -159,14 +159,13 @@ paths: put: tags: - gs-sites - summary: Update the RM site + summary: Update the Records Management (RM) site description: | - **Note:** this endpoint is available in RM 2.6 and newer versions. + **Note:** this endpoint is available in Records Management 2.6 and newer versions. - Update the details for the RM site. Site Manager or otherwise a - (site) admin can update title or description. + Update the details for the RM site. Site Manager or other (site) admin can update title or description. - **Note**: the id, site visibility or compliance of the RM site cannot be updated once the RM site has been created. + **Note**: the id, site visibility, or compliance of the RM site cannot be updated once the site has been created. operationId: updateRMSite produces: @@ -205,9 +204,9 @@ paths: - file-plans summary: Get a file plan description: | - Get information for file plan **filePlanId** + Gets information for file plan **filePlanId** - Besides mandatory fields the file plan's aspects and properties are returned by default. + Mandatory fields and the file plan's aspects and properties are returned by default. You can use the **include** parameter (include=allowableOperations) to return additional information. operationId: getFilePlan @@ -226,11 +225,11 @@ paths: description: | Invalid parameter: **filePlanId** is not a valid format '401': - description: If authentication fails + description: Authentication failed '403': - description: If current user does not have permission to read **filePlanId** + description: Current user does not have permission to read **filePlanId** '404': - description: If **filePlanId** does not exist + description: "**filePlanId** does not exist" default: description: Unexpected error schema: @@ -275,13 +274,13 @@ paths: description: | Invalid parameter: The update request is invalid or **filePlanId** is not a valid format or **filePlanBodyUpdate** is invalid '401': - description: If authentication fails + description: Authentication failed '403': description: If current user does not have permission to update **filePlanId** '404': - description: If **filePlanId** does not exist + description: "**filePlanId** does not exist" '409': - description: If the updated name clashes with an existing fileplan + description: Updated name clashes with an existing fileplan '422': description: Model integrity exception, including file name with invalid characters default: @@ -316,11 +315,11 @@ paths: schema: $ref: '#/definitions/RecordCategoryPaging' '401': - description: If authentication fails + description: Authentication failed '403': - description: If current user does not have permission to read **filePlanId** + description: Current user does not have permission to read **filePlanId** '404': - description: If **filePlanId** does not exist + description: "**filePlanId** does not exist" default: description: Unexpected error schema: @@ -328,9 +327,9 @@ paths: post: tags: - file-plans - summary: Create record categories for given file plan + summary: Create record categories for a file plan description: | - Create a record category as a primary child of **filePlanId**. + Creates a record category as a primary child of **filePlanId**. You can set the **autoRename** boolean field to automatically resolve name clashes. If there is a name clash, then the API method tries to create @@ -413,13 +412,13 @@ paths: description: | Invalid parameter: **filePlanId** is not a valid format or **filePlanId** is invalid '401': - description: If authentication fails + description: Authentication failed '403': - description: If current user does not have permission to add children to **filePlanId** + description: Current user does not have permission to add children to **filePlanId** '404': - description: If **filePlanId** does not exist + description: "**filePlanId** does not exist" '409': - description: If new name clashes with an existing node in the current parent container + description: New name clashes with an existing node in the current parent container '422': description: Model integrity exception, including node name with invalid characters ## Unfiled records containers @@ -429,9 +428,9 @@ paths: - unfiled-containers summary: Get the unfiled records container description: | - Get information for unfiled records contianer **unfiledContainerId** + Gets information for unfiled records container **unfiledContainerId** - Besides mandatory fields the unfiled records container's aspects and properties are returned by default. + Mandatory fields and the unfiled records container's aspects and properties are returned by default. You can use the **include** parameter (include=allowableOperations) to return additional information. operationId: getUnfiledContainer @@ -450,11 +449,11 @@ paths: description: | Invalid parameter: **unfiledContainerId** is not a valid format '401': - description: If authentication fails + description: Authentication failed '403': - description: If current user does not have permission to read **unfiledContainerId** + description: Current user does not have permission to read **unfiledContainerId** '404': - description: If **unfiledContainerId** does not exist + description: "**unfiledContainerId** does not exist" default: description: Unexpected error schema: @@ -504,13 +503,13 @@ paths: description: | Invalid parameter: The update request is invalid or **unfiledContainerId** is not a valid format or **unfiledContainerBodyUpdate** is invalid '401': - description: If authentication fails + description: Authentication failed '403': - description: If current user does not have permission to update **unfiledContainerId** + description: Current user does not have permission to update **unfiledContainerId** '404': - description: If **unfiledContainerId** does not exist + description: "**unfiledContainerId** does not exist" '409': - description: If the updated name clashes with an existing root category of special container in the current fileplan + description: Updated name clashes with an existing root category of special container in the current fileplan '422': description: Model integrity exception, including file name with invalid characters default: @@ -545,11 +544,11 @@ paths: schema: $ref: '#/definitions/UnfiledContainerAssociationPaging' '401': - description: If authentication fails + description: Authentication failed '403': - description: If current user does not have permission to read **unfiledContainerId** + description: Current user does not have permission to read **unfiledContainerId** '404': - description: If **unfiledContainerId** does not exist + description: "**unfiledContainerId** does not exist" default: description: Unexpected error schema: @@ -559,7 +558,7 @@ paths: - unfiled-containers summary: Create a record or an unfiled record folder description: | - Create a record or an unfiled record folder as a primary child of **unfiledContainerId**. + Creates a record or an unfiled record folder as a primary child of **unfiledContainerId**. You can set the **autoRename** boolean field to automatically resolve name clashes. If there is a name clash, then the API method tries to create a unique name using an integer suffix. @@ -702,7 +701,7 @@ paths: - unfiled-record-folders summary: Get the unfiled record folder description: | - Get information for unfiled record folder id **unfiledRecordFolderId** + Gets information for unfiled record folder id **unfiledRecordFolderId** Mandatory fields and the unfiled record folder's aspects and properties are returned by default. @@ -798,7 +797,7 @@ paths: - unfiled-record-folders summary : Delete an unfiled record folder description: | - Deletes unfiled record folder **unfiledRecordFolderId**. + Deletes the unfiled record folder **unfiledRecordFolderId**. operationId: deleteUnfiledRecordFolder parameters: - $ref: '#/parameters/unfiledRecordFolderIdParam' @@ -851,11 +850,11 @@ paths: schema: $ref: '#/definitions/UnfiledRecordFolderAssociationPaging' '401': - description: If authentication fails + description: Authentication failed '403': - description: If current user does not have permission to read **unfiledRecordFolderId** + description: Current user does not have permission to read **unfiledRecordFolderId** '404': - description: If **unfiledRecordFolderId** does not exist + description: "**unfiledRecordFolderId** does not exist" default: description: Unexpected error schema: @@ -902,7 +901,7 @@ paths: } ``` - You can create an empty electronic record: + You can create an empty electronic record like this: ```JSON { "name":"My Electronic Record", @@ -992,13 +991,13 @@ paths: description: | Invalid parameter: **unfiledRecordFolderId** is not a valid format or **unfiledRecordFolderId** is invalid '401': - description: If authentication fails + description: Authentication failed '403': - description: If current user does not have permission to add children to **unfiledRecordFolderId** + description: Current user does not have permission to add children to **unfiledRecordFolderId** '404': - description: If **unfiledRecordFolderId** does not exist + description: "**unfiledRecordFolderId** does not exist" '409': - description: If new name clashes with an existing node in the current parent container + description: New name clashes with an existing node in the current parent container '422': description: Model integrity exception, including node name with invalid characters ## Record categories @@ -1008,9 +1007,9 @@ paths: - record-categories summary: Get a record category description: | - Get information for record category **recordCategoryId** + Gets information for record category **recordCategoryId** - Besides mandatory fields the record category's aspects and properties are returned by default. + Mandatory fields and the record category's aspects and properties are returned by default. You can use the **include** parameter (include=allowableOperations) to return additional information. operationId: getRecordCategory @@ -1030,11 +1029,11 @@ paths: description: | Invalid parameter: **recordCategoryId** is not a valid format '401': - description: If authentication fails + description: Authentication failed '403': - description: If current user does not have permission to read **recordCategoryId** + description: Current user does not have permission to read **recordCategoryId** '404': - description: If **recordCategoryId** does not exist + description: "**recordCategoryId** does not exist" default: description: Unexpected error schema: @@ -1158,7 +1157,7 @@ paths: schema: $ref: '#/definitions/RecordCategoryChildPaging' '401': - description: Authentication fails + description: Authentication failed '403': description: Current user does not have permission to read **recordCategoryId** '404': @@ -1294,7 +1293,7 @@ paths: description: | Invalid parameter: **recordCategoryId** is not a valid format or **nodeBodyCreate** is invalid '401': - description: Authentication fails + description: Authentication failed '403': description: Current user does not have permission to add children to **recordCategoryId** '404': @@ -1310,9 +1309,9 @@ paths: - record-folders summary: Get a record folder description: | - Get information for record folder **recordFolderId** + Gets information for record folder **recordFolderId** - Besides mandatory fields the record folder's aspects and properties are returned by default. + Mandatory fields and the record folder's aspects and properties are returned by default. You can use the **include** parameter (include=allowableOperations) to return additional information. operationId: getRecordFolder @@ -1434,7 +1433,7 @@ paths: - record-folders summary: List records description: | - Returns a list of records. + Gets a list of records. Minimal information for each record is returned by default. @@ -1601,7 +1600,7 @@ paths: - records summary: Get a record description: | - Get information for record **recordId** + Gets information for record **recordId** Mandatory fields and the record's aspects and properties are returned by default. @@ -1622,7 +1621,7 @@ paths: description: | Invalid parameter: **recordId** is not a valid format '401': - description: Authentication fails + description: Authentication failed '403': description: Current user does not have permission to read **recordId** '404': @@ -1708,7 +1707,7 @@ paths: description: | Invalid parameter: **recordId** is not a valid format '401': - description: Authentication fails + description: Authentication failed '403': description: Current user does not have permission to delete **recordId** '404': @@ -1806,7 +1805,7 @@ paths: tags: - files summary: Declare as record - description: Declares the file **fileId** in the unfiled record container. + description: Declares the file **fileId** in the unfiled records container. operationId: declareRecord parameters: - name: fileId @@ -1854,7 +1853,7 @@ paths: - transfer-containers summary: Get a transfer container description: | - Get information for transfer container **transferContainerId** + Gets information for transfer container **transferContainerId** Mandatory fields and the transfer container's aspects and properties are returned by default. @@ -1875,11 +1874,11 @@ paths: description: | Invalid parameter: **transferContainerId** is not a valid format '401': - description: If authentication fails + description: Authentication failed '403': - description: If current user does not have permission to read **transferContainerId** + description: Current user does not have permission to read **transferContainerId** '404': - description: If **transferContainerId** does not exist + description: "**transferContainerId** does not exist" default: description: Unexpected error schema: @@ -1928,13 +1927,13 @@ paths: description: | Invalid parameter: the update request is invalid or **transferContainerId** is not a valid format or **nodeBody** is invalid '401': - description: If authentication fails + description: Authentication failed '403': - description: If current user does not have permission to update **transferContainerId** + description: Current user does not have permission to update **transferContainerId** '404': - description: If **transferContainerId** does not exist + description: "**transferContainerId** does not exist" '409': - description: If the updated name clashes with an existing node in the current parent folder + description: Updated name clashes with an existing node in the current parent folder '422': description: Model integrity exception, including transfer container name with invalid characters default: @@ -1968,11 +1967,11 @@ paths: schema: $ref: '#/definitions/TransferContainerAssociationPaging' '401': - description: If authentication fails + description: Authentication failed '403': - description: If current user does not have permission to read **transferContainerId** + description: Current user does not have permission to read **transferContainerId** '404': - description: If **transferContainerId** does not exist + description: "**transferContainerId** does not exist" default: description: Unexpected error schema: @@ -1984,9 +1983,9 @@ paths: - transfers summary: Get a transfer description: | - Get information for transfer **transferId** + Gets information for transfer **transferId** - Besides mandatory fields the transfer's aspects and properties are returned by default. + Mandatory fields and the transfer's aspects and properties are returned by default. You can use the **include** parameter (include=allowableOperations) to return additional information. operationId: getTransfer @@ -2005,11 +2004,11 @@ paths: description: | Invalid parameter: **transferId** is not a valid format '401': - description: If authentication fails + description: Authentication failed '403': - description: If current user does not have permission to read **transferId** + description: Current user does not have permission to read **transferId** '404': - description: If **transferId** does not exist + description: "**transferId** does not exist" default: description: Unexpected error schema: @@ -2020,7 +2019,7 @@ paths: - transfers summary: List transfer's children description: | - Returns a list of transfer's children. + Gets a list of transfer's children. Minimal information for each child is returned by default. @@ -2041,11 +2040,11 @@ paths: schema: $ref: '#/definitions/TransferAssociationPaging' '401': - description: If authentication fails + description: Authentication failed '403': - description: If current user does not have permission to read **transferId** + description: Current user does not have permission to read **transferId** '404': - description: If **transferId** does not exist + description: "**transferId** does not exist" default: description: Unexpected error schema: @@ -2088,7 +2087,7 @@ parameters: filePlanIncludeSourceParam: name: includeSource in: query - description: Also include **source** in addition to **entries** with folder information on the parent node – the specified parent **filePlanId** + description: Also include **source** (in addition to **entries**) with folder information on the parent node – the specified parent **filePlanId** required: false type: boolean ## Unfiled records containers @@ -2096,14 +2095,14 @@ parameters: name: unfiledContainerId in: path description: - The identifier of a unfiled records container. You can use the **-unfiled-** alias. + The identifier of an unfiled records container. You can use the **-unfiled-** alias. required: true type: string unfiledContainerEntryIncludeParam: name: include in: query description: | - Returns additional information about the unfiled records container children. Any optional field from the response model can be requested. For example: + Returns additional information about the unfiled records container's children. Any optional field from the response model can be requested. For example: * allowableOperations * path required: false @@ -2115,7 +2114,7 @@ parameters: name: include in: query description: | - Returns additional information about the unfiled records container children. Any optional field from the response model can be requested. For example: + Returns additional information about the unfiled records container's children. Any optional field from the response model can be requested. For example: * allowableOperations * aspectNames * association @@ -2129,7 +2128,7 @@ parameters: unfiledContainerIncludeSourceParam: name: includeSource in: query - description: Also include **source** in addition to **entries** with folder information on the parent node – the specified parent **unfiledContainerId** + description: Also include **source** (in addition to **entries**) with folder information on the parent node – the specified parent **unfiledContainerId** required: false type: boolean ## Unfiled record folders @@ -2137,14 +2136,14 @@ parameters: name: unfiledRecordFolderId in: path description: - The identifier of a unfiled record folder. + The identifier of an unfiled record folder. required: true type: string unfiledRecordFolderEntryIncludeParam: name: include in: query description: | - Returns additional information about the unfiled records container children. Any optional field from the response model can be requested. For example: + Returns additional information about the unfiled records container's children. Any optional field from the response model can be requested. For example: * allowableOperations * path required: false @@ -2156,7 +2155,7 @@ parameters: name: include in: query description: | - Returns additional information about the unfiled records container children. Any optional field from the response model can be requested. For example: + Returns additional information about the unfiled records container's children. Any optional field from the response model can be requested. For example: * allowableOperations * aspectNames * association @@ -2177,7 +2176,7 @@ parameters: unfiledRecordFolderIncludeSourceParam: name: includeSource in: query - description: Also include **source** in addition to **entries** with folder information on the parent node – either the specified parent **unfiledRecordFolderId**, or as resolved by **relativePath**. + description: Also include **source** (in addition to **entries**) with folder information on the parent node – either the specified parent **unfiledRecordFolderId**, or as resolved by **relativePath**. required: false type: boolean unfiledRecordFolderAndContainerWhereParam: @@ -2225,7 +2224,7 @@ parameters: recordCategoryIncludeSourceParam: name: includeSource in: query - description: Also include **source** in addition to **entries** with folder information on the parent node – either the specified parent **recordCategoryId**, or as resolved by **relativePath**. + description: Also include **source** (in addition to **entries**) with folder information on the parent node – either the specified parent **recordCategoryId**, or as resolved by **relativePath**. required: false type: boolean recordCategoryChildEntryIncludeParam: @@ -2284,7 +2283,7 @@ parameters: recordFolderIncludeSourceParam: name: includeSource in: query - description: Also include **source** in addition to **entries** with record information on the parent folder – the specified parent **recordFolderId** + description: Also include **source** (in addition to **entries**) with record information on the parent folder – the specified parent **recordFolderId** required: false type: boolean recordFolderChildEntryIncludeParam: @@ -2356,7 +2355,7 @@ parameters: transferContainerIncludeSourceParam: name: includeSource in: query - description: Also include **source** in addition to **entries** with folder information on the specified parent **transferContainerId**. + description: Also include **source** (in addition to **entries**) with folder information on the specified parent **transferContainerId**. required: false type: boolean ## Transfers @@ -2383,7 +2382,7 @@ parameters: transferIncludeSourceParam: name: includeSource in: query - description: Also include **source** in addition to **entries** with folder information on the specified parent **transferId**. + description: Also include **source** (in addition to **entries**) with folder information on the specified parent **transferId**. required: false type: boolean transferChildEntryIncludeParam: @@ -3718,4 +3717,4 @@ definitions: - SiteConsumer - SiteCollaborator - SiteContributor - - SiteManager \ No newline at end of file + - SiteManager From 4fb2a32f9d71f97d2d33c627f530d33e2b7fbc42 Mon Sep 17 00:00:00 2001 From: Andy Healey Date: Mon, 12 Jun 2017 10:40:35 +0100 Subject: [PATCH 13/16] Update gs-core-api.yaml with Tuna feedback for DOCS-2764 --- .../src/main/webapp/definitions/gs-core-api.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rm-community/rm-community-rest-api-explorer/src/main/webapp/definitions/gs-core-api.yaml b/rm-community/rm-community-rest-api-explorer/src/main/webapp/definitions/gs-core-api.yaml index b875f2dd2b..a9a7e3f9a7 100644 --- a/rm-community/rm-community-rest-api-explorer/src/main/webapp/definitions/gs-core-api.yaml +++ b/rm-community/rm-community-rest-api-explorer/src/main/webapp/definitions/gs-core-api.yaml @@ -144,7 +144,7 @@ paths: description: Successful response '400': description: | - Invalid parameter: DELETE request is only supported for the RM site + Invalid parameter: DELETE request is only supported for the RM site '401': description: Authentication failed '403': @@ -430,7 +430,7 @@ paths: description: | Gets information for unfiled records container **unfiledContainerId** - Mandatory fields and the unfiled records container's aspects and properties are returned by default. + Mandatory fields and the unfiled records container's aspects and properties are returned by default. You can use the **include** parameter (include=allowableOperations) to return additional information. operationId: getUnfiledContainer @@ -1853,7 +1853,7 @@ paths: - transfer-containers summary: Get a transfer container description: | - Gets information for transfer container **transferContainerId** + Gets information for transfer container **transferContainerId** Mandatory fields and the transfer container's aspects and properties are returned by default. From 1712bac9957a223f816ad547db2f178cb0264b1c Mon Sep 17 00:00:00 2001 From: Tuna Aksoy Date: Mon, 12 Jun 2017 16:17:24 +0100 Subject: [PATCH 14/16] RM-4689 (Can delete all RM site content through Repository browser) Manually backported following changes: RM-4293 734c1b2a82d900c9468028700ce54a0f47f61d4f RM-4326 a4aac053a86ad58ca163836047edd5ee5a3a6e0e c6ca74ab26a37b0fd0fc7c56bb8b3311c9ce90cf --- .gitignore | 3 + .../rm-capabilities-fileplan-context.xml | 31 +++++ .../rm-capabilities-group-context.xml | 2 + .../rm-model-context.xml | 12 ++ .../rm-service-context.xml | 8 +- .../model/BaseBehaviourBean.java | 61 ++++++++- .../model/rma/type/FilePlanType.java | 125 +++++++++++++++--- .../model/rma/type/HoldContainerType.java | 41 +++++- .../model/rma/type/RecordCategoryType.java | 21 ++- .../model/rma/type/RmSiteType.java | 79 ++++++++++- .../model/rma/type/TransferContainerType.java | 53 ++++++-- .../model/rma/type/TransferType.java | 79 +++++++++++ .../rma/type/UnfiledRecordContainerType.java | 97 ++++++++++++++ .../rma/type/UnfiledRecordFolderType.java | 70 ++++++++++ .../transfer/TransferServiceImpl.java | 57 +++++--- .../test/integration/issue/RM3341Test.java | 24 ++-- .../test/integration/issue/RM4293Test.java | 125 ++++++++++++++++++ .../type/TransferContainerTypeUnitTest.java | 54 ++++---- 18 files changed, 831 insertions(+), 111 deletions(-) create mode 100644 rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/TransferType.java create mode 100644 rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/UnfiledRecordContainerType.java create mode 100644 rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/UnfiledRecordFolderType.java create mode 100644 rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/issue/RM4293Test.java diff --git a/.gitignore b/.gitignore index dfdc3e2322..f5146b465f 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,6 @@ test-output /rm-enterprise/rm-automation-enterprise/root rm-automation/src/test/resources/webdriver.properties + +/rm-community/rm-community-rest-api-explorer/overlays +/rm-enterprise/rm-enterprise-rest-api-explorer/overlays \ No newline at end of file diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/capability/rm-capabilities-fileplan-context.xml b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/capability/rm-capabilities-fileplan-context.xml index e8eeae8602..b7d1e33fd6 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/capability/rm-capabilities-fileplan-context.xml +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/capability/rm-capabilities-fileplan-context.xml @@ -94,4 +94,35 @@ + + + + + TRANSFER_CONTAINER + + + + + + + + + + + + + + + HOLD_CONTAINER + + + + + + + + + \ No newline at end of file diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/capability/rm-capabilities-group-context.xml b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/capability/rm-capabilities-group-context.xml index 91a50ef89d..3b67d92639 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/capability/rm-capabilities-group-context.xml +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/capability/rm-capabilities-group-context.xml @@ -25,6 +25,8 @@ + + diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-model-context.xml b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-model-context.xml index 7eb9c17eda..8fe840ba94 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-model-context.xml +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-model-context.xml @@ -76,6 +76,9 @@ + + + @@ -84,6 +87,14 @@ + + + + + + + @@ -105,6 +116,7 @@ + diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml index 0298088a35..f7e95ba278 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml @@ -173,6 +173,8 @@ + + @@ -579,9 +581,9 @@ parent="baseService"> - - - + + + diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/BaseBehaviourBean.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/BaseBehaviourBean.java index bd09ccb6b1..4fc2b96b93 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/BaseBehaviourBean.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/BaseBehaviourBean.java @@ -27,15 +27,20 @@ package org.alfresco.module.org_alfresco_module_rm.model; +import com.google.common.collect.Sets; import java.util.HashMap; +import java.util.List; import java.util.Map; - import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.module.org_alfresco_module_rm.util.ServiceBaseImpl; +import org.alfresco.repo.node.integrity.IntegrityException; import org.alfresco.repo.policy.BehaviourFilter; import org.alfresco.repo.policy.annotation.BehaviourRegistry; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.extensions.surf.util.I18NUtil; /** * Convenient base class for behaviour beans. @@ -50,6 +55,12 @@ public abstract class BaseBehaviourBean extends ServiceBaseImpl /** Logger */ protected static final Log LOGGER = LogFactory.getLog(BaseBehaviourBean.class); + /** + * I18N + */ + protected static final String UNIQUE_CHILD_TYPE_ERROR = "rm.action.unique.child.type-error-message"; + protected static final String MULTIPLE_CHILDREN_TYPE_ERROR = "rm.action.multiple.children.type-error-message"; + /** behaviour filter */ protected BehaviourFilter behaviourFilter; @@ -87,4 +98,52 @@ public abstract class BaseBehaviourBean extends ServiceBaseImpl return behaviours.get(name); } + /** + * Helper method that checks if the newly created child association complies with the RM rules + * + * @param parent the parent node + * @param childType the child node + * @param acceptedUniqueChildType a list of node types that are accepted as children of the provided parent only once + * @param acceptedMultipleChildType a list of node types that are accepted as children of the provided parent multiple times + * @throws IntegrityException if the child association doesn't comply with the RM rules + */ + protected void validateNewChildAssociation(NodeRef parent, NodeRef child, List acceptedUniqueChildType, List acceptedMultipleChildType) throws IntegrityException + { + QName childType = getInternalNodeService().getType(child); + if (acceptedUniqueChildType.contains(childType)) + { + // check the user is not trying to create multiple children of a type that is only accepted once + if (nodeService.getChildAssocs(parent, Sets.newHashSet(childType)) + .size() > 1) + { + throw new IntegrityException(I18NUtil.getMessage(UNIQUE_CHILD_TYPE_ERROR), null); + } + } + else if (!acceptedMultipleChildType.contains(childType)) + { + throw new IntegrityException(I18NUtil.getMessage(MULTIPLE_CHILDREN_TYPE_ERROR, childType), null); + } + } + + /** + * Helper method that checks if the newly created child association is between the sub-types of accepted types. + * + * @param child the child node + * @param acceptedMultipleChildType a list of node types that are accepted as children of the provided parent multiple times + * @throws IntegrityException if the child association isn't between the sub-types of accepted types + */ + protected void validateNewChildAssociationSubTypesIncluded(NodeRef child, List acceptedMultipleChildType) + throws IntegrityException + { + QName childType = getInternalNodeService().getType(child); + for (QName type : acceptedMultipleChildType) + { + if (instanceOf(childType, type)) + { + return; + } + } + //no match was found in sub-types of permitted types list + throw new IntegrityException(I18NUtil.getMessage(MULTIPLE_CHILDREN_TYPE_ERROR, childType), null); + } } diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/FilePlanType.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/FilePlanType.java index 8344e8f696..485c7b5543 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/FilePlanType.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/FilePlanType.java @@ -27,7 +27,8 @@ package org.alfresco.module.org_alfresco_module_rm.model.rma.type; -import org.alfresco.error.AlfrescoRuntimeException; +import java.util.Arrays; +import java.util.List; import org.alfresco.model.ContentModel; import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService; import org.alfresco.module.org_alfresco_module_rm.identifier.IdentifierService; @@ -36,6 +37,7 @@ import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; import org.alfresco.module.org_alfresco_module_rm.recordfolder.RecordFolderService; import org.alfresco.module.org_alfresco_module_rm.role.FilePlanRoleService; import org.alfresco.repo.node.NodeServicePolicies; +import org.alfresco.repo.node.integrity.IntegrityException; import org.alfresco.repo.policy.Behaviour.NotificationFrequency; import org.alfresco.repo.policy.annotation.Behaviour; import org.alfresco.repo.policy.annotation.BehaviourBean; @@ -45,6 +47,7 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; /** * rma:filePlan behaviour bean @@ -59,8 +62,13 @@ import org.alfresco.service.cmr.repository.NodeRef; public class FilePlanType extends BaseBehaviourBean implements NodeServicePolicies.OnCreateChildAssociationPolicy, NodeServicePolicies.OnCreateNodePolicy, - NodeServicePolicies.OnDeleteNodePolicy + NodeServicePolicies.OnDeleteNodePolicy, + NodeServicePolicies.BeforeDeleteNodePolicy { + private final static List ACCEPTED_UNIQUE_CHILD_TYPES = Arrays.asList(TYPE_HOLD_CONTAINER, TYPE_TRANSFER_CONTAINER, TYPE_UNFILED_RECORD_CONTAINER); + private final static List ACCEPTED_NON_UNIQUE_CHILD_TYPES = Arrays.asList(TYPE_RECORD_CATEGORY); + private static final String BEHAVIOUR_NAME = "onDeleteFilePlan"; + /** file plan service */ private FilePlanService filePlanService; @@ -73,6 +81,21 @@ public class FilePlanType extends BaseBehaviourBean /** file plan role service */ private FilePlanRoleService filePlanRoleService; + /** + * Unfiled Record Container Type behaviour bean + */ + private UnfiledRecordContainerType unfilerRecordContainerType; + + /** + * Transfer Container Type behaviour bean + */ + private TransferContainerType transferContainerType; + + /** + * Hold Container Type behaviour bean + */ + private HoldContainerType holdContainerType; + /** * @return File plan service */ @@ -137,6 +160,46 @@ public class FilePlanType extends BaseBehaviourBean this.filePlanRoleService = filePlanRoleService; } + /** + * @param unfilerRecordContainerType - unfiled record container type behaviour bean + */ + public void setUnfilerRecordContainerType(UnfiledRecordContainerType unfilerRecordContainerType) + { + this.unfilerRecordContainerType = unfilerRecordContainerType; + } + + /** + * @param transferContainerType - transfer container type behaviour bean + */ + public void setTransferContainerType(TransferContainerType transferContainerType) + { + this.transferContainerType = transferContainerType; + } + + /** + * @param holdContainerType - hold container type behaviour bean + */ + public void setHoldContainerType(HoldContainerType holdContainerType) + { + this.holdContainerType = holdContainerType; + } + + /** + * Disable the behaviours for this transaction + */ + public void disable() + { + getBehaviour(BEHAVIOUR_NAME).disable(); + } + + /** + * Enable behaviours for this transaction + */ + public void enable() + { + getBehaviour(BEHAVIOUR_NAME).enable(); + } + /** * @see org.alfresco.repo.node.NodeServicePolicies.OnCreateChildAssociationPolicy#onCreateChildAssociation(org.alfresco.service.cmr.repository.ChildAssociationRef, boolean) */ @@ -147,20 +210,17 @@ public class FilePlanType extends BaseBehaviourBean @Override public void onCreateChildAssociation(ChildAssociationRef childAssocRef, boolean bNew) { - // ensure we are not trying to put content in the file plan root node - NodeRef nodeRef = childAssocRef.getChildRef(); - if (instanceOf(nodeRef, ContentModel.TYPE_CONTENT)) + // We need to automatically cast the created folder to category if it is a plain folder + // This occurs if the RM folder has been created via IMap, WebDav, etc. Don't check subtypes. + // Some modules use hidden files to store information (see RM-3283) + if (nodeService.getType(childAssocRef.getChildRef()) + .equals(ContentModel.TYPE_FOLDER)) { - throw new AlfrescoRuntimeException("Operation failed, because you can't place content in the root of the file plan."); - } - - // ensure we are not trying to put a record folder in the root of the file plan - NodeRef parent = childAssocRef.getParentRef(); - if (getFilePlanService().isFilePlan(parent) && getRecordFolderService().isRecordFolder(nodeRef)) - { - throw new AlfrescoRuntimeException("Operation failed, because you can not place a record folder in the root of the file plan."); + nodeService.setType(childAssocRef.getChildRef(), TYPE_RECORD_CATEGORY); } + // check the created child is of an accepted type + validateNewChildAssociation(childAssocRef.getParentRef(), childAssocRef.getChildRef(), ACCEPTED_UNIQUE_CHILD_TYPES, ACCEPTED_NON_UNIQUE_CHILD_TYPES); } /** @@ -182,8 +242,8 @@ public class FilePlanType extends BaseBehaviourBean { // ensure rules are not inherited nodeService.addAspect(filePlan, RuleModel.ASPECT_IGNORE_INHERITED_RULES, null); - - // set the identifier + + // set the identifier if (nodeService.getProperty(filePlan, PROP_IDENTIFIER) == null) { String id = getIdentifierService().generateIdentifier(filePlan); @@ -201,15 +261,40 @@ public class FilePlanType extends BaseBehaviourBean /** * @see org.alfresco.repo.node.NodeServicePolicies.OnDeleteNodePolicy#onDeleteNode(org.alfresco.service.cmr.repository.ChildAssociationRef, boolean) */ - @Behaviour - ( - kind = BehaviourKind.CLASS, - notificationFrequency = NotificationFrequency.TRANSACTION_COMMIT - ) @Override + @Behaviour(kind = BehaviourKind.CLASS, + notificationFrequency = NotificationFrequency.FIRST_EVENT, + name = BEHAVIOUR_NAME) public void onDeleteNode(ChildAssociationRef childAssocRef, boolean archived) + { + unfilerRecordContainerType.enable(); + transferContainerType.enable(); + holdContainerType.enable(); + throw new IntegrityException("Operation failed. Deletion of File Plan is not allowed.", null); + } + + /** + * @see org.alfresco.repo.node.NodeServicePolicies.BeforeDeleteNodePolicy#beforeDeleteNode(org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + @Behaviour(kind = BehaviourKind.CLASS, + notificationFrequency = NotificationFrequency.FIRST_EVENT) + public void beforeDeleteNode(NodeRef nodeRef) + { + unfilerRecordContainerType.disable(); + transferContainerType.disable(); + holdContainerType.disable(); + } + + @Behaviour(kind = BehaviourKind.CLASS, + policy = "alf:onDeleteNode", + notificationFrequency = NotificationFrequency.TRANSACTION_COMMIT) + public void onDeleteNodeOnCommit(ChildAssociationRef childAssocRef, boolean archived) { // tear down the file plan roles getFilePlanRoleService().tearDownFilePlanRoles(childAssocRef.getChildRef()); + unfilerRecordContainerType.enable(); + transferContainerType.enable(); + holdContainerType.enable(); } } diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/HoldContainerType.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/HoldContainerType.java index 16e233e3aa..937f5de737 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/HoldContainerType.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/HoldContainerType.java @@ -26,15 +26,19 @@ */ package org.alfresco.module.org_alfresco_module_rm.model.rma.type; +import java.util.Arrays; +import java.util.List; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.module.org_alfresco_module_rm.model.BaseBehaviourBean; import org.alfresco.repo.node.NodeServicePolicies; +import org.alfresco.repo.node.integrity.IntegrityException; import org.alfresco.repo.policy.annotation.Behaviour; import org.alfresco.repo.policy.annotation.BehaviourBean; import org.alfresco.repo.policy.annotation.BehaviourKind; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; import org.springframework.extensions.surf.util.I18NUtil; /** @@ -45,9 +49,29 @@ import org.springframework.extensions.surf.util.I18NUtil; */ @BehaviourBean(defaultType = "rma:holdContainer") public class HoldContainerType extends BaseBehaviourBean - implements NodeServicePolicies.OnCreateChildAssociationPolicy, NodeServicePolicies.OnCreateNodePolicy + implements NodeServicePolicies.OnCreateChildAssociationPolicy, + NodeServicePolicies.OnCreateNodePolicy, + NodeServicePolicies.OnDeleteNodePolicy { private final static String MSG_ERROR_ADD_CONTENT_CONTAINER = "rm.service.error-add-content-container"; + private final static List ACCEPTED_NON_UNIQUE_CHILD_TYPES = Arrays.asList(TYPE_HOLD); + private static final String DELETE_BEHAVIOUR_NAME = "onDeleteHoldContainer"; + + /** + * Disable the behaviours for this transaction + */ + public void disable() + { + getBehaviour(DELETE_BEHAVIOUR_NAME).disable(); + } + + /** + * Enable behaviours for this transaction + */ + public void enable() + { + getBehaviour(DELETE_BEHAVIOUR_NAME).enable(); + } /** * On every event @@ -59,11 +83,8 @@ public class HoldContainerType extends BaseBehaviourBean @Behaviour(kind = BehaviourKind.ASSOCIATION) public void onCreateChildAssociation(ChildAssociationRef childAssocRef, boolean bNew) { - - NodeRef nodeRef = childAssocRef.getChildRef(); - if (instanceOf(nodeRef, ContentModel.TYPE_CONTENT) == true) { throw new AlfrescoRuntimeException( - I18NUtil.getMessage(MSG_ERROR_ADD_CONTENT_CONTAINER)); } - + // check the created child is of an accepted type + validateNewChildAssociationSubTypesIncluded(childAssocRef.getChildRef(), ACCEPTED_NON_UNIQUE_CHILD_TYPES); } @Override @@ -74,4 +95,12 @@ public class HoldContainerType extends BaseBehaviourBean I18NUtil.getMessage(MSG_ERROR_ADD_CONTENT_CONTAINER)); } } + + @Override + @Behaviour(kind = BehaviourKind.CLASS, + name = DELETE_BEHAVIOUR_NAME) + public void onDeleteNode(ChildAssociationRef childAssocRef, boolean isNodeArchived) + { + throw new IntegrityException("Operation failed. Deletion of Hold Container is not allowed.", null); + } } diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/RecordCategoryType.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/RecordCategoryType.java index 81dc259b95..eee8199119 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/RecordCategoryType.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/RecordCategoryType.java @@ -27,6 +27,9 @@ package org.alfresco.module.org_alfresco_module_rm.model.rma.type; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.module.org_alfresco_module_rm.model.BaseBehaviourBean; @@ -62,6 +65,9 @@ public class RecordCategoryType extends BaseBehaviourBean NodeServicePolicies.OnCreateNodePolicy, NodeServicePolicies.OnMoveNodePolicy { + private final static List ACCEPTED_UNIQUE_CHILD_TYPES = new ArrayList(); + private final static List ACCEPTED_NON_UNIQUE_CHILD_TYPES = Arrays.asList(TYPE_RECORD_CATEGORY, TYPE_RECORD_FOLDER); + /** vital record service */ protected VitalRecordService vitalRecordService; @@ -107,17 +113,22 @@ public class RecordCategoryType extends BaseBehaviourBean ) public void onCreateChildAssociation(ChildAssociationRef childAssocRef, boolean bNew) { - // ensure content is not placed directly into a record category - NodeRef nodeRef = childAssocRef.getChildRef(); - if (instanceOf(nodeRef, ContentModel.TYPE_CONTENT)) + QName childType = nodeService.getType(childAssocRef.getChildRef()); + + // We need to automatically cast the created folder to record folder if it is a plain folder + // This occurs if the RM folder has been created via IMap, WebDav, etc. Don't check subtypes. + // Some modules use hidden folders to store information (see RM-3283). + if (childType.equals(ContentModel.TYPE_FOLDER)) { - throw new AlfrescoRuntimeException("Operation failed, because you can't place content directly into a record category."); + nodeService.setType(childAssocRef.getChildRef(), TYPE_RECORD_FOLDER); } + validateNewChildAssociation(childAssocRef.getParentRef(), childAssocRef.getChildRef(), ACCEPTED_UNIQUE_CHILD_TYPES, ACCEPTED_NON_UNIQUE_CHILD_TYPES); + if (bNew) { // setup the record folder - recordFolderService.setupRecordFolder(nodeRef); + recordFolderService.setupRecordFolder(childAssocRef.getChildRef()); } } diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/RmSiteType.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/RmSiteType.java index 63fb27c048..682589598a 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/RmSiteType.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/RmSiteType.java @@ -27,15 +27,20 @@ package org.alfresco.module.org_alfresco_module_rm.model.rma.type; +import com.google.common.collect.Sets; import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; - import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; import org.alfresco.module.org_alfresco_module_rm.capability.CapabilityService; import org.alfresco.module.org_alfresco_module_rm.model.BaseBehaviourBean; import org.alfresco.module.org_alfresco_module_rm.search.RecordsManagementSearchService; import org.alfresco.repo.node.NodeServicePolicies; +import org.alfresco.repo.node.integrity.IntegrityException; import org.alfresco.repo.policy.Behaviour.NotificationFrequency; import org.alfresco.repo.policy.annotation.Behaviour; import org.alfresco.repo.policy.annotation.BehaviourBean; @@ -54,6 +59,7 @@ import org.alfresco.service.cmr.site.SiteVisibility; import org.alfresco.service.namespace.QName; import org.alfresco.util.ParameterCheck; import org.alfresco.util.PropertyMap; +import org.springframework.extensions.surf.util.I18NUtil; /** * Behaviour associated with the RM Site type @@ -68,12 +74,14 @@ import org.alfresco.util.PropertyMap; public class RmSiteType extends BaseBehaviourBean implements NodeServicePolicies.OnCreateNodePolicy, NodeServicePolicies.OnUpdatePropertiesPolicy, - NodeServicePolicies.BeforeDeleteNodePolicy + NodeServicePolicies.BeforeDeleteNodePolicy, + NodeServicePolicies.OnCreateChildAssociationPolicy { - /** Constant values */ - public static final String COMPONENT_DOCUMENT_LIBRARY = "documentLibrary"; + /** Constant values */ + public static final String COMPONENT_DOCUMENT_LIBRARY = "documentLibrary"; public static final String DEFAULT_SITE_NAME = "rm"; public static final QName DEFAULT_FILE_PLAN_TYPE = TYPE_FILE_PLAN; + private final static List ACCEPTED_NON_UNIQUE_CHILD_TYPES = Arrays.asList(ContentModel.TYPE_FOLDER); /** Site service */ protected SiteService siteService; @@ -87,6 +95,8 @@ public class RmSiteType extends BaseBehaviourBean /** Authority service */ private AuthorityService authorityService; + private FilePlanType filePlanType; + /** Map of file plan type's key'ed by corresponding site types */ protected Map mapFilePlanType = new HashMap(3); @@ -123,6 +133,11 @@ public class RmSiteType extends BaseBehaviourBean this.authorityService = authorityService; } + public void setFilePlanType(FilePlanType filePlanType) + { + this.filePlanType = filePlanType; + } + /** * Registers a file plan type for a specific site type. * @@ -292,7 +307,63 @@ public class RmSiteType extends BaseBehaviourBean return null; } }); + filePlanType.disable(); } } } + + /** + * Add the limitation of creating only one rma:filePlan or one dod:filePlan depending on the type of rm site. + * Also added the limitation of crating two cm:folder type under rm site. + * Other than this nothing can be created under rm site nodeRef + * + * @author Silviu Dinuta + * @since 2.6 + */ + @Override + @Behaviour(kind = BehaviourKind.ASSOCIATION) + public void onCreateChildAssociation(final ChildAssociationRef childAssocRef, boolean isNewNode) + { + AuthenticationUtil.runAsSystem(new RunAsWork() + { + @Override + public Void doWork() + { + final NodeRef child = childAssocRef.getChildRef(); + final NodeRef parent = childAssocRef.getParentRef(); + List acceptedUniqueChildTypes = new ArrayList(); + SiteInfo siteInfo = siteService.getSite(parent); + acceptedUniqueChildTypes.add(getFilePlanType(siteInfo)); + // check the created child is of an accepted type + validateNewChildAssociation(parent, child, acceptedUniqueChildTypes, ACCEPTED_NON_UNIQUE_CHILD_TYPES); + return null; + } + }); + } + + /** + * Overridden this because in this case we need to have multiple cm:folder types but not more than two of them. + * The two mentioned folders are created when rm site is created and one of them is Saved Searches and the other surf-config folder. + * After that creation of cm:folder should not be allowed under rm site node + */ + @Override + protected void validateNewChildAssociation(NodeRef parent, NodeRef child, List acceptedUniqueChildType, + List acceptedMultipleChildType) throws IntegrityException + { + super.validateNewChildAssociation(parent, child, acceptedUniqueChildType, acceptedMultipleChildType); + + // check the user is not trying to create more than 2 folders that are created by default. + if (nodeService.getChildAssocs(parent, Sets.newHashSet(ContentModel.TYPE_FOLDER)).size() > 2) + { + throw new IntegrityException(I18NUtil.getMessage(MULTIPLE_CHILDREN_TYPE_ERROR, ContentModel.TYPE_FOLDER), null); + } + } + + @Behaviour(kind = BehaviourKind.CLASS, + policy = "alf:onDeleteNode", + notificationFrequency = NotificationFrequency.TRANSACTION_COMMIT) + public void onDeleteNodeOnCommit(ChildAssociationRef childAssocRef, boolean isNodeArchived) + { + filePlanType.enable(); + } } diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/TransferContainerType.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/TransferContainerType.java index 753e4b2036..06694e3308 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/TransferContainerType.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/TransferContainerType.java @@ -30,6 +30,7 @@ import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.module.org_alfresco_module_rm.model.BaseBehaviourBean; import org.alfresco.repo.node.NodeServicePolicies; +import org.alfresco.repo.node.integrity.IntegrityException; import org.alfresco.repo.policy.annotation.Behaviour; import org.alfresco.repo.policy.annotation.BehaviourBean; import org.alfresco.repo.policy.annotation.BehaviourKind; @@ -45,31 +46,61 @@ import org.springframework.extensions.surf.util.I18NUtil; */ @BehaviourBean(defaultType = "rma:transferContainer") public class TransferContainerType extends BaseBehaviourBean - implements NodeServicePolicies.OnCreateChildAssociationPolicy, NodeServicePolicies.OnCreateNodePolicy + implements NodeServicePolicies.OnCreateChildAssociationPolicy, + NodeServicePolicies.OnCreateNodePolicy, + NodeServicePolicies.OnDeleteNodePolicy { private final static String MSG_ERROR_ADD_CONTENT_CONTAINER = "rm.service.error-add-content-container"; + private final static String MSG_ERROR_ADD_CHILD_TO_TRANSFER_CONTAINER = "rm.action.create.transfer.container.child-error-message"; + private static final String BEHAVIOUR_NAME = "onCreateChildAssocsForTransferContainer"; + private static final String DELETE_BEHAVIOUR_NAME = "onDeleteTransferContainer"; /** - * On every event + * Disable the behaviours for this transaction + */ + public void disable() + { + getBehaviour(BEHAVIOUR_NAME).disable(); + getBehaviour(DELETE_BEHAVIOUR_NAME).disable(); + } + + /** + * Enable behaviours for this transaction + */ + public void enable() + { + getBehaviour(BEHAVIOUR_NAME).enable(); + getBehaviour(DELETE_BEHAVIOUR_NAME).enable(); + } + + /** + * Prevent creating a node inside transfer container, this will be possible only through internal services in a controlled manner. * - * @see org.alfresco.repo.node.NodeServicePolicies.OnCreateChildAssociationPolicy#onCreateChildAssociation(org.alfresco.service.cmr.repository.ChildAssociationRef, - * boolean) + * @see org.alfresco.repo.node.NodeServicePolicies.OnCreateChildAssociationPolicy#onCreateChildAssociation(org.alfresco.service.cmr.repository.ChildAssociationRef, * boolean) */ @Override - @Behaviour(kind = BehaviourKind.ASSOCIATION) + @Behaviour(kind = BehaviourKind.ASSOCIATION, + name = BEHAVIOUR_NAME) public void onCreateChildAssociation(ChildAssociationRef childAssocRef, boolean bNew) { - // ensure not content to be added in Holdsfolder - NodeRef nodeRef = childAssocRef.getChildRef(); - if (instanceOf(nodeRef, ContentModel.TYPE_CONTENT) == true) { throw new AlfrescoRuntimeException( - I18NUtil.getMessage(MSG_ERROR_ADD_CONTENT_CONTAINER)); } + throw new IntegrityException(I18NUtil.getMessage(MSG_ERROR_ADD_CHILD_TO_TRANSFER_CONTAINER), null); } @Override public void onCreateNode(ChildAssociationRef childAssocRef) { NodeRef nodeRef = childAssocRef.getChildRef(); - if (instanceOf(nodeRef, ContentModel.TYPE_CONTENT) == true) { throw new AlfrescoRuntimeException( - I18NUtil.getMessage(MSG_ERROR_ADD_CONTENT_CONTAINER)); } + if (instanceOf(nodeRef, ContentModel.TYPE_CONTENT) == true) + { + throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_ERROR_ADD_CONTENT_CONTAINER)); + } + } + + @Override + @Behaviour(kind = BehaviourKind.CLASS, + name = DELETE_BEHAVIOUR_NAME) + public void onDeleteNode(ChildAssociationRef childAssocRef, boolean isNodeArchived) + { + throw new IntegrityException("Operation failed. Deletion of Transfer Container is not allowed.", null); } } diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/TransferType.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/TransferType.java new file mode 100644 index 0000000000..ba0989bab0 --- /dev/null +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/TransferType.java @@ -0,0 +1,79 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2017 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.model.rma.type; + +import org.alfresco.module.org_alfresco_module_rm.model.BaseBehaviourBean; +import org.alfresco.repo.node.NodeServicePolicies; +import org.alfresco.repo.node.integrity.IntegrityException; +import org.alfresco.repo.policy.annotation.Behaviour; +import org.alfresco.repo.policy.annotation.BehaviourBean; +import org.alfresco.repo.policy.annotation.BehaviourKind; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.springframework.extensions.surf.util.I18NUtil; + +/** + * rma:transfer behaviour bean + * + * @author Silviu Dinuta + * @since 2.6 + */ +@BehaviourBean(defaultType = "rma:transfer") +public class TransferType extends BaseBehaviourBean + implements NodeServicePolicies.OnCreateChildAssociationPolicy +{ + private final static String MSG_ERROR_ADD_CHILD_TO_TRANSFER = "rm.action.create.transfer.child-error-message"; + + private static final String CREATE_CHILD_BEHAVIOUR_NAME = "onCreateChildAssocsForTransferType"; + + /** + * Disable the behaviours for this transaction + */ + public void disable() + { + getBehaviour(CREATE_CHILD_BEHAVIOUR_NAME).disable(); + } + + /** + * Enable behaviours for this transaction + */ + public void enable() + { + getBehaviour(CREATE_CHILD_BEHAVIOUR_NAME).enable(); + } + + /** + * Prevent creating a node inside transfer folder, this will be possible only through internal services in a controlled manner. + */ + @Override + @Behaviour(kind = BehaviourKind.ASSOCIATION, + name = CREATE_CHILD_BEHAVIOUR_NAME) + public void onCreateChildAssociation(ChildAssociationRef childAssocRef, boolean isNewNode) + { + throw new IntegrityException(I18NUtil.getMessage(MSG_ERROR_ADD_CHILD_TO_TRANSFER), null); + } +} diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/UnfiledRecordContainerType.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/UnfiledRecordContainerType.java new file mode 100644 index 0000000000..4c051465e4 --- /dev/null +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/UnfiledRecordContainerType.java @@ -0,0 +1,97 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2017 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.model.rma.type; + +import java.util.Arrays; +import java.util.List; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.model.BaseBehaviourBean; +import org.alfresco.repo.node.NodeServicePolicies; +import org.alfresco.repo.node.integrity.IntegrityException; +import org.alfresco.repo.policy.annotation.Behaviour; +import org.alfresco.repo.policy.annotation.BehaviourBean; +import org.alfresco.repo.policy.annotation.BehaviourKind; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.namespace.QName; + +/** + * rma:unfiledRecordContainer behaviour bean + * + * @author Silviu Dinuta + * @since 2.6 + */ +@BehaviourBean(defaultType = "rma:unfiledRecordContainer") +public class UnfiledRecordContainerType extends BaseBehaviourBean + implements NodeServicePolicies.OnCreateChildAssociationPolicy, + NodeServicePolicies.OnDeleteNodePolicy +{ + private static final String BEHAVIOUR_NAME = "onDeleteUnfiledRecordContainer"; + private final static List ACCEPTED_NON_UNIQUE_CHILD_TYPES = Arrays.asList(TYPE_UNFILED_RECORD_FOLDER, ContentModel.TYPE_CONTENT, TYPE_NON_ELECTRONIC_DOCUMENT); + + /** + * Disable the behaviours for this transaction + */ + public void disable() + { + getBehaviour(BEHAVIOUR_NAME).disable(); + } + + /** + * Enable behaviours for this transaction + */ + public void enable() + { + getBehaviour(BEHAVIOUR_NAME).enable(); + } + + @Override + @Behaviour(kind = BehaviourKind.ASSOCIATION) + public void onCreateChildAssociation(ChildAssociationRef childAssocRef, boolean isNewNode) + { + // We need to automatically cast the created folder to record folder if it is a plain folder + // This occurs if the RM folder has been created via IMap, WebDav, etc. Don't check subtypes. + // Some modules use hidden folder subtypes to store information (see RM-3283). + QName childType = nodeService.getType(childAssocRef.getChildRef()); + if (childType.equals(ContentModel.TYPE_FOLDER)) + { + nodeService.setType(childAssocRef.getChildRef(), TYPE_UNFILED_RECORD_FOLDER); + } + + // check the created child is of an accepted type + validateNewChildAssociationSubTypesIncluded(childAssocRef.getChildRef(), ACCEPTED_NON_UNIQUE_CHILD_TYPES); + } + + @Override + @Behaviour(kind = BehaviourKind.CLASS, + name = BEHAVIOUR_NAME) + public void onDeleteNode(ChildAssociationRef childAssocRef, boolean isNodeArchived) + { + throw new IntegrityException("Operation failed. Deletion of Unfiled Record Container is not allowed.", null); + } +} \ No newline at end of file diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/UnfiledRecordFolderType.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/UnfiledRecordFolderType.java new file mode 100644 index 0000000000..51f4f62981 --- /dev/null +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/UnfiledRecordFolderType.java @@ -0,0 +1,70 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2017 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.model.rma.type; + +import java.util.Arrays; +import java.util.List; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.model.BaseBehaviourBean; +import org.alfresco.repo.node.NodeServicePolicies; +import org.alfresco.repo.policy.annotation.Behaviour; +import org.alfresco.repo.policy.annotation.BehaviourBean; +import org.alfresco.repo.policy.annotation.BehaviourKind; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.namespace.QName; + +/** + * rma:unfiledRecordFolder behaviour bean + * + * @author Silviu Dinuta + * @since 2.6 + */ +@BehaviourBean(defaultType = "rma:unfiledRecordFolder") +public class UnfiledRecordFolderType extends BaseBehaviourBean + implements NodeServicePolicies.OnCreateChildAssociationPolicy +{ + private final static List ACCEPTED_NON_UNIQUE_CHILD_TYPES = Arrays.asList(TYPE_UNFILED_RECORD_FOLDER, ContentModel.TYPE_CONTENT, TYPE_NON_ELECTRONIC_DOCUMENT); + + @Override + @Behaviour(kind = BehaviourKind.ASSOCIATION) + public void onCreateChildAssociation(ChildAssociationRef childAssocRef, boolean isNewNode) + { + // We need to automatically cast the created folder to record folder if it is a plain folder + // This occurs if the RM folder has been created via IMap, WebDav, etc. Don't check subtypes. + // Some modules use hidden folder subtypes to store information (see RM-3283). + QName childType = nodeService.getType(childAssocRef.getChildRef()); + if (childType.equals(ContentModel.TYPE_FOLDER)) + { + nodeService.setType(childAssocRef.getChildRef(), TYPE_UNFILED_RECORD_FOLDER); + } + + // check the created child is of an accepted type + validateNewChildAssociationSubTypesIncluded(childAssocRef.getChildRef(), ACCEPTED_NON_UNIQUE_CHILD_TYPES); + } +} diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/transfer/TransferServiceImpl.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/transfer/TransferServiceImpl.java index ad25cb8239..652df3b6c5 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/transfer/TransferServiceImpl.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/transfer/TransferServiceImpl.java @@ -32,7 +32,6 @@ import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; - import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionAction; @@ -41,6 +40,8 @@ import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService; import org.alfresco.module.org_alfresco_module_rm.freeze.FreezeService; import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.module.org_alfresco_module_rm.model.rma.type.TransferContainerType; +import org.alfresco.module.org_alfresco_module_rm.model.rma.type.TransferType; 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; @@ -87,6 +88,10 @@ public class TransferServiceImpl extends ServiceBaseImpl /** Freeze Service */ protected FreezeService freezeService; + protected TransferContainerType transferContainerType; + + protected TransferType transferType; + /** * @param filePlanService file plan service */ @@ -127,6 +132,16 @@ public class TransferServiceImpl extends ServiceBaseImpl this.freezeService = freezeService; } + public void setTransferContainerType(TransferContainerType transferContainerType) + { + this.transferContainerType = transferContainerType; + } + + public void setTransferType(TransferType transferType) + { + this.transferType = transferType; + } + /** * @see org.alfresco.module.org_alfresco_module_rm.transfer.TransferService#transfer(NodeRef, boolean) */ @@ -164,11 +179,19 @@ public class TransferServiceImpl extends ServiceBaseImpl } NodeRef transferContainer = filePlanService.getTransferContainer(root); - transferNodeRef = nodeService.createNode(transferContainer, - ContentModel.ASSOC_CONTAINS, - QName.createQName(RM_URI, transferName), - TYPE_TRANSFER, - transferProps).getChildRef(); + + transferContainerType.disable(); + try + { + transferNodeRef = nodeService.createNode(transferContainer, ContentModel.ASSOC_CONTAINS, + QName.createQName(RM_URI, transferName), TYPE_TRANSFER, transferProps) + .getChildRef(); + + } + finally + { + transferContainerType.enable(); + } // Bind the hold node reference to the transaction AlfrescoTransactionSupport.bindResource(KEY_TRANSFER_NODEREF, transferNodeRef); @@ -188,13 +211,17 @@ public class TransferServiceImpl extends ServiceBaseImpl } // Link the record to the trasnfer object - nodeService.addChild(transferNodeRef, - nodeRef, - ASSOC_TRANSFERRED, - ASSOC_TRANSFERRED); - - // Set PDF indicator flag - setPDFIndicationFlag(transferNodeRef, nodeRef); + transferType.disable(); + try + { + nodeService.addChild(transferNodeRef, nodeRef, ASSOC_TRANSFERRED, ASSOC_TRANSFERRED); + // Set PDF indicator flag + setPDFIndicationFlag(transferNodeRef, nodeRef); + } + finally + { + transferType.enable(); + } // Set the transferring indicator aspect nodeService.addAspect(nodeRef, ASPECT_TRANSFERRING, null); @@ -253,12 +280,12 @@ public class TransferServiceImpl extends ServiceBaseImpl { throw new AlfrescoRuntimeException("Could not complete a transfer that contains held folders"); } - + if(freezeService.hasFrozenChildren(assoc.getChildRef())) { throw new AlfrescoRuntimeException("Cound not complete a transfer that contains folders with held children"); } - + markComplete(assoc.getChildRef(), accessionIndicator, transferLocation); } diff --git a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/issue/RM3341Test.java b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/issue/RM3341Test.java index b954591d18..30e3c76578 100644 --- a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/issue/RM3341Test.java +++ b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/issue/RM3341Test.java @@ -25,16 +25,15 @@ * #L% * */ + package org.alfresco.module.org_alfresco_module_rm.test.integration.issue; -import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel; import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase; -import org.alfresco.service.cmr.model.FileInfo; +import org.alfresco.repo.node.integrity.IntegrityException; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.security.AccessStatus; -import org.springframework.extensions.surf.util.I18NUtil; /** * Unit test for RM-3341 .. can copy to hold and transfer folder @@ -43,9 +42,6 @@ import org.springframework.extensions.surf.util.I18NUtil; */ public class RM3341Test extends BaseRMTestCase { - - private final static String MSG_ERROR_ADD_CONTENT_CONTAINER = "rm.service.error-add-content-container"; - public void testCopyingContentsInHoldandTransfer() throws Exception { doTestInTransaction(new Test() @@ -59,9 +55,9 @@ public class RM3341Test extends BaseRMTestCase assertNotNull(transferContainer); assertEquals(AccessStatus.ALLOWED, - permissionService.hasPermission(holdContainer, RMPermissionModel.FILING)); + permissionService.hasPermission(holdContainer, RMPermissionModel.FILING)); assertEquals(AccessStatus.ALLOWED, - permissionService.hasPermission(transferContainer, RMPermissionModel.FILING)); + permissionService.hasPermission(transferContainer, RMPermissionModel.FILING)); return null; } @@ -79,13 +75,12 @@ public class RM3341Test extends BaseRMTestCase try { - FileInfo copyInfo = fileFolderService.create(holdContainer, "test file", ContentModel.TYPE_CONTENT); + fileFolderService.create(holdContainer, "test file", ContentModel.TYPE_CONTENT); fail("This should have thrown an exception"); } - catch (AlfrescoRuntimeException e) + catch (IntegrityException e) { - // ("Content can't be added to a record container. Use record folders to file content.") - assertEquals(I18NUtil.getMessage(MSG_ERROR_ADD_CONTENT_CONTAINER), e.getMsgId()); + // ("Content can't be added to a hold container. Use record folders to file content.") } return null; } @@ -109,10 +104,9 @@ public class RM3341Test extends BaseRMTestCase fail("This should have thrown an exception"); } - catch (AlfrescoRuntimeException e) + catch (IntegrityException e) { - // ("Content can't be added to a record container. Use record folders to file content.") - assertEquals(I18NUtil.getMessage(MSG_ERROR_ADD_CONTENT_CONTAINER), e.getMsgId()); + // ("Content can't be added to a transfer container. Use record folders to file content.") } return null; } diff --git a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/issue/RM4293Test.java b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/issue/RM4293Test.java new file mode 100644 index 0000000000..d39fbadcd8 --- /dev/null +++ b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/issue/RM4293Test.java @@ -0,0 +1,125 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2017 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.issue; + +import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase; +import org.alfresco.repo.node.integrity.IntegrityException; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * Test for RM-4293 + * + * @author Silviu Dinuta + * @since 2.6 + */ +public class RM4293Test extends BaseRMTestCase +{ + public void testDeleteSpecialContainers() throws Exception + { + doTestInTransaction(new Test() + { + @Override + public Void run() + { + NodeRef holdContainer = filePlanService.getHoldContainer(filePlan); + assertNotNull(holdContainer); + + try + { + fileFolderService.delete(holdContainer); + fail("This should have thrown an exception"); + } + catch (IntegrityException e) + { + // ("Hold Container can't be deleted.") + } + return null; + } + }); + + doTestInTransaction(new Test() + { + @Override + public Void run() + { + NodeRef transferContainer = filePlanService.getTransferContainer(filePlan); + assertNotNull(transferContainer); + try + { + fileFolderService.delete(transferContainer); + fail("This should have thrown an exception"); + } + catch (IntegrityException e) + { + // ("Transfer Container can't be deleted.") + } + return null; + } + }); + + doTestInTransaction(new Test() + { + @Override + public Void run() + { + NodeRef unfiledRecordContainer = filePlanService.getUnfiledContainer(filePlan); + assertNotNull(unfiledRecordContainer); + + try + { + fileFolderService.delete(unfiledRecordContainer); + fail("This should have thrown an exception"); + } + catch (IntegrityException e) + { + // ("Unfiled Record Container can't be deleted.") + } + return null; + } + }); + + doTestInTransaction(new Test() + { + @Override + public Void run() + { + try + { + fileFolderService.delete(filePlan); + fail("This should have thrown an exception"); + } + catch (IntegrityException e) + { + // ("FilePlan can't be deleted.") + } + return null; + } + }); + } +} \ No newline at end of file diff --git a/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/TransferContainerTypeUnitTest.java b/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/TransferContainerTypeUnitTest.java index 2414556d3d..e797308c3e 100644 --- a/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/TransferContainerTypeUnitTest.java +++ b/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/TransferContainerTypeUnitTest.java @@ -26,11 +26,16 @@ */ package org.alfresco.module.org_alfresco_module_rm.model.rma.type; -import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.model.ContentModel; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + + +import org.alfresco.module.org_alfresco_module_rm.test.util.AlfMock; import org.alfresco.module.org_alfresco_module_rm.test.util.BaseUnitTest; +import org.alfresco.repo.node.integrity.IntegrityException; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; import org.junit.Test; import org.mockito.InjectMocks; @@ -42,40 +47,27 @@ import org.mockito.InjectMocks; */ public class TransferContainerTypeUnitTest extends BaseUnitTest { - /** test object */ - private @InjectMocks TransferContainerType transferContainerType; + /** + * test object + */ + private @InjectMocks + TransferContainerType transferContainerType; /** - * Having the Unfilled Record container and a folder having the aspect ASPECT_HIDDEN When adding a child association - * between the folder and the container Then the folder type shouldn't be renamed + * Given that we try to add to transfer container, + * Then IntegrityException is thrown. */ - @Test(expected = AlfrescoRuntimeException.class) - public void testAddContentToTransferContainerTest() + @Test(expected = IntegrityException.class) + public void testAddToTransferContainerTest() { + NodeRef transferContainer = generateNodeRef(TYPE_TRANSFER_CONTAINER, true); - NodeRef transferContainer = createTransferContainer(); - - /* - * When adding a child association between the folder and the container - */ - NodeRef record = generateNodeRef(ContentModel.TYPE_CONTENT); - ChildAssociationRef childAssoc = new ChildAssociationRef(ContentModel.TYPE_CONTENT, transferContainer, - ContentModel.TYPE_CONTENT, record); - - transferContainerType.onCreateChildAssociation(childAssoc, true); + QName type = AlfMock.generateQName(); + NodeRef nodeRef = AlfMock.generateNodeRef(mockedNodeService, type); + ChildAssociationRef mockedChildAssoc = mock(ChildAssociationRef.class); + when(mockedChildAssoc.getChildRef()).thenReturn(nodeRef); + when(mockedChildAssoc.getParentRef()).thenReturn(transferContainer); + transferContainerType.onCreateChildAssociation(mockedChildAssoc, true); } - - /** - * Generates a record management container - * - * @return reference to the generated container - */ - private NodeRef createTransferContainer() - { - NodeRef holdContainer = generateNodeRef(TYPE_TRANSFER, true); - - return holdContainer; - } - } From 06a48d73cd8ecc515feadcd07cf18f6aafd56034 Mon Sep 17 00:00:00 2001 From: Tuna Aksoy Date: Mon, 12 Jun 2017 16:41:30 +0100 Subject: [PATCH 15/16] Merged release/V2.5 and addressed review comments --- .../org_alfresco_module_rm/rm-model-context.xml | 2 +- .../model/rma/type/FilePlanType.java | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-model-context.xml b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-model-context.xml index 8fe840ba94..dde414270d 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-model-context.xml +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-model-context.xml @@ -76,7 +76,7 @@ - + diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/FilePlanType.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/FilePlanType.java index 485c7b5543..b91a005bee 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/FilePlanType.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/FilePlanType.java @@ -84,7 +84,7 @@ public class FilePlanType extends BaseBehaviourBean /** * Unfiled Record Container Type behaviour bean */ - private UnfiledRecordContainerType unfilerRecordContainerType; + private UnfiledRecordContainerType unfiledRecordContainerType; /** * Transfer Container Type behaviour bean @@ -161,11 +161,11 @@ public class FilePlanType extends BaseBehaviourBean } /** - * @param unfilerRecordContainerType - unfiled record container type behaviour bean + * @param unfiledRecordContainerType - unfiled record container type behaviour bean */ - public void setUnfilerRecordContainerType(UnfiledRecordContainerType unfilerRecordContainerType) + public void setUnfiledRecordContainerType(UnfiledRecordContainerType unfiledRecordContainerType) { - this.unfilerRecordContainerType = unfilerRecordContainerType; + this.unfiledRecordContainerType = unfiledRecordContainerType; } /** @@ -267,7 +267,7 @@ public class FilePlanType extends BaseBehaviourBean name = BEHAVIOUR_NAME) public void onDeleteNode(ChildAssociationRef childAssocRef, boolean archived) { - unfilerRecordContainerType.enable(); + unfiledRecordContainerType.enable(); transferContainerType.enable(); holdContainerType.enable(); throw new IntegrityException("Operation failed. Deletion of File Plan is not allowed.", null); @@ -281,7 +281,7 @@ public class FilePlanType extends BaseBehaviourBean notificationFrequency = NotificationFrequency.FIRST_EVENT) public void beforeDeleteNode(NodeRef nodeRef) { - unfilerRecordContainerType.disable(); + unfiledRecordContainerType.disable(); transferContainerType.disable(); holdContainerType.disable(); } @@ -293,7 +293,7 @@ public class FilePlanType extends BaseBehaviourBean { // tear down the file plan roles getFilePlanRoleService().tearDownFilePlanRoles(childAssocRef.getChildRef()); - unfilerRecordContainerType.enable(); + unfiledRecordContainerType.enable(); transferContainerType.enable(); holdContainerType.enable(); } From 09e3766230f5a7aa2f9d5c991c2c74ea67ad5238 Mon Sep 17 00:00:00 2001 From: Sara Aspery Date: Mon, 12 Jun 2017 16:42:05 +0100 Subject: [PATCH 16/16] RM-5175_Complete_record_with_missing_metadata changes after review --- .../action/impl/DeclareRecordAction.java | 8 ++------ .../test/util/CommonRMTestUtils.java | 18 ++---------------- 2 files changed, 4 insertions(+), 22 deletions(-) diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/DeclareRecordAction.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/DeclareRecordAction.java index 2ccfee90cf..d701500601 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/DeclareRecordAction.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/DeclareRecordAction.java @@ -197,16 +197,12 @@ public class DeclareRecordAction extends RMActionExecuterAbstractBase // check for missing mandatory metadata from custom aspect definitions if (result) { - QName aspect; + QName aspect = ASPECT_RECORD; if (nodeRefType.equals(TYPE_NON_ELECTRONIC_DOCUMENT)) - //if (nodeRefType.getLocalName())) { aspect = TYPE_NON_ELECTRONIC_DOCUMENT; } - else - { - aspect = ASPECT_RECORD; - } + // get customAspectImpl String localName = aspect.toPrefixString(getNamespaceService()).replace(":", ""); localName = MessageFormat.format("{0}CustomProperties", localName); diff --git a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/CommonRMTestUtils.java b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/CommonRMTestUtils.java index f0b8e2829f..65a731455d 100644 --- a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/CommonRMTestUtils.java +++ b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/CommonRMTestUtils.java @@ -310,28 +310,14 @@ public class CommonRMTestUtils implements RecordsManagementModel { Map props = new HashMap(1); props.put(ContentModel.PROP_TITLE, title); - return createNonElectronicRecordImpl(recordFolder, name, props); - } + props.put(ContentModel.PROP_NAME, name); - /** - * Helper to consolidate creation of non-electronic record - */ - private NodeRef createNonElectronicRecordImpl(NodeRef recordFolder, String name, Map properties) - { // Create the document - if (properties == null) - { - properties = new HashMap(1); - } - if (!properties.containsKey(ContentModel.PROP_NAME)) - { - properties.put(ContentModel.PROP_NAME, name); - } NodeRef record = nodeService.createNode(recordFolder, ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, name), RecordsManagementModel.TYPE_NON_ELECTRONIC_DOCUMENT, - properties).getChildRef(); + props).getChildRef(); return record; }