diff --git a/rm-automation/pom.xml b/rm-automation/pom.xml
index 9ea8e7ea8f..d6cbef4544 100644
--- a/rm-automation/pom.xml
+++ b/rm-automation/pom.xml
@@ -173,6 +173,17 @@
mockito-alltest
+
+ org.slf4j
+ slf4j-log4j12
+ test
+
+
+ org.slf4j
+ jul-to-slf4j
+ 1.7.21
+ test
+
diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/log4j.properties b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/log4j.properties
index 7a14ca27f5..83e724394e 100644
--- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/log4j.properties
+++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/log4j.properties
@@ -55,4 +55,5 @@ log4j.logger.org.alfresco.module.org_alfresco_module_rm.patch=info
#
# Job debug
#
-#log4j.logger.org.alfresco.module.org_alfresco_module_rm.job=debug
\ No newline at end of file
+#log4j.logger.org.alfresco.module.org_alfresco_module_rm.job=debug
+log4j.logger.org.alfresco.repo.web.scripts.roles.DynamicAuthoritiesGet=info
\ No newline at end of file
diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-webscript-context.xml b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-webscript-context.xml
index 58628e9921..728ac66fc5 100644
--- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-webscript-context.xml
+++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-webscript-context.xml
@@ -587,6 +587,19 @@
+
+
+
+
+
+
+
+
+
+
+
+ Removes dynamic authorities
+
+ URL parameter batchsize is mandatory, and represents the number of records that are processed in one transaction.
+ URL parameter maxProcessedRecords is optional, and represents the maximum number of records that will be processed in one request.
+ ]]>
+
+ /api/rm/rm-dynamicauthorities?batchsize={batchsize}&maxProcessedRecords={maxProcessedRecords?}
+ argument
+ admin
+ required
+
\ No newline at end of file
diff --git a/rm-community/rm-community-repo/config/alfresco/templates/webscripts/org/alfresco/repository/roles/rm-dynamicauthorities.get.json.ftl b/rm-community/rm-community-repo/config/alfresco/templates/webscripts/org/alfresco/repository/roles/rm-dynamicauthorities.get.json.ftl
new file mode 100644
index 0000000000..8929b9a33c
--- /dev/null
+++ b/rm-community/rm-community-repo/config/alfresco/templates/webscripts/org/alfresco/repository/roles/rm-dynamicauthorities.get.json.ftl
@@ -0,0 +1,30 @@
+<#--
+ #%L
+ Alfresco Records Management Module
+ %%
+ Copyright (C) 2005 - 2016 Alfresco Software Limited
+ %%
+ This file is part of the Alfresco software.
+ -
+ If the software was purchased under a paid Alfresco license, the terms of
+ the paid license agreement will prevail. Otherwise, the software is
+ provided under the following open source license terms:
+ -
+ Alfresco is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+ -
+ Alfresco is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+ -
+ You should have received a copy of the GNU Lesser General Public License
+ along with Alfresco. If not, see .
+ #L%
+-->
+{
+ "responsestatus" : "${responsestatus?json_string}",
+ "message" : "${message?json_string}"
+}
\ No newline at end of file
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/CopyMoveLinkFileToBaseAction.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/CopyMoveLinkFileToBaseAction.java
index 9a1bbe3d8f..1c0d10ea83 100644
--- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/CopyMoveLinkFileToBaseAction.java
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/CopyMoveLinkFileToBaseAction.java
@@ -143,7 +143,7 @@ public abstract class CopyMoveLinkFileToBaseAction extends RMActionExecuterAbstr
* @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef)
*/
@Override
- protected synchronized void executeImpl(Action action, final NodeRef actionedUponNodeRef)
+ protected synchronized void executeImpl(final Action action, final NodeRef actionedUponNodeRef)
{
String actionName = action.getActionDefinitionName();
if (isOkToProceedWithAction(actionedUponNodeRef, actionName))
@@ -165,8 +165,25 @@ public abstract class CopyMoveLinkFileToBaseAction extends RMActionExecuterAbstr
NodeRef recordFolder = (NodeRef)action.getParameterValue(PARAM_DESTINATION_RECORD_FOLDER);
if (recordFolder == null)
{
- final boolean finaltargetIsUnfiledRecords = targetIsUnfiledRecords;
- recordFolder = createOrResolvePath(action, actionedUponNodeRef, finaltargetIsUnfiledRecords);
+ final boolean finaltargetIsUnfiledRecords = targetIsUnfiledRecords;
+ recordFolder = retryingTransactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback()
+ {
+ public NodeRef execute() throws Throwable
+ {
+ NodeRef result = null;
+ try
+ {
+ // get the reference to the record folder based on the relative path
+ result = createOrResolvePath(action, actionedUponNodeRef, finaltargetIsUnfiledRecords);
+ }
+ catch (DuplicateChildNodeNameException ex)
+ {
+ throw new ConcurrencyFailureException("Cannot create or resolve path.", ex);
+ }
+
+ return result;
+ }
+ }, false, true);
}
// now we have the reference to the target folder we can do some final checks to see if the action is valid
@@ -180,29 +197,30 @@ public abstract class CopyMoveLinkFileToBaseAction extends RMActionExecuterAbstr
{
try
{
- if(getMode() == CopyMoveLinkFileToActionMode.MOVE)
+ synchronized (this)
{
- fileFolderService.move(actionedUponNodeRef, finalRecordFolder, null);
- }
- else if(getMode() == CopyMoveLinkFileToActionMode.COPY)
- {
- fileFolderService.copy(actionedUponNodeRef, finalRecordFolder, null);
- }
- else if(getMode() == CopyMoveLinkFileToActionMode.LINK)
- {
- getRecordService().link(actionedUponNodeRef, finalRecordFolder);
+ if (getMode() == CopyMoveLinkFileToActionMode.MOVE)
+ {
+ fileFolderService.move(actionedUponNodeRef, finalRecordFolder, null);
+ }
+ else if (getMode() == CopyMoveLinkFileToActionMode.COPY)
+ {
+ fileFolderService.copy(actionedUponNodeRef, finalRecordFolder, null);
+ }
+ else if (getMode() == CopyMoveLinkFileToActionMode.LINK)
+ {
+ getRecordService().link(actionedUponNodeRef, finalRecordFolder);
+ }
}
}
catch (FileNotFoundException fileNotFound)
{
- throw new AlfrescoRuntimeException(
- "Unable to execute file to action, because the " + (mode == CopyMoveLinkFileToActionMode.MOVE ? "move" : "copy") + " operation failed.",
- fileNotFound
- );
+ throw new AlfrescoRuntimeException("Unable to execute file to action, because the " + (mode == CopyMoveLinkFileToActionMode.MOVE ? "move" : "copy") + " operation failed.", fileNotFound);
}
-
+
return null;
}
+
});
}
}
@@ -389,7 +407,7 @@ public abstract class CopyMoveLinkFileToBaseAction extends RMActionExecuterAbstr
*/
private NodeRef getChild(NodeRef parent, String childName)
{
- return getNodeService().getChildByName(parent, ContentModel.ASSOC_CONTAINS, childName);
+ return getNodeService().getChildByName(parent, ContentModel.ASSOC_CONTAINS, childName);
}
/**
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/repo/web/scripts/roles/DynamicAuthoritiesGet.java b/rm-community/rm-community-repo/source/java/org/alfresco/repo/web/scripts/roles/DynamicAuthoritiesGet.java
new file mode 100644
index 0000000000..5e3020e9aa
--- /dev/null
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/repo/web/scripts/roles/DynamicAuthoritiesGet.java
@@ -0,0 +1,267 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2016 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+/*
+ * Copyright (C) 2005-2014 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+package org.alfresco.repo.web.scripts.roles;
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.alfresco.model.ContentModel;
+import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
+import org.alfresco.module.org_alfresco_module_rm.security.ExtendedReaderDynamicAuthority;
+import org.alfresco.module.org_alfresco_module_rm.security.ExtendedSecurityService;
+import org.alfresco.module.org_alfresco_module_rm.security.ExtendedWriterDynamicAuthority;
+import org.alfresco.repo.domain.node.NodeDAO;
+import org.alfresco.repo.domain.patch.PatchDAO;
+import org.alfresco.repo.domain.qname.QNameDAO;
+import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.repository.NodeService;
+import org.alfresco.service.cmr.security.PermissionService;
+import org.alfresco.service.namespace.QName;
+import org.alfresco.service.transaction.TransactionService;
+import org.alfresco.util.Pair;
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.extensions.webscripts.Cache;
+import org.springframework.extensions.webscripts.DeclarativeWebScript;
+import org.springframework.extensions.webscripts.Status;
+import org.springframework.extensions.webscripts.WebScriptRequest;
+
+/**
+ * Webscript used for removing dynamic authorities from the records.
+ *
+ * @author Silviu Dinuta
+ * @since 2.3.0.7
+ */
+@SuppressWarnings("deprecation")
+public class DynamicAuthoritiesGet extends DeclarativeWebScript implements RecordsManagementModel
+{
+ private static final String MESSAGE_PARAMETER_BATCHSIZE_GREATER_THAN_ZERO = "Parameter batchsize should be a number greater than 0.";
+ private static final String MESSAGE_PROCESSING_BEGIN = "Processing - BEGIN";
+ private static final String MESSAGE_PROCESSING_END = "Processing - END";
+ private static final String MESSAGE_PROCESSING_RECORD_END_TEMPLATE = "Processing record {0} - END";
+ private static final String MESSAGE_PROCESSING_RECORD_BEGIN_TEMPLATE = "Processing record {0} - BEGIN";
+ private static final String MESSAGE_BATCHSIZE_IS_INVALID = "Parameter batchsize is invalid.";
+ private static final String MESSAGE_BATCHSIZE_IS_MANDATORY = "Parameter batchsize is mandatory";
+ private static final String SUCCESS_STATUS = "success";
+ private static final String FAILED_STATUS = "failed";
+ /**
+ * The logger
+ */
+ private static Log logger = LogFactory.getLog(DynamicAuthoritiesGet.class);
+ private static final String BATCH_SIZE = "batchsize";
+ private static final String TOTAL_NUMBER_TO_PROCESS = "maxProcessedRecords";
+ private static final String MODEL_STATUS = "responsestatus";
+ private static final String MODEL_MESSAGE = "message";
+ private static final String MESSAGE_ALL_TEMPLATE = "Processed {0} records.";
+ private static final String MESSAGE_PARTIAL_TEMPLATE = "Processed first {0} records.";
+ private static final String MESSAGE_NO_RECORDS_TO_PROCESS = "There where no records to be processed.";
+
+
+ /** services */
+ private PatchDAO patchDAO;
+ private NodeDAO nodeDAO;
+ private QNameDAO qnameDAO;
+ private NodeService nodeService;
+ private PermissionService permissionService;
+ private ExtendedSecurityService extendedSecurityService;
+ private TransactionService transactionService;
+
+ /** service setters */
+ public void setPatchDAO(PatchDAO patchDAO) { this.patchDAO = patchDAO; }
+ public void setNodeDAO(NodeDAO nodeDAO) { this.nodeDAO = nodeDAO; }
+ public void setQnameDAO(QNameDAO qnameDAO) { this.qnameDAO = qnameDAO; }
+ public void setNodeService(NodeService nodeService) { this.nodeService = nodeService; }
+ public void setPermissionService(PermissionService permissionService) { this.permissionService = permissionService; }
+ public void setExtendedSecurityService(ExtendedSecurityService extendedSecurityService) { this.extendedSecurityService = extendedSecurityService; }
+ public void setTransactionService(TransactionService transactionService) { this.transactionService = transactionService; }
+
+ @Override
+ protected Map executeImpl(WebScriptRequest req, Status status, Cache cache)
+ {
+ Map model = new HashMap();
+ String batchSizeStr = req.getParameter(BATCH_SIZE);
+ String totalToBeProcessedRecordsStr = req.getParameter(TOTAL_NUMBER_TO_PROCESS);
+
+ Long size = 0L;
+ if (StringUtils.isBlank(batchSizeStr))
+ {
+ model.put(MODEL_STATUS, FAILED_STATUS);
+ model.put(MODEL_MESSAGE, MESSAGE_BATCHSIZE_IS_MANDATORY);
+ logger.info(MESSAGE_BATCHSIZE_IS_MANDATORY);
+ return model;
+ }
+ try
+ {
+ size = Long.parseLong(batchSizeStr);
+ if(size <= 0)
+ {
+ model.put(MODEL_STATUS, FAILED_STATUS);
+ model.put(MODEL_MESSAGE, MESSAGE_PARAMETER_BATCHSIZE_GREATER_THAN_ZERO);
+ logger.info(MESSAGE_PARAMETER_BATCHSIZE_GREATER_THAN_ZERO);
+ return model;
+ }
+ }
+ catch(NumberFormatException ex)
+ {
+ model.put(MODEL_STATUS, FAILED_STATUS);
+ model.put(MODEL_MESSAGE, MESSAGE_BATCHSIZE_IS_INVALID);
+ logger.info(MESSAGE_BATCHSIZE_IS_INVALID);
+ return model;
+ }
+ final Long batchSize = size;
+ // get the max node id and the extended security aspect
+ Long maxNodeId = patchDAO.getMaxAdmNodeID();
+ final Pair recordAspectPair = qnameDAO.getQName(ASPECT_EXTENDED_SECURITY);
+ if(recordAspectPair == null)
+ {
+ model.put(MODEL_STATUS, SUCCESS_STATUS);
+ model.put(MODEL_MESSAGE, MESSAGE_NO_RECORDS_TO_PROCESS);
+ logger.info(MESSAGE_NO_RECORDS_TO_PROCESS);
+ return model;
+ }
+
+ //default total number of records to be processed to batch size value
+ Long totalNumberOfRecordsToProcess = batchSize;
+ if (StringUtils.isNotBlank(totalToBeProcessedRecordsStr))
+ {
+ try
+ {
+ totalNumberOfRecordsToProcess = Long.parseLong(totalToBeProcessedRecordsStr);
+ }
+ catch(NumberFormatException ex)
+ {
+ //do nothing here, the value will remain 0L in this case
+ }
+ }
+
+ final Long maxRecordsToProcess = totalNumberOfRecordsToProcess;
+ final List processedNodes = new ArrayList();
+ logger.info(MESSAGE_PROCESSING_BEGIN);
+ // by batch size
+ for (Long i = 0L; i < maxNodeId; i+=batchSize)
+ {
+ if(maxRecordsToProcess != 0 && processedNodes.size() >= maxRecordsToProcess)
+ {
+ break;
+ }
+ final Long currentIndex = i;
+
+ transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback()
+ {
+ public Void execute() throws Throwable
+ {
+ // get the nodes with the extended security aspect applied
+ List nodeIds = patchDAO.getNodesByAspectQNameId(recordAspectPair.getFirst(), currentIndex, currentIndex + batchSize);
+
+ // process each one
+ for (Long nodeId : nodeIds)
+ {
+ if(maxRecordsToProcess != 0 && processedNodes.size() >= maxRecordsToProcess)
+ {
+ break;
+ }
+ NodeRef record = nodeDAO.getNodePair(nodeId).getSecond();
+ String recordName = (String) nodeService.getProperty(record, ContentModel.PROP_NAME);
+ logger.info(MessageFormat.format(MESSAGE_PROCESSING_RECORD_BEGIN_TEMPLATE, recordName));
+ processNode(record);
+ logger.info(MessageFormat.format(MESSAGE_PROCESSING_RECORD_END_TEMPLATE, recordName));
+ processedNodes.add(record);
+ }
+
+ return null;
+ }
+ },
+ false, // read only
+ true); // requires new
+ }
+ logger.info(MESSAGE_PROCESSING_END);
+ int processedNodesSize = processedNodes.size();
+ String message = "";
+ if(totalNumberOfRecordsToProcess == 0 || (totalNumberOfRecordsToProcess > 0 && processedNodesSize < totalNumberOfRecordsToProcess))
+ {
+ message = MessageFormat.format(MESSAGE_ALL_TEMPLATE, processedNodesSize);
+ }
+ if (totalNumberOfRecordsToProcess > 0 && totalNumberOfRecordsToProcess == processedNodesSize)
+ {
+ message = MessageFormat.format(MESSAGE_PARTIAL_TEMPLATE, totalNumberOfRecordsToProcess);
+ }
+ model.put(MODEL_STATUS, SUCCESS_STATUS);
+ model.put(MODEL_MESSAGE, message);
+ logger.info(message);
+ return model;
+ }
+
+ /**
+ * Process each node
+ *
+ * @param nodeRef
+ */
+ @SuppressWarnings({ "unchecked"})
+ private void processNode(NodeRef nodeRef)
+ {
+ // get the reader/writer data
+ Map readers = (Map)nodeService.getProperty(nodeRef, PROP_READERS);
+ Map writers = (Map)nodeService.getProperty(nodeRef, PROP_WRITERS);
+
+ // remove extended security aspect
+ nodeService.removeAspect(nodeRef, ASPECT_EXTENDED_SECURITY);
+
+ // remove dynamic authority permissions
+ permissionService.clearPermission(nodeRef, ExtendedReaderDynamicAuthority.EXTENDED_READER);
+ permissionService.clearPermission(nodeRef, ExtendedWriterDynamicAuthority.EXTENDED_WRITER);
+
+ // if record then ...
+ if (nodeService.hasAspect(nodeRef, ASPECT_RECORD))
+ {
+ // re-set extended security via API
+ extendedSecurityService.set(nodeRef, readers.keySet(), writers.keySet());
+ }
+ }
+}
diff --git a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/issue/RM3993Test.java b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/issue/RM3993Test.java
new file mode 100644
index 0000000000..20c6dc3e4d
--- /dev/null
+++ b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/issue/RM3993Test.java
@@ -0,0 +1,253 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2016 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+/*
+ * Copyright (C) 2005-2014 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+
+package org.alfresco.module.org_alfresco_module_rm.test.integration.issue;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+
+import org.alfresco.model.ContentModel;
+import org.alfresco.module.org_alfresco_module_rm.action.dm.CreateRecordAction;
+import org.alfresco.module.org_alfresco_module_rm.action.impl.FileToAction;
+import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase;
+import org.alfresco.repo.security.authentication.AuthenticationUtil;
+import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
+import org.alfresco.service.cmr.action.Action;
+import org.alfresco.service.cmr.repository.ChildAssociationRef;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.rule.Rule;
+import org.alfresco.service.cmr.rule.RuleService;
+import org.alfresco.service.cmr.rule.RuleType;
+import org.alfresco.service.namespace.NamespaceService;
+import org.alfresco.service.namespace.QName;
+
+/**
+ * System test for RM-3993: Exceptions thrown when concurrently creating identical folder structure
+ *
+ * @author Silviu Dinuta
+ * @since 2.3.0.7
+ */
+public class RM3993Test extends BaseRMTestCase
+{
+ private static final int NUMBER_OF_BATCHES = 4;
+ private static final int NUMBER_IN_BATCH = 500;
+
+ private RuleService ruleService;
+ private NodeRef ruleFolder;
+ private NodeRef nodeRefCategory1;
+
+ @Override
+ protected void initServices()
+ {
+ super.initServices();
+
+ ruleService = (RuleService) applicationContext.getBean("RuleService");
+ }
+
+ @Override
+ protected boolean isCollaborationSiteTest()
+ {
+ return true;
+ }
+
+ @Override
+ protected boolean isRecordTest()
+ {
+ return true;
+ }
+
+ /**
+ * Given that I have auto declare configured And that I have auto file configured to a path where only the record
+ * folder needs to be created When I add lots of documents in the same transaction Then the rules should fire And
+ * the documents should be filed in the new record folder
+ */
+ public void testAutoDeclareAutoFileCreateRecordFolderOnly() throws Exception
+ {
+ doTestInTransaction(new Test()
+ {
+ @Override
+ public Void run()
+ {
+ // create the folder
+ ruleFolder = fileFolderService.create(documentLibrary, "mytestfolder", ContentModel.TYPE_FOLDER)
+ .getNodeRef();
+
+ // create record category
+ nodeRefCategory1 = filePlanService.createRecordCategory(filePlan, "category1");
+
+ Action action = actionService.createAction(CreateRecordAction.NAME);
+ action.setParameterValue(CreateRecordAction.PARAM_FILE_PLAN, filePlan);
+
+ Rule rule = new Rule();
+ rule.setRuleType(RuleType.INBOUND);
+ rule.setTitle("my rule");
+ rule.setAction(action);
+ rule.setExecuteAsynchronously(true);
+ ruleService.saveRule(ruleFolder, rule);
+
+ Action fileAction = actionService.createAction(FileToAction.NAME);
+ fileAction.setParameterValue(FileToAction.PARAM_PATH,
+ "/category1/{node.cm:description}");
+ fileAction.setParameterValue(FileToAction.PARAM_CREATE_RECORD_PATH, true);
+
+ Rule fileRule = new Rule();
+ fileRule.setRuleType(RuleType.INBOUND);
+ fileRule.setTitle("my rule");
+ fileRule.setAction(fileAction);
+ fileRule.setExecuteAsynchronously(true);
+ ruleService.saveRule(filePlanService.getUnfiledContainer(filePlan), fileRule);
+
+ return null;
+ }
+
+ @Override
+ public void test(Void result) throws Exception
+ {
+ assertFalse(ruleService.getRules(ruleFolder).isEmpty());
+ }
+ });
+
+ List records = new ArrayList(NUMBER_OF_BATCHES * NUMBER_IN_BATCH);
+
+ for (int i = 0; i < NUMBER_OF_BATCHES; i++)
+ {
+ final int finali = i;
+ records.addAll(doTestInTransaction(new Test>()
+ {
+ @Override
+ public List run() throws Exception
+ {
+ List records = new ArrayList(NUMBER_IN_BATCH);
+ for (int j = 0; j < NUMBER_IN_BATCH; j++)
+ {
+ int count = (finali)* NUMBER_IN_BATCH + (j + 1);
+ String name = "content" + count + ".txt";
+ System.out.println(name + " - creating");
+
+ Random rand = new Random();
+ int descInt = rand.nextInt(2)+1;
+ NodeRef record = createFile(ruleFolder, name, Integer.toString(descInt), ContentModel.TYPE_CONTENT);
+ records.add(record);
+ }
+ return records;
+ }
+ }));
+ }
+
+ try
+ {
+ while (!records.isEmpty())
+ {
+ Thread.sleep(1000);
+
+ final Iterator temp = records.iterator();
+ doTestInTransaction(new Test()
+ {
+ @Override
+ public Void run() throws Exception
+ {
+ while (temp.hasNext())
+ {
+ NodeRef record = temp.next();
+ if (nodeService.hasAspect(record, ASPECT_RECORD) && recordService.isFiled(record))
+ {
+ String name = (String) nodeService.getProperty(record, ContentModel.PROP_NAME);
+ System.out.println(name + " - complete");
+ temp.remove();
+ }
+ }
+
+ return null;
+ }
+ });
+ }
+ }
+ catch (Exception exception)
+ {
+ exception.printStackTrace();
+ throw exception;
+ }
+
+ Integer numberOfRecords = AuthenticationUtil.runAsSystem(new RunAsWork()
+ {
+
+ @Override
+ public Integer doWork() throws Exception
+ {
+ List containedRecordFolders = filePlanService.getContainedRecordFolders(nodeRefCategory1);
+ int numberOfRecords = 0;
+ for(NodeRef recordFolder : containedRecordFolders)
+ {
+ numberOfRecords = numberOfRecords + fileFolderService.list(recordFolder).size();
+ }
+ return numberOfRecords;
+ }
+ });
+ assertTrue(numberOfRecords == NUMBER_OF_BATCHES * NUMBER_IN_BATCH);
+ }
+
+ private NodeRef createFile(NodeRef parentNodeRef, String name, String descrption, QName typeQName)
+ {
+ Map properties = new HashMap(11);
+ properties.put(ContentModel.PROP_NAME, (Serializable) name);
+ properties.put(ContentModel.PROP_DESCRIPTION, (Serializable) descrption);
+ QName assocQName = QName.createQName(
+ NamespaceService.CONTENT_MODEL_1_0_URI,
+ QName.createValidLocalName(name));
+ ChildAssociationRef assocRef = nodeService.createNode(
+ parentNodeRef,
+ ContentModel.ASSOC_CONTAINS,
+ assocQName,
+ typeQName,
+ properties);
+ NodeRef nodeRef = assocRef.getChildRef();
+ return nodeRef;
+ }
+}
\ No newline at end of file
diff --git a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/record/CreateInplaceRecordTest.java b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/record/CreateInplaceRecordTest.java
new file mode 100644
index 0000000000..51159550ac
--- /dev/null
+++ b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/record/CreateInplaceRecordTest.java
@@ -0,0 +1,188 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2016 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+
+package org.alfresco.module.org_alfresco_module_rm.test.integration.record;
+
+import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel;
+import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase;
+import org.alfresco.repo.security.authentication.AuthenticationUtil;
+import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
+import org.alfresco.service.cmr.model.FileExistsException;
+import org.alfresco.service.cmr.model.FileNotFoundException;
+import org.alfresco.service.cmr.security.AccessStatus;
+
+/**
+ * Create Inplace Record Test
+ *
+ * @author Roy Wetherall
+ */
+public class CreateInplaceRecordTest extends BaseRMTestCase
+{
+ @Override
+ protected boolean isCollaborationSiteTest()
+ {
+ return true;
+ }
+
+ /**
+ * Given a document in a collaboration site
+ * When the document is declared by a site collaborator
+ * Then the document becomes a record
+ * And the site users have the appropriate in-place permissions on the record
+ */
+ public void testCreateInplaceRecordFromCollabSite()
+ {
+ doBehaviourDrivenTest(new BehaviourDrivenTest()
+ {
+ public void given()
+ {
+ // Check that the document is not a record
+ assertFalse("The document should not be a record", recordService.isRecord(dmDocument));
+ }
+
+ public void when()
+ {
+ // Declare the document as a record
+ AuthenticationUtil.runAs(new RunAsWork()
+ {
+ public Void doWork() throws Exception
+ {
+ // Declare record
+ recordService.createRecord(filePlan, dmDocument);
+
+ return null;
+ }
+ }, dmCollaborator);
+ }
+
+ public void then()
+ {
+ // Check that the document is a record now
+ assertTrue("The document should now be a record", recordService.isRecord(dmDocument));
+
+ // Check that the record is in the unfiled container
+
+ // Check that the record is still a child of the collaboration folder
+
+ // Check that the collaborator has filling permissions on the record
+ AuthenticationUtil.runAs(new RunAsWork()
+ {
+ public Void doWork() throws Exception
+ {
+ assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(dmDocument, RMPermissionModel.FILING));
+ assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(dmDocument, RMPermissionModel.READ_RECORDS));
+ return null;
+ }
+ }, dmCollaborator);
+
+
+ // Check that the consumer has read permissions on the record
+ AuthenticationUtil.runAs(new RunAsWork()
+ {
+ public Void doWork() throws Exception
+ {
+ assertEquals(AccessStatus.DENIED, permissionService.hasPermission(dmDocument, RMPermissionModel.FILING));
+ assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(dmDocument, RMPermissionModel.READ_RECORDS));
+ return null;
+ }
+ }, dmConsumer);
+
+ }
+ });
+ }
+
+ public void testFileInplaceRecordFromCollabSite()
+ {
+ doBehaviourDrivenTest(new BehaviourDrivenTest()
+ {
+ public void given()
+ {
+ // Check that the document is not a record
+ assertFalse("The document should not be a record", recordService.isRecord(dmDocument));
+
+ // Declare the document as a record
+ AuthenticationUtil.runAs(new RunAsWork()
+ {
+ public Void doWork() throws Exception
+ {
+ // Declare record
+ recordService.createRecord(filePlan, dmDocument);
+
+ return null;
+ }
+ }, dmCollaborator);
+
+ // Check that the document is a record
+ assertTrue("The document should be a record", recordService.isRecord(dmDocument));
+ assertFalse("The record should not be filed", recordService.isFiled(dmDocument));
+ }
+
+ public void when() throws FileExistsException, FileNotFoundException
+ {
+ // file the document to a location in the file plan
+ fileFolderService.move(dmDocument, rmFolder, null);
+ }
+
+ public void then()
+ {
+ // Check that the document is a record now
+ assertTrue("The document should be a record", recordService.isRecord(dmDocument));
+ assertTrue("The record hsould be filed", recordService.isFiled(dmDocument));
+
+ // Check that the record is in the unfiled container
+ // TODO
+
+ // Check that the record is still a child of the collaboration folder
+ // TODO
+
+ // Check that the collaborator has filling permissions on the record
+ AuthenticationUtil.runAs(new RunAsWork()
+ {
+ public Void doWork() throws Exception
+ {
+ assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(dmDocument, RMPermissionModel.FILING));
+ assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(dmDocument, RMPermissionModel.READ_RECORDS));
+ return null;
+ }
+ }, dmCollaborator);
+
+
+ // Check that the consumer has read permissions on the record
+ AuthenticationUtil.runAs(new RunAsWork()
+ {
+ public Void doWork() throws Exception
+ {
+ assertEquals(AccessStatus.DENIED, permissionService.hasPermission(dmDocument, RMPermissionModel.FILING));
+ assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(dmDocument, RMPermissionModel.READ_RECORDS));
+ return null;
+ }
+ }, dmConsumer);
+
+ }
+ });
+ }
+}
diff --git a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/legacy/action/FileToActionTest.java b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/legacy/action/FileToActionTest.java
index c77928b7a1..11020e2c57 100644
--- a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/legacy/action/FileToActionTest.java
+++ b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/legacy/action/FileToActionTest.java
@@ -119,24 +119,27 @@ public class FileToActionTest extends BaseRMTestCase
// create record from document
recordService.createRecord(filePlan, dmDocument);
- // check things have gone according to plan
- assertTrue(recordService.isRecord(dmDocument));
- assertFalse(recordService.isFiled(dmDocument));
-
- AuthenticationUtil.runAsSystem(new AuthenticationUtil.RunAsWork()
- {
- public Void doWork() throws Exception
- {
- // is the unfiled container the primary parent of the filed record
- NodeRef parent = nodeService.getPrimaryParent(dmDocument).getParentRef();
- assertEquals(filePlanService.getUnfiledContainer(filePlan), parent);
-
- // TODO Auto-generated method stub
- return null;
- }});
-
return null;
}
+
+ @Override
+ public void test(Void result) throws Exception
+ {
+ AuthenticationUtil.runAs(() ->
+ {
+ // check things have gone according to plan
+ assertTrue(recordService.isRecord(dmDocument));
+ assertFalse(recordService.isFiled(dmDocument));
+
+ // is the unfiled container the primary parent of the filed record
+ NodeRef parent = nodeService.getPrimaryParent(dmDocument).getParentRef();
+ assertEquals(filePlanService.getUnfiledContainer(filePlan), parent);
+
+ return null;
+ },
+ AuthenticationUtil.getAdminUserName());
+ }
+
}, dmCollaborator);
}
diff --git a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/legacy/service/RecordServiceImplTest.java b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/legacy/service/RecordServiceImplTest.java
index adb64abe8a..7f80b18167 100644
--- a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/legacy/service/RecordServiceImplTest.java
+++ b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/legacy/service/RecordServiceImplTest.java
@@ -291,7 +291,8 @@ public class RecordServiceImplTest extends BaseRMTestCase
public void test(Void result)
{
- checkPermissions(READ_RECORDS, AccessStatus.DENIED, // file plan
+ checkPermissions(READ_RECORDS,
+ AccessStatus.DENIED, // file plan
AccessStatus.DENIED, // unfiled container
AccessStatus.DENIED, // record category
AccessStatus.DENIED, // record folder
@@ -300,7 +301,8 @@ public class RecordServiceImplTest extends BaseRMTestCase
assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(filePlan,
RMPermissionModel.VIEW_RECORDS));
- checkPermissions(FILING, AccessStatus.DENIED, // file plan
+ checkPermissions(FILING,
+ AccessStatus.DENIED, // file plan
AccessStatus.DENIED, // unfiled container
AccessStatus.DENIED, // record category
AccessStatus.DENIED, // record folder
diff --git a/rm-community/rm-community-repo/unit-test/java/org/alfresco/repo/web/scripts/roles/DynamicAuthoritiesGetUnitTest.java b/rm-community/rm-community-repo/unit-test/java/org/alfresco/repo/web/scripts/roles/DynamicAuthoritiesGetUnitTest.java
new file mode 100644
index 0000000000..70304fb172
--- /dev/null
+++ b/rm-community/rm-community-repo/unit-test/java/org/alfresco/repo/web/scripts/roles/DynamicAuthoritiesGetUnitTest.java
@@ -0,0 +1,418 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2016 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+/*
+ * Copyright (C) 2005-2014 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+
+package org.alfresco.repo.web.scripts.roles;
+
+import static java.util.Collections.emptyMap;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.collect.ImmutableMap;
+
+import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
+import org.alfresco.module.org_alfresco_module_rm.security.ExtendedReaderDynamicAuthority;
+import org.alfresco.module.org_alfresco_module_rm.security.ExtendedSecurityService;
+import org.alfresco.module.org_alfresco_module_rm.security.ExtendedWriterDynamicAuthority;
+import org.alfresco.module.org_alfresco_module_rm.test.util.AlfMock;
+import org.alfresco.module.org_alfresco_module_rm.test.util.BaseWebScriptUnitTest;
+import org.alfresco.repo.domain.node.NodeDAO;
+import org.alfresco.repo.domain.patch.PatchDAO;
+import org.alfresco.repo.domain.qname.QNameDAO;
+import org.alfresco.repo.transaction.RetryingTransactionHelper;
+import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.repository.NodeService;
+import org.alfresco.service.cmr.security.PermissionService;
+import org.alfresco.service.namespace.QName;
+import org.alfresco.service.transaction.TransactionService;
+import org.alfresco.util.Pair;
+import org.json.JSONObject;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.springframework.extensions.webscripts.DeclarativeWebScript;
+
+/**
+ * DynamicAuthoritiesGet Unit Test
+ *
+ * @author Silviu Dinuta
+ */
+@SuppressWarnings("deprecation")
+public class DynamicAuthoritiesGetUnitTest extends BaseWebScriptUnitTest implements RecordsManagementModel
+{
+ /** test data */
+ private static final Long ASPECT_ID = 123l;
+ private static final QName ASPECT = AlfMock.generateQName();
+
+ /** mocks */
+ @Mock
+ private PatchDAO mockedPatchDAO;
+ @Mock
+ private NodeDAO mockedNodeDAO;
+ @Mock
+ private QNameDAO mockedQnameDAO;
+ @Mock
+ private NodeService mockedNodeService;
+ @Mock
+ private PermissionService mockedPermissionService;
+ @Mock
+ private ExtendedSecurityService mockedExtendedSecurityService;
+ @Mock
+ private TransactionService mockedTransactionService;
+ @Mock
+ private RetryingTransactionHelper mockedRetryingTransactionHelper;
+
+ /** test component */
+ @InjectMocks
+ private DynamicAuthoritiesGet webScript;
+
+ @Override
+ protected DeclarativeWebScript getWebScript()
+ {
+ return webScript;
+ }
+
+ @Override
+ protected String getWebScriptTemplate()
+ {
+ return "alfresco/templates/webscripts/org/alfresco/repository/roles/rm-dynamicauthorities.get.json.ftl";
+ }
+
+ /**
+ * Before test
+ */
+ @SuppressWarnings("unchecked")
+ @Before
+ public void before()
+ {
+ MockitoAnnotations.initMocks(this);
+ webScript.setNodeService(mockedNodeService);
+ webScript.setPermissionService(mockedPermissionService);
+ webScript.setExtendedSecurityService(mockedExtendedSecurityService);
+ // setup retrying transaction helper
+ Answer