diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/model/recordsModel.xml b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/model/recordsModel.xml index d23a9f7b37..83031dcb57 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/model/recordsModel.xml +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/model/recordsModel.xml @@ -718,6 +718,10 @@ Saved search + + Disposition processed + + Vital Record Definition diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/patch/rm-patch-v24-context.xml b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/patch/rm-patch-v24-context.xml index d1e70ff98f..e263b4aa28 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/patch/rm-patch-v24-context.xml +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/patch/rm-patch-v24-context.xml @@ -16,5 +16,5 @@ - + \ No newline at end of file diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/query/rm-common-SqlMap.xml b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/query/rm-common-SqlMap.xml index 457b70432e..766842f0a8 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/query/rm-common-SqlMap.xml +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/query/rm-common-SqlMap.xml @@ -3,16 +3,30 @@ - - - - + + + + - - - - - select count(*) from @@ -21,7 +35,7 @@ where prop.qname_id = ? and prop.string_value = ? - + @@ -39,10 +53,10 @@ - - select p.node_id from @@ -55,5 +69,19 @@ + - \ 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 30cddbbeee..730ab7f56e 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 @@ -680,4 +680,19 @@ parent="rmBaseWebscript"> + + + + + + + + + + + + + \ No newline at end of file diff --git a/rm-community/rm-community-repo/config/alfresco/templates/webscripts/org/alfresco/repository/schedules/rm-updaterecordschedule.get.desc.xml b/rm-community/rm-community-repo/config/alfresco/templates/webscripts/org/alfresco/repository/schedules/rm-updaterecordschedule.get.desc.xml new file mode 100644 index 0000000000..8ff54084a0 --- /dev/null +++ b/rm-community/rm-community-repo/config/alfresco/templates/webscripts/org/alfresco/repository/schedules/rm-updaterecordschedule.get.desc.xml @@ -0,0 +1,13 @@ + + Updates Record Schedules based on Hierarchical Retention Instructions + + URL parameter maxRecordFolders is optional, and represents the maximum number of record folders that should be processed. If not specified maxRecordFolders will be set to the max value for an integer.
+ URL parameter recordFolder is optional, and represents the nodeRef of a record folder whose records should be processed. If specified then maxRecordFolders will be ignored.
+ ]]> +
+ /api/rm/rm-updateRecordSchedule?maxRecordFolders={maxRecordFolders?}&recordFolder={recordFolder?} + argument + admin + required +
\ No newline at end of file diff --git a/rm-community/rm-community-repo/config/alfresco/templates/webscripts/org/alfresco/repository/schedules/rm-updaterecordschedule.get.json.ftl b/rm-community/rm-community-repo/config/alfresco/templates/webscripts/org/alfresco/repository/schedules/rm-updaterecordschedule.get.json.ftl new file mode 100644 index 0000000000..2b395291a4 --- /dev/null +++ b/rm-community/rm-community-repo/config/alfresco/templates/webscripts/org/alfresco/repository/schedules/rm-updaterecordschedule.get.json.ftl @@ -0,0 +1,30 @@ +<#-- + #%L + Alfresco Records Management Module + %% + Copyright (C) 2005 - 2019 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}" +} diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionService.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionService.java index ef7a27b86a..cf700d0096 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionService.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionService.java @@ -237,6 +237,14 @@ public interface DispositionService */ void updateNextDispositionAction(NodeRef nodeRef); + /** + * Updates the next disposition action + * + * @param nodeRef node reference + * @param dispositionSchedule the schedule to be applied + */ + void updateNextDispositionAction(NodeRef nodeRef, DispositionSchedule dispositionSchedule); + /** * Refreshes the disposition action details of the given node. * diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImpl.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImpl.java index a0f76bab82..b6e1d75add 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImpl.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/disposition/DispositionServiceImpl.java @@ -977,8 +977,36 @@ public class DispositionServiceImpl extends ServiceBaseImpl public Void doWork() { // Get this disposition instructions for the node - DispositionSchedule di = getDispositionSchedule(nodeRef); - if (di != null) + DispositionSchedule dispositionSchedule = getDispositionSchedule(nodeRef); + + updateNextDispositionAction(nodeRef, dispositionSchedule); + + return null; + } + + }; + + AuthenticationUtil.runAsSystem(runAsWork); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService#updateNextDispositionAction(NodeRef) + */ + @Override + public void updateNextDispositionAction(final NodeRef nodeRef, final DispositionSchedule dispositionSchedule) + { + + + RunAsWork runAsWork = new RunAsWork() + { + /** + * @see org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork#doWork() + */ + @Override + public Void doWork() + { + + if (dispositionSchedule != null) { // Get the current action node NodeRef currentDispositionAction = null; @@ -997,7 +1025,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl nodeService.moveNode(currentDispositionAction, nodeRef, ASSOC_DISPOSITION_ACTION_HISTORY, ASSOC_DISPOSITION_ACTION_HISTORY); } - List dispositionActionDefinitions = di.getDispositionActionDefinitions(); + List dispositionActionDefinitions = dispositionSchedule.getDispositionActionDefinitions(); DispositionActionDefinition currentDispositionActionDefinition = null; DispositionActionDefinition nextDispositionActionDefinition = null; @@ -1013,14 +1041,14 @@ public class DispositionServiceImpl extends ServiceBaseImpl { // Get the current action String currentADId = (String) nodeService.getProperty(currentDispositionAction, PROP_DISPOSITION_ACTION_ID); - currentDispositionActionDefinition = di.getDispositionActionDefinition(currentADId); + currentDispositionActionDefinition = dispositionSchedule.getDispositionActionDefinition(currentADId); // When the record has multiple disposition schedules the current disposition action may not be found by id // In this case it will be searched by name if(currentDispositionActionDefinition == null) { String currentADName = (String) nodeService.getProperty(currentDispositionAction, PROP_DISPOSITION_ACTION); - currentDispositionActionDefinition = di.getDispositionActionDefinitionByName(currentADName); + currentDispositionActionDefinition = dispositionSchedule.getDispositionActionDefinitionByName(currentADName); } // Get the next disposition action diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/RecordsManagementModel.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/RecordsManagementModel.java index c8f4775bf9..f9ffd80d6f 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/RecordsManagementModel.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/RecordsManagementModel.java @@ -285,9 +285,9 @@ public interface RecordsManagementModel extends RecordsManagementCustomModel QName PROP_COUNT = QName.createQName(RM_URI, "count"); QName ASPECT_SAVED_SEARCH = QName.createQName(RM_URI, "savedSearch"); - //Workaround for RM-6788 String GL_URI = "http://www.alfresco.org/model/glacier/1.0"; QName ASPECT_ARCHIVED = QName.createQName(GL_URI, "archived"); + QName ASPECT_DISPOSITION_PROCESSED = QName.createQName(RM_URI, "dispositionProcessed"); } diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/NodeRefEntity.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/NodeRefEntity.java new file mode 100644 index 0000000000..203a186e45 --- /dev/null +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/NodeRefEntity.java @@ -0,0 +1,88 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2019 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.query; + +/** + * NodeRef Entity - used by {@link RecordsManagementQueryDAOImpl}. + * + * @author Tom Page + * @since 2.5.0.4 + */ +public class NodeRefEntity +{ + private Long num; + private String protocol; + private String identifier; + private String uuid; + + /** + * Default constructor. + */ + public NodeRefEntity() + { + } + + public Long getNum() + { + return num; + } + + public void setNum(Long num) + { + this.num = num; + } + + public String getProtocol() + { + return protocol; + } + + public void setProtocol(String protocol) + { + this.protocol = protocol; + } + + public String getIdentifier() + { + return identifier; + } + + public void setIdentifier(String identifier) + { + this.identifier = identifier; + } + + public String getUuid() + { + return uuid; + } + + public void setUuid(String uuid) + { + this.uuid = uuid; + } +} diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/RecordsManagementQueryDAO.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/RecordsManagementQueryDAO.java index a3c041fc9e..6810d68687 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/RecordsManagementQueryDAO.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/RecordsManagementQueryDAO.java @@ -27,9 +27,11 @@ package org.alfresco.module.org_alfresco_module_rm.query; +import java.util.List; import java.util.Set; import org.alfresco.service.cmr.repository.NodeRef; +import java.util.Collection; import org.alfresco.service.namespace.QName; @@ -54,6 +56,17 @@ public interface RecordsManagementQueryDAO int getCountRmaIdentifier(String identifierValue); /** + * Returns a number of nodeRefs for record folders in the system + * that have the property recordSearchHasDispositionSchedule:true + * (used for MNT-20864) + * @param start long - the first result row to return + * @param end long - the last result row to return + * @return list of node refs + */ + List getRecordFoldersWithSchedules(Long start, Long end); + + /** + * Returns whether a given node contains children with one of the given values for the given property * Returns distinct property values from children for the given property * * @param parent the parent to evaluate @@ -63,7 +76,7 @@ public interface RecordsManagementQueryDAO public Set getChildrenStringPropertyValues(NodeRef parent, QName property); /** - * @param contentUrl the URL of the content url entity + * @param contentUrl the URL of the content url entity * @return Set a set of nodes that reference the given content url */ Set getNodeRefsWhichReferenceContentUrl(String contentUrl); diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/RecordsManagementQueryDAOImpl.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/RecordsManagementQueryDAOImpl.java index a13a6fcd44..36131f95fa 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/RecordsManagementQueryDAOImpl.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/RecordsManagementQueryDAOImpl.java @@ -27,8 +27,11 @@ package org.alfresco.module.org_alfresco_module_rm.query; -import java.util.Collections; + +import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -56,33 +59,43 @@ import org.mybatis.spring.SqlSessionTemplate; */ public class RecordsManagementQueryDAOImpl implements RecordsManagementQueryDAO, RecordsManagementModel { - /** logger */ - @SuppressWarnings ("unused") + /** + * logger + */ + @SuppressWarnings("unused") private static final Log logger = LogFactory.getLog(RecordsManagementQueryDAOImpl.class); - /** query names */ + /** + * query names + */ private static final String COUNT_IDENTIFIER = "alfresco.query.rm.select_CountRMIndentifier"; private static final String GET_CHILDREN_PROPERTY_VALUES = "select_GetStringPropertyValuesOfChildren"; private static final String SELECT_NODE_IDS_WHICH_REFERENCE_CONTENT_URL = "select_NodeIdsWhichReferenceContentUrl"; + private static final String SCHEDULED_FOLDERS = "alfresco.query.rm.select_RecordFoldersWithSchedules"; + private static final String SCHEDULED_FOLDERS_COUNT = "alfresco.query.rm.select_RecordFoldersWithSchedulesCount"; - /** SQL session template */ + /** + * SQL session template + */ protected SqlSessionTemplate template; - - /** QName DAO */ + + /** + * QName DAO + */ protected QNameDAO qnameDAO; protected NodeDAO nodeDAO; protected TenantService tenantService; - + /** - * @param sqlSessionTemplate SQL session template + * @param sqlSessionTemplate SQL session template */ - public final void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) + public final void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) { this.template = sqlSessionTemplate; } - + /** - * @param qnameDAO qname DAO + * @param qnameDAO qname DAO */ public final void setQnameDAO(QNameDAO qnameDAO) { @@ -106,25 +119,25 @@ public class RecordsManagementQueryDAOImpl implements RecordsManagementQueryDAO, public int getCountRmaIdentifier(String identifierValue) { int result = 0; - + // lookup the id of the identifier property qname Pair pair = qnameDAO.getQName(PROP_IDENTIFIER); if (pair != null) - { + { // create query params Map params = new HashMap<>(2); params.put("qnameId", pair.getFirst()); params.put("idValue", identifierValue); - + // return the number of rma identifiers found that match the passed value - Integer count = (Integer)template.selectOne(COUNT_IDENTIFIER, params); - + Integer count = (Integer) template.selectOne(COUNT_IDENTIFIER, params); + if (count != null) { result = count; } } - + return result; } @@ -158,8 +171,8 @@ public class RecordsManagementQueryDAOImpl implements RecordsManagementQueryDAO, /** * Get a set of node reference which reference the provided content URL * + * @param String contentUrl content URL * @return Set set of nodes that reference the provided content URL - * @param String contentUrl content URL */ @Override public Set getNodeRefsWhichReferenceContentUrl(String contentUrl) @@ -208,14 +221,16 @@ public class RecordsManagementQueryDAOImpl implements RecordsManagementQueryDAO, if (logger.isDebugEnabled()) { - logMessage.append(nodeRefToAdd).append(" (from version)"); + logMessage.append(nodeRefToAdd) + .append(" (from version)"); } } // add the node ref of the referencing node else { - nodeRefToAdd = nodeDAO.getNodeIdStatus(nodeId).getNodeRef(); + nodeRefToAdd = nodeDAO.getNodeIdStatus(nodeId) + .getNodeRef(); if (logger.isDebugEnabled()) { logMessage.append(nodeRefToAdd); @@ -240,4 +255,33 @@ public class RecordsManagementQueryDAOImpl implements RecordsManagementQueryDAO, return nodesReferencingContentUrl; } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.query.RecordsManagementQueryDAO#getRecordFoldersWithSchedules(Long, Long) + */ + @Override + public List getRecordFoldersWithSchedules(Long start, Long end) + { + Map params = new HashMap<>(2); + params.put("processed", qnameDAO.getQName(ASPECT_DISPOSITION_PROCESSED) + .getFirst()); + params.put("folderQnameId", qnameDAO.getQName(TYPE_RECORD_FOLDER) + .getFirst()); + params.put("start", start); + params.put("end", end); + + List entities = template.selectList(SCHEDULED_FOLDERS, params); + + List results = new ArrayList<>(); + + // convert the entities to NodeRefs + for (NodeRefEntity nodeRefEntity : entities) + { + results.add( + new NodeRef(nodeRefEntity.getProtocol(), nodeRefEntity.getIdentifier(), nodeRefEntity.getUuid())); + } + + return results; + } + } diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/repo/web/scripts/schedule/UpdateRecordScheduleGet.java b/rm-community/rm-community-repo/source/java/org/alfresco/repo/web/scripts/schedule/UpdateRecordScheduleGet.java new file mode 100644 index 0000000000..ee5c16207e --- /dev/null +++ b/rm-community/rm-community-repo/source/java/org/alfresco/repo/web/scripts/schedule/UpdateRecordScheduleGet.java @@ -0,0 +1,430 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2019 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.schedule; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.io.Writer; +import java.text.MessageFormat; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.servlet.http.HttpServletResponse; + +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule; +import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.module.org_alfresco_module_rm.query.RecordsManagementQueryDAO; +import org.alfresco.module.org_alfresco_module_rm.record.RecordService; +import org.alfresco.repo.domain.node.NodeDAO; +import org.alfresco.repo.domain.qname.QNameDAO; +import org.alfresco.repo.policy.BehaviourFilter; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.transaction.TransactionService; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.extensions.webscripts.AbstractWebScript; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.Format; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; +import org.springframework.extensions.webscripts.WebScriptResponse; + +/** + * Webscript used to update records that are missing their schedule information + * + * @author Roy Wetherall + */ +public class UpdateRecordScheduleGet extends AbstractWebScript implements RecordsManagementModel +{ + /** + * logger + */ + private static Log logger = LogFactory.getLog(UpdateRecordScheduleGet.class); + + /** + * parameters + */ + private static final String PARAM_MAX_RECORD_FOLDERS = "maxRecordFolders"; + private static final String PARAM_RECORD_FOLDER = "recordFolder"; + + private static final String SUCCESS_STATUS = "success"; + private static final String MODEL_STATUS = "responsestatus"; + private static final String MODEL_MESSAGE = "message"; + private static final String MESSAGE_ALL_TEMPLATE = "Updated {0} records from {1} folders with updated disposition instructions."; + private static final String MESSAGE_FOLDER_TEMPLATE = "Updated records in folder {0} with updated disposition instructions."; + + /** + * services + */ + private NodeService nodeService; + private DispositionService dispositionService; + private RecordService recordService; + private TransactionService transactionService; + private RecordsManagementQueryDAO recordsManagementQueryDAO; + private BehaviourFilter behaviourFilter; + private NodeDAO nodeDAO; + private QNameDAO qnameDAO; + + /** + * service setters + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + public void setRecordsManagementQueryDAO(RecordsManagementQueryDAO recordsManagementQueryDAO) + { + this.recordsManagementQueryDAO = recordsManagementQueryDAO; + } + + public void setRecordService(RecordService recordService) + { + this.recordService = recordService; + } + + public void setDispositionService(DispositionService dispositionService) + { + this.dispositionService = dispositionService; + } + + public void setTransactionService(TransactionService transactionService) + { + this.transactionService = transactionService; + } + + public void setBehaviourFilter(BehaviourFilter behaviourFilter) + { + this.behaviourFilter = behaviourFilter; + } + + public void setNodeDAO(NodeDAO nodeDAO) + { + this.nodeDAO = nodeDAO; + } + + public void setQnameDAO(QNameDAO qnameDAO) + { + this.qnameDAO = qnameDAO; + } + + /** + * Build web script model + */ + protected Map buildModel(WebScriptRequest req, WebScriptResponse res) throws IOException + { + Map model = new HashMap<>(); + transactionService.getRetryingTransactionHelper() + .doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public String execute() throws Throwable + { + qnameDAO.getOrCreateQName(ASPECT_DISPOSITION_PROCESSED); + return null; + } + + }, false, true); + + int maxRecordFolders = getMaxRecordFolders(req); + NodeRef recordFolder = getRecordFolder(req); + + int processedRecords = 0; + String message; + if (recordFolder != null) + { + // Process the specified record folder + updateRecordFolder(recordFolder); + message = MessageFormat.format(MESSAGE_FOLDER_TEMPLATE, recordFolder); + } + else + { + int processedRecordFolders = 0; + int queryBatchSize = 10000; + Long maxNodeId = nodeDAO.getMaxNodeId(); + for (Long i = 0L; i < maxNodeId; i += queryBatchSize) + { + List folders = recordsManagementQueryDAO.getRecordFoldersWithSchedules(i, i + queryBatchSize); + for (NodeRef folder : folders) + { + processedRecords = processedRecords + updateRecordFolder(folder); + processedRecordFolders++; + + if (processedRecordFolders >= maxRecordFolders) + { + // stop processing since we have meet our limit + break; + } + } + + if (processedRecordFolders >= maxRecordFolders) + { + // stop processing since we have meet our limit + break; + } + } + message = MessageFormat.format(MESSAGE_ALL_TEMPLATE, processedRecords, processedRecordFolders); + } + + model.put(MODEL_STATUS, SUCCESS_STATUS); + model.put(MODEL_MESSAGE, message); + logger.info(message); + + return model; + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.web.scripts.content.StreamContent#execute(org.springframework.extensions.webscripts. + * WebScriptRequest, org.springframework.extensions.webscripts.WebScriptResponse) + */ + @Override + public void execute(WebScriptRequest req, WebScriptResponse res) throws IOException + { + // retrieve requested format + String format = req.getFormat(); + + try + { + String mimetype = getContainer().getFormatRegistry() + .getMimeType(req.getAgent(), format); + if (mimetype == null) + { + throw new WebScriptException("Web Script format '" + format + "' is not registered"); + } + + // construct model for script / template + Status status = new Status(); + Cache cache = new Cache(getDescription().getRequiredCache()); + + Map model = buildModel(req, res); + + if (model == null) { return; } + model.put("status", status); + model.put("cache", cache); + + Map templateModel = createTemplateParameters(req, res, model); + + // render output + int statusCode = status.getCode(); + if (statusCode != HttpServletResponse.SC_OK && !req.forceSuccessStatus()) + { + if (logger.isDebugEnabled()) + { + logger.debug("Force success status header in response: " + req.forceSuccessStatus()); + logger.debug("Setting status " + statusCode); + } + res.setStatus(statusCode); + } + + // apply location + String location = status.getLocation(); + if (location != null && location.length() > 0) + { + if (logger.isDebugEnabled()) + logger.debug("Setting location to " + location); + res.setHeader(WebScriptResponse.HEADER_LOCATION, location); + } + + // apply cache + res.setCache(cache); + + String callback = null; + if (getContainer().allowCallbacks()) + { + callback = req.getJSONCallback(); + } + if (format.equals(WebScriptResponse.JSON_FORMAT) && callback != null) + { + if (logger.isDebugEnabled()) + logger.debug( + "Rendering JSON callback response: content type=" + Format.JAVASCRIPT.mimetype() + ", status=" + + statusCode + ", callback=" + callback); + + // NOTE: special case for wrapping JSON results in a javascript function callback + res.setContentType(Format.JAVASCRIPT.mimetype() + ";charset=UTF-8"); + res.getWriter() + .write((callback + "(")); + } + else + { + if (logger.isDebugEnabled()) + logger.debug("Rendering response: content type=" + mimetype + ", status=" + statusCode); + + res.setContentType(mimetype + ";charset=UTF-8"); + } + + // render response according to requested format + renderFormatTemplate(format, templateModel, res.getWriter()); + + if (format.equals(WebScriptResponse.JSON_FORMAT) && callback != null) + { + // NOTE: special case for wrapping JSON results in a javascript function callback + res.getWriter() + .write(")"); + } + } + catch (Throwable e) + { + if (logger.isDebugEnabled()) + { + StringWriter stack = new StringWriter(); + e.printStackTrace(new PrintWriter(stack)); + logger.debug("Caught exception; decorating with appropriate status template : " + stack.toString()); + } + + throw createStatusException(e, req, res); + } + } + + protected void renderFormatTemplate(String format, Map model, Writer writer) + { + format = (format == null) ? "" : format; + + String templatePath = getDescription().getId() + "." + format; + + if (logger.isDebugEnabled()) + logger.debug("Rendering template '" + templatePath + "'"); + + renderTemplate(templatePath, model, writer); + } + + protected int getMaxRecordFolders(WebScriptRequest req) + { + String valueStr = req.getParameter(PARAM_MAX_RECORD_FOLDERS); + int value = Integer.MAX_VALUE; + if (StringUtils.isNotBlank(valueStr)) + { + try + { + value = Integer.parseInt(valueStr); + } + catch (NumberFormatException ex) + { + //do nothing here, the value will remain 0L in this case + } + } + return value; + } + + protected NodeRef getRecordFolder(WebScriptRequest req) + { + String valueStr = req.getParameter(PARAM_RECORD_FOLDER); + NodeRef value = null; + if (StringUtils.isNotBlank(valueStr)) + { + value = new NodeRef(valueStr); + } + + return value; + } + + private int updateRecordFolder(final NodeRef recordFolder) + { + return transactionService.getRetryingTransactionHelper() + .doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Integer execute() throws Throwable + { + int recordCount = 0; + + behaviourFilter.disableBehaviour(ASPECT_FILE_PLAN_COMPONENT); + try + { + if (logger.isDebugEnabled()) + { + logger.info("Checking folder: " + recordFolder); + } + recordCount = AuthenticationUtil.runAsSystem(new AuthenticationUtil.RunAsWork() + { + @Override + public Integer doWork() throws Exception + { + DispositionSchedule schedule = dispositionService.getDispositionSchedule(recordFolder); + int innerRecordCount = 0; + if (schedule != null && schedule.isRecordLevelDisposition()) + { + + List records = recordService.getRecords(recordFolder); + for (NodeRef record : records) + { + if (!nodeService.hasAspect(record, ASPECT_DISPOSITION_LIFECYCLE)) + { + if (recordFolder.equals(nodeService.getPrimaryParent(record).getParentRef())) + { + if (logger.isDebugEnabled()) + { + logger.info("updating record: " + record); + } + + // update record disposition information + dispositionService.updateNextDispositionAction(record, schedule); + innerRecordCount++; + } + } + } + } + return innerRecordCount; + } + }); + nodeService.addAspect(recordFolder, ASPECT_DISPOSITION_PROCESSED, null); + } + finally + { + behaviourFilter.enableBehaviour(ASPECT_FILE_PLAN_COMPONENT); + } + return recordCount; + } + }, false, true); + } +}