/* * #%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 <http://www.gnu.org/licenses/>. * #L% */ package org.alfresco.module.org_alfresco_module_rm.dataset; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Set; 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.disposition.DispositionSchedule; 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.model.RecordsManagementModel; import org.alfresco.module.org_alfresco_module_rm.model.behaviour.RecordsManagementSearchBehaviour; import org.alfresco.module.org_alfresco_module_rm.recordfolder.RecordFolderService; import org.alfresco.module.org_alfresco_module_rm.role.FilePlanRoleService; import org.alfresco.module.org_alfresco_module_rm.role.Role; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authority.RMAuthority; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.search.ResultSet; import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.cmr.security.AuthorityService; import org.alfresco.service.cmr.security.AuthorityType; import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.cmr.view.ImporterService; import org.alfresco.service.cmr.view.Location; import org.alfresco.service.namespace.QName; import org.alfresco.util.ParameterCheck; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; public class DataSetServiceImpl implements DataSetService, RecordsManagementModel { /** Logger */ private static Log logger = LogFactory.getLog(DataSetServiceImpl.class); /** Registered data set implementations */ private Map<String, DataSet> dataSets = new HashMap<String, DataSet>(); /** Spaces store */ private static final StoreRef SPACES_STORE = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore"); /** Charset name */ private static final String CHARSET_NAME = "UTF-8"; /** Importer service */ private ImporterService importerService; /** Search service */ private SearchService searchService; /** Node service */ private NodeService nodeService; /** File plan service service */ private FilePlanService filePlanService; /** Permission service */ private PermissionService permissionService; /** Authority service */ private AuthorityService authorityService; /** File plan role service */ private FilePlanRoleService filePlanRoleService; /** Records management search behaviour */ private RecordsManagementSearchBehaviour recordsManagementSearchBehaviour; /** Disposition service */ private DispositionService dispositionService; /** Record folder service */ private RecordFolderService recordFolderService; /** * Set importer service * * @param importerService the importer service */ public void setImporterService(ImporterService importerService) { this.importerService = importerService; } /** * Set search service * * @param searchService the search service */ public void setSearchService(SearchService searchService) { this.searchService = searchService; } /** * Set node service * * @param nodeService the node service */ public void setNodeService(NodeService nodeService) { this.nodeService = nodeService; } /** * Set file plan service * * @param filePlanService the file plan service */ public void setFilePlanService(FilePlanService filePlanService) { this.filePlanService = filePlanService; } /** * Set permission service * * @param permissionService the permission service */ public void setPermissionService(PermissionService permissionService) { this.permissionService = permissionService; } /** * Set authority service * * @param authorityService the authority service */ public void setAuthorityService(AuthorityService authorityService) { this.authorityService = authorityService; } /** * @param filePlanRoleService file plan role service */ public void setFilePlanRoleService(FilePlanRoleService filePlanRoleService) { this.filePlanRoleService = filePlanRoleService; } /** * Set records management search behaviour * * @param recordsManagementSearchBehaviour the records management search * behaviour */ public void setRecordsManagementSearchBehaviour(RecordsManagementSearchBehaviour recordsManagementSearchBehaviour) { this.recordsManagementSearchBehaviour = recordsManagementSearchBehaviour; } /** * Set disposition service * * @param dispositionService the disposition service */ public void setDispositionService(DispositionService dispositionService) { this.dispositionService = dispositionService; } /** * Set record folder service * * @param recordFolderService the record folder service */ public void setRecordFolderService(RecordFolderService recordFolderService) { this.recordFolderService = recordFolderService; } /** * @see org.alfresco.module.org_alfresco_module_rm.dataset.DataSetService#register(org.alfresco.module.org_alfresco_module_rm.dataset.DataSet) */ @Override public void register(DataSet dataSet) { ParameterCheck.mandatory("dataSet", dataSet); this.dataSets.put(dataSet.getId(), dataSet); } /** * @see org.alfresco.module.org_alfresco_module_rm.dataset.DataSetService#getDataSets() */ @Override public Map<String, DataSet> getDataSets() { return this.dataSets; } /** * @see org.alfresco.module.org_alfresco_module_rm.dataset.DataSetService#getDataSets(NodeRef, * boolean) */ @Override public Map<String, DataSet> getDataSets(NodeRef filePlan, boolean excludeLoaded) { ParameterCheck.mandatory("filePlan", filePlan); ParameterCheck.mandatory("excludeLoaded", excludeLoaded); // Get the list of all available data sets Map<String, DataSet> dataSets = new HashMap<String, DataSet>(getDataSets()); // Should the list of unloaded data sets be retrieved if (excludeLoaded) { dataSets.keySet().removeAll(getLoadedDataSets(filePlan).keySet()); } // Return the (filtered) list of data sets return dataSets; } /** * @see org.alfresco.module.org_alfresco_module_rm.dataset.DataSetService#loadDataSet(java.lang.String, * org.alfresco.service.cmr.repository.NodeRef) */ @Override public void loadDataSet(NodeRef filePlan, String dataSetId) { ParameterCheck.mandatory("filePlan", filePlan); ParameterCheck.mandatoryString("dataSetId", dataSetId); // Get the data set DataSet dataSet = getDataSets().get(dataSetId); // Import the RM test data ACP into the the provided file plan node // reference InputStream is = null; try { is = getClass().getClassLoader().getResourceAsStream(dataSet.getPath()); if (is == null) { throw new AlfrescoRuntimeException("The '" + dataSet.getLabel() + "' import file could not be found!"); } // Import view Reader viewReader = new InputStreamReader(is, CHARSET_NAME); Location location = new Location(filePlan); importerService.importView(viewReader, location, null, null); // Patch data patchLoadedData(); // Set the data set id into the file plan's custom aspect setDataSetIdIntoFilePlan(dataSetId, filePlan); } catch (Exception ex) { throw new RuntimeException("Unexpected exception thrown. Please refer to the log files for details.", ex); } finally { if (is != null) { try { is.close(); is = null; } catch (IOException ex) { throw new RuntimeException("Failed to close the input stream!", ex); } } } } /** * @see org.alfresco.module.org_alfresco_module_rm.dataset.DataSetService#existsDataSet(java.lang.String) */ @Override public boolean existsDataSet(String dataSetId) { ParameterCheck.mandatoryString("dataSetId", dataSetId); return getDataSets().containsKey(dataSetId); } /** * @see org.alfresco.module.org_alfresco_module_rm.dataset.DataSetService#getLoadedDataSets(org.alfresco.service.cmr.repository.NodeRef) */ @Override public Map<String, DataSet> getLoadedDataSets(NodeRef filePlan) { ParameterCheck.mandatory("filePlan", filePlan); // Get the list of available data sets Map<String, DataSet> availableDataSets = new HashMap<String, DataSet>(getDataSets()); // Get the property value of the aspect Serializable dataSetIds = nodeService.getProperty(filePlan, PROP_LOADED_DATA_SET_IDS); // Check if any data has been loaded before if (dataSetIds != null) { // Filter the data sets which have already been loaded @SuppressWarnings("unchecked") ArrayList<String> loadedDataSetIds = (ArrayList<String>) dataSetIds; Iterator<Map.Entry<String, DataSet>> iterator = availableDataSets.entrySet().iterator(); while (iterator.hasNext()) { Entry<String, DataSet> entry = iterator.next(); String key = entry.getKey(); if (!loadedDataSetIds.contains(key)) { iterator.remove(); } } return availableDataSets; } return new HashMap<String, DataSet>(); } /** * @see org.alfresco.module.org_alfresco_module_rm.dataset.DataSetService#isLoadedDataSet(org.alfresco.service.cmr.repository.NodeRef, * java.lang.String) */ @Override public boolean isLoadedDataSet(NodeRef filePlan, String dataSetId) { ParameterCheck.mandatory("filePlan", filePlan); ParameterCheck.mandatory("dataSetId", dataSetId); return getLoadedDataSets(filePlan).containsKey(dataSetId); } /** * Temp method to patch AMP'ed data */ private void patchLoadedData() { AuthenticationUtil.RunAsWork<Object> runAsWork = new AuthenticationUtil.RunAsWork<Object>() { public Object doWork() { Set<NodeRef> rmRoots = filePlanService.getFilePlans(); logger.info("Bootstraping " + rmRoots.size() + " rm roots ..."); for (NodeRef rmRoot : rmRoots) { if (permissionService.getInheritParentPermissions(rmRoot)) { logger.info("Updating permissions for rm root: " + rmRoot); permissionService.setInheritParentPermissions(rmRoot, false); } String allRoleShortName = RMAuthority.ALL_ROLES_PREFIX + rmRoot.getId(); String allRoleGroupName = authorityService.getName(AuthorityType.GROUP, allRoleShortName); if (!authorityService.authorityExists(allRoleGroupName)) { logger.info("Creating all roles group for root node: " + rmRoot.toString()); // Create "all" role group for root node String allRoles = authorityService.createAuthority(AuthorityType.GROUP, allRoleShortName, RMAuthority.ALL_ROLES_DISPLAY_NAME, new HashSet<String>(Arrays.asList(RMAuthority.ZONE_APP_RM))); // Put all the role groups in it Set<Role> roles = filePlanRoleService.getRoles(rmRoot); for (Role role : roles) { logger.info(" - adding role group " + role.getRoleGroupName() + " to all roles group"); authorityService.addAuthority(allRoles, role.getRoleGroupName()); } // Set the permissions permissionService.setPermission(rmRoot, allRoles, RMPermissionModel.READ_RECORDS, true); } } // Make sure all the containers do not inherit permissions ResultSet rs = searchService.query(SPACES_STORE, SearchService.LANGUAGE_FTS_ALFRESCO, "TYPE:\"rma:recordsManagementContainer\""); try { logger.info("Bootstraping " + rs.length() + " record containers ..."); for (NodeRef container : rs.getNodeRefs()) { String containerName = (String) nodeService.getProperty(container, ContentModel.PROP_NAME); // Set permissions if (permissionService.getInheritParentPermissions(container)) { logger.info("Updating permissions for record container: " + containerName); permissionService.setInheritParentPermissions(container, false); } } } finally { rs.close(); } // fix up the test dataset to fire initial events for // disposition // schedules rs = searchService.query(SPACES_STORE, SearchService.LANGUAGE_FTS_ALFRESCO, "TYPE:\"rma:recordFolder\""); try { logger.info("Bootstraping " + rs.length() + " record folders ..."); for (NodeRef recordFolder : rs.getNodeRefs()) { String folderName = (String) nodeService.getProperty(recordFolder, ContentModel.PROP_NAME); // Set permissions if (permissionService.getInheritParentPermissions(recordFolder)) { logger.info("Updating permissions for record folder: " + folderName); permissionService.setInheritParentPermissions(recordFolder, false); } if (!nodeService.hasAspect(recordFolder, ASPECT_DISPOSITION_LIFECYCLE)) { // See if the folder has a disposition schedule that // needs // to be applied DispositionSchedule ds = dispositionService.getDispositionSchedule(recordFolder); if (ds != null) { // Fire action to "set-up" the folder correctly logger.info("Setting up bootstraped record folder: " + folderName); recordFolderService.setupRecordFolder(recordFolder); } } // fixup the search behaviour aspect for the record // folder logger.info("Setting up search aspect for record folder: " + folderName); recordsManagementSearchBehaviour.fixupSearchAspect(recordFolder); } } finally { rs.close(); } return null; } }; AuthenticationUtil.runAs(runAsWork, AuthenticationUtil.getAdminUserName()); } /** * Helper method for setting the id of the imported data set into the file * plan's aspect * * @param dataSetId The id of the imported data set * @param filePlan The file plan into which the data set has been imported */ @SuppressWarnings("unchecked") private void setDataSetIdIntoFilePlan(String dataSetId, NodeRef filePlan) { ArrayList<String> loadedDataSetIds; Serializable dataSetIds = nodeService.getProperty(filePlan, PROP_LOADED_DATA_SET_IDS); // Check if any data set has been imported if (dataSetIds == null) { Map<QName, Serializable> aspectProperties = new HashMap<QName, Serializable>(1); aspectProperties.put(PROP_LOADED_DATA_SET_IDS, (Serializable) new ArrayList<String>()); nodeService.addAspect(filePlan, ASPECT_LOADED_DATA_SET_ID, aspectProperties); loadedDataSetIds = (ArrayList<String>) nodeService.getProperty(filePlan, PROP_LOADED_DATA_SET_IDS); } else { loadedDataSetIds = (ArrayList<String>) dataSetIds; } // Add the new loaded data set id loadedDataSetIds.add(dataSetId); Map<QName, Serializable> aspectProperties = new HashMap<QName, Serializable>(1); aspectProperties.put(PROP_LOADED_DATA_SET_IDS, (Serializable) loadedDataSetIds); nodeService.addAspect(filePlan, ASPECT_LOADED_DATA_SET_ID, aspectProperties); } }