diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/log4j.properties b/rm-server/config/alfresco/module/org_alfresco_module_rm/log4j.properties index b5bdc8f089..41e9ee8c9f 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/log4j.properties +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/log4j.properties @@ -18,4 +18,7 @@ log4j.logger.org.alfresco.module.org_alfresco_module_rm.patch=info #log4j.logger.org.alfresco.module.org_alfresco_module_rm.record.RecordServiceImpl=debug #log4j.logger.org.springframework.extensions.webscripts.ScriptDebugger=on -#log4j.logger.org.alfresco.module.org_alfresco_module_rm.audit.RecordsManagementAuditService=debug \ No newline at end of file +#log4j.logger.org.alfresco.module.org_alfresco_module_rm.audit.RecordsManagementAuditService=debug + +log4j.logger.org.alfresco.repo.policy.annotation.AnnotatedBehaviourPostProcessor=debug +log4j.logger.org.alfresco.module.org_alfresco_module_rm.behaviour.BaseBehaviourBean=debug \ No newline at end of file diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/module-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/module-context.xml index fea08301e7..a94aa0165a 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/module-context.xml +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/module-context.xml @@ -212,7 +212,10 @@ - - + + + + + diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-model-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-model-context.xml index 9dd6446d8d..c16b30db73 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-model-context.xml +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-model-context.xml @@ -50,14 +50,6 @@ - - - - - - @@ -94,6 +86,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -629,12 +630,11 @@ + depends-on="org_alfresco_module_rm_dictionaryBootstrap"> - @@ -685,6 +685,8 @@ diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementServiceImpl.java index f662c3a305..5059a3d9ed 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementServiceImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementServiceImpl.java @@ -28,12 +28,10 @@ import java.util.Set; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; -import org.alfresco.model.RenditionModel; import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService; import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanComponentKind; 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.RecordsManagementCustomModel; import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; import org.alfresco.module.org_alfresco_module_rm.record.RecordService; import org.alfresco.module.org_alfresco_module_rm.recordfolder.RecordFolderService; @@ -45,7 +43,6 @@ import org.alfresco.repo.policy.JavaBehaviour; import org.alfresco.repo.policy.PolicyComponent; 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.cmr.repository.ScriptService; import org.alfresco.service.cmr.repository.StoreRef; @@ -61,12 +58,11 @@ import org.springframework.extensions.surf.util.I18NUtil; */ public class RecordsManagementServiceImpl extends ServiceBaseImpl implements RecordsManagementService, - RecordsManagementModel, - RecordsManagementPolicies.OnCreateReference, - RecordsManagementPolicies.OnRemoveReference + RecordsManagementModel //, + //RecordsManagementPolicies.OnCreateReference, + //RecordsManagementPolicies.OnRemoveReference { /** I18N */ - private final static String MSG_ERROR_ADD_CONTENT_CONTAINER = "rm.service.error-add-content-container"; private final static String MSG_UPDATE_DISP_ACT_DEF = "rm.service.update-disposition-action-def"; private final static String MSG_SET_ID = "rm.service.set-id"; @@ -81,7 +77,7 @@ public class RecordsManagementServiceImpl extends ServiceBaseImpl /** Policy component */ private PolicyComponent policyComponent; - /** Well-known location of the scripts folder. */ + ///** Well-known location of the scripts folder. */ private NodeRef scriptsFolderNodeRef = new NodeRef("workspace", "SpacesStore", "rm_behavior_scripts"); /** Java behaviour */ @@ -196,26 +192,6 @@ public class RecordsManagementServiceImpl extends ServiceBaseImpl */ public void init() { - // Register the association behaviours - policyComponent.bindAssociationBehaviour( - QName.createQName(NamespaceService.ALFRESCO_URI, "onCreateChildAssociation"), - TYPE_FILE_PLAN, - ContentModel.ASSOC_CONTAINS, - new JavaBehaviour(this, "onAddContentToContainer", NotificationFrequency.EVERY_EVENT)); - policyComponent.bindAssociationBehaviour( - QName.createQName(NamespaceService.ALFRESCO_URI, "onCreateChildAssociation"), - TYPE_RECORD_CATEGORY, - ContentModel.ASSOC_CONTAINS, - new JavaBehaviour(this, "onAddContentToContainer", NotificationFrequency.EVERY_EVENT)); - - // TODO move this into the record service - policyComponent.bindAssociationBehaviour( - QName.createQName(NamespaceService.ALFRESCO_URI, "onCreateChildAssociation"), - ASPECT_RECORD, - RenditionModel.ASSOC_RENDITION, - new JavaBehaviour(this, "onAddRecordThumbnail", NotificationFrequency.TRANSACTION_COMMIT) - ); - // Register script execution behaviour on RM property update. policyComponent.bindClassBehaviour(QName.createQName(NamespaceService.ALFRESCO_URI, "onUpdateProperties"), ASPECT_FILE_PLAN_COMPONENT, @@ -227,81 +203,12 @@ public class RecordsManagementServiceImpl extends ServiceBaseImpl TYPE_DISPOSITION_ACTION_DEFINITION, onChangeToDispositionActionDefinition); - // Reference behaviours - policyComponent.bindClassBehaviour(RecordsManagementPolicies.ON_CREATE_REFERENCE, - ASPECT_RECORD, - new JavaBehaviour(this, "onCreateReference", NotificationFrequency.TRANSACTION_COMMIT)); - - policyComponent.bindClassBehaviour(RecordsManagementPolicies.ON_REMOVE_REFERENCE, - ASPECT_RECORD, - new JavaBehaviour(this, "onRemoveReference", NotificationFrequency.TRANSACTION_COMMIT)); - // Identifier behaviours policyComponent.bindClassBehaviour(QName.createQName(NamespaceService.ALFRESCO_URI, "onUpdateProperties"), ASPECT_RECORD_COMPONENT_ID, new JavaBehaviour(this, "onIdentifierUpdate", NotificationFrequency.TRANSACTION_COMMIT)); } - /** - * On add content to container - * - * Prevents content nodes being added to record series and record category folders - * by imap, cifs etc. - * - * @param childAssocRef - * @param bNew - */ - public void onAddContentToContainer(ChildAssociationRef childAssocRef, boolean bNew) - { - NodeRef parent = childAssocRef.getParentRef(); - NodeRef nodeRef = childAssocRef.getChildRef(); - if (instanceOf(nodeRef, ContentModel.TYPE_CONTENT) == true) - { - throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_ERROR_ADD_CONTENT_CONTAINER)); - } - if (isFilePlan(parent) == true && isRecordFolder(nodeRef) == true) - { - throw new AlfrescoRuntimeException("Operation failed, because you can not place a record folder in the root of the file plan."); - } - } - - /** - * Make sure the thumbnails of records are marked as file plan components as are therefore subject to the same - * permission restrictions. - * - * @param childAssocRef - * @param bNew - */ - public void onAddRecordThumbnail(final ChildAssociationRef childAssocRef, final boolean bNew) - { - AuthenticationUtil.runAsSystem(new RunAsWork() - { - @Override - public Void doWork() throws Exception - { - NodeRef thumbnail = childAssocRef.getChildRef(); - - if (nodeService.exists(thumbnail) == true) - { - // apply file plan component aspect to thumbnail - nodeService.addAspect(thumbnail, ASPECT_FILE_PLAN_COMPONENT, null); - - // manage any extended readers - NodeRef parent = childAssocRef.getParentRef(); - ExtendedSecurityService extendedSecurityService = getExtendedSecurityService(); - Set readers = extendedSecurityService.getExtendedReaders(parent); - Set writers = extendedSecurityService.getExtendedWriters(parent); - if (readers != null && readers.size() != 0) - { - extendedSecurityService.addExtendedSecurity(thumbnail, readers, writers, false); - } - } - - return null; - } - }); - } - /** * Called after a DispositionActionDefinition property has been updated. */ @@ -483,67 +390,6 @@ public class RecordsManagementServiceImpl extends ServiceBaseImpl } } - /** - * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies.OnCreateReference#onCreateReference(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) - */ - public void onCreateReference(NodeRef fromNodeRef, NodeRef toNodeRef, QName reference) - { - // Deal with versioned records - if (reference.equals(QName.createQName(RecordsManagementCustomModel.RM_CUSTOM_URI, "versions")) == true) - { - // Apply the versioned aspect to the from node - this.nodeService.addAspect(fromNodeRef, ASPECT_VERSIONED_RECORD, null); - } - - // Execute script if for the reference event - executeReferenceScript("onCreate", reference, fromNodeRef, toNodeRef); - } - - /** - * Executes a reference script if present - * - * @param policy - * @param reference - * @param from - * @param to - */ - private void executeReferenceScript(String policy, QName reference, NodeRef from, NodeRef to) - { - String referenceId = reference.getLocalName(); - - // This is the filename pattern which is assumed. - // e.g. a script file onCreate_superceded.js for the creation of a superseded reference - String expectedScriptName = policy + "_" + referenceId + ".js"; - - NodeRef scriptNodeRef = nodeService.getChildByName(scriptsFolderNodeRef, ContentModel.ASSOC_CONTAINS, expectedScriptName); - if (scriptNodeRef != null) - { - Map objectModel = new HashMap(1); - objectModel.put("node", from); - objectModel.put("toNode", to); - objectModel.put("policy", policy); - objectModel.put("reference", referenceId); - - getScriptService().executeScript(scriptNodeRef, null, objectModel); - } - } - - /** - * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies.OnRemoveReference#onRemoveReference(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) - */ - public void onRemoveReference(NodeRef fromNodeRef, NodeRef toNodeRef, QName reference) - { - // Deal with versioned records - if (reference.equals(QName.createQName(RecordsManagementCustomModel.RM_CUSTOM_URI, "versions")) == true) - { - // Apply the versioned aspect to the from node - this.nodeService.removeAspect(fromNodeRef, ASPECT_VERSIONED_RECORD); - } - - // Execute script if for the reference event - executeReferenceScript("onRemove", reference, fromNodeRef, toNodeRef); - } - /** * @deprecated As of 2.1, see {@link FilePlanService#isFilePlanComponent(NodeRef)} */ diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/CustomisableTypesBootstrap.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/CustomisableTypesBootstrap.java index 2622664234..b86ba6a5bf 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/CustomisableTypesBootstrap.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/CustomisableTypesBootstrap.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2011 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -26,8 +26,10 @@ import org.alfresco.service.namespace.QName; /** * Bootstrap bean that indicates that the specified types or aspects are + * customizable. * * @author Roy Wetherall + * @since 2.0 */ public class CustomisableTypesBootstrap { diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/behaviour/RecordCopyBehaviours.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/behaviour/RecordCopyBehaviours.java index ee2c79418f..11210a51b3 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/behaviour/RecordCopyBehaviours.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/behaviour/RecordCopyBehaviours.java @@ -143,10 +143,6 @@ public class RecordCopyBehaviours implements RecordsManagementModel QName.createQName(NamespaceService.ALFRESCO_URI, "getCopyCallback"), RecordsManagementModel.TYPE_RECORD_FOLDER, new JavaBehaviour(this, "onCopyRecordFolderNode")); - this.policyComponent.bindClassBehaviour( - QName.createQName(NamespaceService.ALFRESCO_URI, "getCopyCallback"), - RecordsManagementModel.TYPE_RECORD_CATEGORY, - new JavaBehaviour(this, "onCopyRecordCategoryNode")); } /** @@ -330,36 +326,6 @@ public class RecordCopyBehaviours implements RecordsManagementModel }; } - /** - * Record Category Copy Behaviour - * - *
  • Do not allow copy of record category into a record folder
  • - * - * @param classRef - * @param copyDetails - * @return - */ - public CopyBehaviourCallback onCopyRecordCategoryNode(final QName classRef, final CopyDetails copyDetails) - { - return new DefaultCopyBehaviourCallback() - { - final NodeService nodeService = rmServiceRegistry.getNodeService(); - - /** - * If the targets parent is a Record Folder -- Do Not Allow Copy - * - * @param classQName - * @param copyDetails - * @return boolean - */ - @Override - public boolean getMustCopy(QName classQName, CopyDetails copyDetails) - { - return nodeService.getType(copyDetails.getTargetParentNodeRef()).equals(TYPE_RECORD_FOLDER) ? false : true; - } - }; - } - /** * Removes unwanted aspects * diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/BaseBehaviourBean.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/BaseBehaviourBean.java new file mode 100644 index 0000000000..dca196d4ff --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/BaseBehaviourBean.java @@ -0,0 +1,80 @@ +/** + * + */ +package org.alfresco.module.org_alfresco_module_rm.model.rma; + +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.repo.policy.BehaviourFilter; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.ParameterCheck; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Convenient base class for behaviour beans. + * + * @author Roy Wetherall + * @since 2.2 + */ +public abstract class BaseBehaviourBean implements RecordsManagementModel +{ + /** Logger */ + protected static Log logger = LogFactory.getLog(BaseBehaviourBean.class); + + /** node service */ + protected NodeService nodeService; + + /** dictionary service */ + protected DictionaryService dictionaryService; + + /** behaviour filter */ + protected BehaviourFilter behaviourFilter; + + /** + * @param nodeService node service + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * @param dictionaryService dictionary service + */ + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + /** + * @param behaviourFilter behaviour filter + */ + public void setBehaviourFilter(BehaviourFilter behaviourFilter) + { + this.behaviourFilter = behaviourFilter; + } + + /** + * Utility method to safely and quickly determine if a node is a type (or sub-type) of the one specified. + * + * @param nodeRef node reference + * @param ofClassName class name to check + */ + protected boolean instanceOf(NodeRef nodeRef, QName ofClassName) + { + ParameterCheck.mandatory("nodeRef", nodeRef); + ParameterCheck.mandatory("ofClassName", ofClassName); + boolean result = false; + if (nodeService.exists(nodeRef) == true && + (ofClassName.equals(nodeService.getType(nodeRef)) == true || + dictionaryService.isSubClass(nodeService.getType(nodeRef), ofClassName) == true)) + { + result = true; + } + return result; + } + +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/FilePlanType.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/FilePlanType.java new file mode 100644 index 0000000000..64f379a3d9 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/FilePlanType.java @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2005-2011 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.model.rma; + +import org.alfresco.error.AlfrescoRuntimeException; +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; +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.policy.Behaviour.NotificationFrequency; +import org.alfresco.repo.policy.annotation.Behaviour; +import org.alfresco.repo.policy.annotation.BehaviourBean; +import org.alfresco.repo.policy.annotation.BehaviourKind; +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; + +/** + * rma:filePlan behaviour bean + * + * @author Roy Wetherall + * @since 2.2 + */ +@BehaviourBean +( + defaultType = "rma:filePlan" +) +public class FilePlanType extends BaseBehaviourBean + implements NodeServicePolicies.OnCreateChildAssociationPolicy, + NodeServicePolicies.OnCreateNodePolicy, + NodeServicePolicies.OnDeleteNodePolicy +{ + /** file plan service */ + protected FilePlanService filePlanService; + + /** record folder service */ + protected RecordFolderService recordFolderService; + + /** identifier service */ + protected IdentifierService identifierService; + + /** file plan role service */ + protected FilePlanRoleService filePlanRoleService; + + /** + * @param filePlanService file plan service + */ + public void setFilePlanService(FilePlanService filePlanService) + { + this.filePlanService = filePlanService; + } + + /** + * @param recordFolderService record folder service + */ + public void setRecordFolderService(RecordFolderService recordFolderService) + { + this.recordFolderService = recordFolderService; + } + + /** + * @param identifierService identifier service + */ + public void setIdentifierService(IdentifierService identifierService) + { + this.identifierService = identifierService; + } + + /** + * @param filePlanRoleService file plan role service + */ + public void setFilePlanRoleService(FilePlanRoleService filePlanRoleService) + { + this.filePlanRoleService = filePlanRoleService; + } + + /** + * @see org.alfresco.repo.node.NodeServicePolicies.OnCreateChildAssociationPolicy#onCreateChildAssociation(org.alfresco.service.cmr.repository.ChildAssociationRef, boolean) + */ + @Behaviour + ( + kind = BehaviourKind.ASSOCIATION + ) + @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) == true) + { + 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 (filePlanService.isFilePlan(parent) == true && recordFolderService.isRecordFolder(nodeRef) == true) + { + throw new AlfrescoRuntimeException("Operation failed, because you can not place a record folder in the root of the file plan."); + } + + } + + /** + * @see org.alfresco.repo.node.NodeServicePolicies.OnCreateNodePolicy#onCreateNode(org.alfresco.service.cmr.repository.ChildAssociationRef) + */ + @Behaviour + ( + kind = BehaviourKind.CLASS, + notificationFrequency = NotificationFrequency.TRANSACTION_COMMIT + ) + @Override + public void onCreateNode(final ChildAssociationRef childAssocRef) + { + final NodeRef filePlan = childAssocRef.getChildRef(); + + AuthenticationUtil.runAsSystem(new RunAsWork() + { + public Object doWork() throws Exception + { + if (nodeService.hasAspect(filePlan, ASPECT_FILE_PLAN_COMPONENT) == true && + nodeService.getProperty(filePlan, PROP_IDENTIFIER) == null) + { + String id = identifierService.generateIdentifier(filePlan); + nodeService.setProperty(filePlan, RecordsManagementModel.PROP_IDENTIFIER, id); + } + + return null; + } + }); + + // setup the file plan roles + filePlanRoleService.setupFilePlanRoles(filePlan); + } + + /** + * @see org.alfresco.repo.node.NodeServicePolicies.OnDeleteNodePolicy#onDeleteNode(org.alfresco.service.cmr.repository.ChildAssociationRef, boolean) + */ + @Behaviour + ( + kind = BehaviourKind.CLASS, + notificationFrequency = NotificationFrequency.TRANSACTION_COMMIT + ) + @Override + public void onDeleteNode(ChildAssociationRef childAssocRef, boolean archived) + { + // tear down the file plan roles + filePlanRoleService.tearDownFilePlanRoles(childAssocRef.getChildRef()); + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/RecordAspect.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/RecordAspect.java new file mode 100644 index 0000000000..49ec89b950 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/RecordAspect.java @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2005-2011 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.model.rma; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementCustomModel; +import org.alfresco.module.org_alfresco_module_rm.security.ExtendedSecurityService; +import org.alfresco.repo.node.NodeServicePolicies; +import org.alfresco.repo.policy.Behaviour.NotificationFrequency; +import org.alfresco.repo.policy.annotation.Behaviour; +import org.alfresco.repo.policy.annotation.BehaviourBean; +import org.alfresco.repo.policy.annotation.BehaviourKind; +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.cmr.repository.ScriptService; +import org.alfresco.service.namespace.QName; + +/** + * rma:record behaviour bean + * + * @author Roy Wetherall + * @since 2.2 + */ +@BehaviourBean +( + defaultType = "rma:record" +) +public class RecordAspect extends BaseBehaviourBean + implements NodeServicePolicies.OnCreateChildAssociationPolicy, + RecordsManagementPolicies.OnCreateReference, + RecordsManagementPolicies.OnRemoveReference +{ + /** Well-known location of the scripts folder. */ + // TODO make configurable + private NodeRef scriptsFolderNodeRef = new NodeRef("workspace", "SpacesStore", "rm_behavior_scripts"); + + /** extended security service */ + protected ExtendedSecurityService extendedSecurityService; + + /** script service */ + protected ScriptService scriptService; + + /** + * @param extendedSecurityService extended security service + */ + public void setExtendedSecurityService(ExtendedSecurityService extendedSecurityService) + { + this.extendedSecurityService = extendedSecurityService; + } + + /** + * @param scriptService script service + */ + public void setScriptService(ScriptService scriptService) + { + this.scriptService = scriptService; + } + + /** + * Behaviour to ensure renditions have the appropriate extended security. + * + * @see org.alfresco.repo.node.NodeServicePolicies.OnCreateChildAssociationPolicy#onCreateChildAssociation(org.alfresco.service.cmr.repository.ChildAssociationRef, boolean) + */ + @Override + @Behaviour + ( + kind = BehaviourKind.ASSOCIATION, + assocType = "rn:rendition", + notificationFrequency = NotificationFrequency.TRANSACTION_COMMIT + ) + public void onCreateChildAssociation(final ChildAssociationRef childAssocRef, boolean bNew) + { + AuthenticationUtil.runAsSystem(new RunAsWork() + { + @Override + public Void doWork() throws Exception + { + NodeRef thumbnail = childAssocRef.getChildRef(); + + if (nodeService.exists(thumbnail) == true) + { + // apply file plan component aspect to thumbnail + nodeService.addAspect(thumbnail, ASPECT_FILE_PLAN_COMPONENT, null); + + // manage any extended readers + NodeRef parent = childAssocRef.getParentRef(); + Set readers = extendedSecurityService.getExtendedReaders(parent); + Set writers = extendedSecurityService.getExtendedWriters(parent); + if (readers != null && readers.size() != 0) + { + extendedSecurityService.addExtendedSecurity(thumbnail, readers, writers, false); + } + } + + return null; + } + }); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies.OnCreateReference#onCreateReference(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) + */ + @Override + @Behaviour + ( + kind = BehaviourKind.CLASS, + notificationFrequency = NotificationFrequency.TRANSACTION_COMMIT + ) + public void onCreateReference(NodeRef fromNodeRef, NodeRef toNodeRef, QName reference) + { + // Deal with versioned records + if (reference.equals(QName.createQName(RecordsManagementCustomModel.RM_CUSTOM_URI, "versions")) == true) + { + // Apply the versioned aspect to the from node + nodeService.addAspect(fromNodeRef, ASPECT_VERSIONED_RECORD, null); + } + + // Execute script if for the reference event + executeReferenceScript("onCreate", reference, fromNodeRef, toNodeRef); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies.OnRemoveReference#onRemoveReference(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) + */ + @Override + @Behaviour + ( + kind = BehaviourKind.CLASS, + notificationFrequency = NotificationFrequency.TRANSACTION_COMMIT + ) + public void onRemoveReference(NodeRef fromNodeRef, NodeRef toNodeRef, QName reference) + { + // Deal with versioned records + if (reference.equals(QName.createQName(RecordsManagementCustomModel.RM_CUSTOM_URI, "versions")) == true) + { + // Apply the versioned aspect to the from node + nodeService.removeAspect(fromNodeRef, ASPECT_VERSIONED_RECORD); + } + + // Execute script if for the reference event + executeReferenceScript("onRemove", reference, fromNodeRef, toNodeRef); + } + + /** + * Executes a reference script if present + * + * @param policy + * @param reference + * @param from + * @param to + */ + private void executeReferenceScript(String policy, QName reference, NodeRef from, NodeRef to) + { + String referenceId = reference.getLocalName(); + + // This is the filename pattern which is assumed. + // e.g. a script file onCreate_superceded.js for the creation of a superseded reference + String expectedScriptName = policy + "_" + referenceId + ".js"; + + NodeRef scriptNodeRef = nodeService.getChildByName(scriptsFolderNodeRef, ContentModel.ASSOC_CONTAINS, expectedScriptName); + if (scriptNodeRef != null) + { + Map objectModel = new HashMap(1); + objectModel.put("node", from); + objectModel.put("toNode", to); + objectModel.put("policy", policy); + objectModel.put("reference", referenceId); + + scriptService.executeScript(scriptNodeRef, null, objectModel); + } + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/RecordCategoryType.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/RecordCategoryType.java new file mode 100644 index 0000000000..433387207e --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/RecordCategoryType.java @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2005-2011 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.model.rma; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.security.FilePlanPermissionService; +import org.alfresco.module.org_alfresco_module_rm.vital.VitalRecordService; +import org.alfresco.repo.copy.CopyBehaviourCallback; +import org.alfresco.repo.copy.CopyDetails; +import org.alfresco.repo.copy.DefaultCopyBehaviourCallback; +import org.alfresco.repo.node.NodeServicePolicies; +import org.alfresco.repo.policy.Behaviour.NotificationFrequency; +import org.alfresco.repo.policy.annotation.Behaviour; +import org.alfresco.repo.policy.annotation.BehaviourBean; +import org.alfresco.repo.policy.annotation.BehaviourKind; +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:recordCategory behaviour bean + * + * @author Roy Wetherall + * @since 2.2 + */ +@BehaviourBean +( + defaultType = "rma:recordCategory" +) +public class RecordCategoryType extends BaseBehaviourBean + implements NodeServicePolicies.OnCreateChildAssociationPolicy, + NodeServicePolicies.OnCreateNodePolicy +{ + /** vital record service */ + protected VitalRecordService vitalRecordService; + + /** file plan permission service */ + protected FilePlanPermissionService filePlanPermissionService; + + /** + * @param vitalRecordService vital record service + */ + public void setVitalRecordService(VitalRecordService vitalRecordService) + { + this.vitalRecordService = vitalRecordService; + } + + /** + * @param filePlanPermissionService file plan permission service + */ + public void setFilePlanPermissionService(FilePlanPermissionService filePlanPermissionService) + { + this.filePlanPermissionService = filePlanPermissionService; + } + + /** + * On every event + * + * @see org.alfresco.repo.node.NodeServicePolicies.OnCreateChildAssociationPolicy#onCreateChildAssociation(org.alfresco.service.cmr.repository.ChildAssociationRef, boolean) + */ + @Override + @Behaviour + ( + kind = BehaviourKind.ASSOCIATION + ) + 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) == true) + { + throw new AlfrescoRuntimeException("Operation failed, because you can't place content directly into a record category."); + } + } + + /** + * On transaction commit + * + * @see org.alfresco.repo.node.NodeServicePolicies.OnCreateChildAssociationPolicy#onCreateChildAssociation(org.alfresco.service.cmr.repository.ChildAssociationRef, boolean) + */ + @Behaviour + ( + kind = BehaviourKind.ASSOCIATION, + policy = "alf:onCreateChildAssociation", + notificationFrequency = NotificationFrequency.TRANSACTION_COMMIT + ) + public void onCreateChildAssociationOnCommit(ChildAssociationRef childAssocRef, boolean bNew) + { + final NodeRef recordCategory = childAssocRef.getChildRef(); + + behaviourFilter.disableBehaviour(); + try + { + AuthenticationUtil.runAsSystem(new RunAsWork() + { + @Override + public Void doWork() throws Exception + { + // setup vital record definition + vitalRecordService.setupVitalRecordDefinition(recordCategory); + + return null; + } + }); + } + finally + { + behaviourFilter.enableBehaviour(); + } + } + + /** + * @see org.alfresco.repo.node.NodeServicePolicies.OnCreateNodePolicy#onCreateNode(org.alfresco.service.cmr.repository.ChildAssociationRef) + */ + @Override + @Behaviour + ( + kind = BehaviourKind.CLASS, + notificationFrequency = NotificationFrequency.TRANSACTION_COMMIT + ) + public void onCreateNode(ChildAssociationRef childAssocRef) + { + if (logger.isDebugEnabled() == true) + { + logger.debug("rma:recordCategory|alf:onCreateNode|this.onCreateNode()|TRANSATION_COMMIT"); + } + + // setup record category permissions + filePlanPermissionService.setupRecordCategoryPermissions(childAssocRef.getChildRef()); + } + + /** + * Copy callback for record category + */ + @Behaviour + ( + kind = BehaviourKind.CLASS, + policy = "alf:getCopyCallback" + ) + public CopyBehaviourCallback onCopyRecordCategory(final QName classRef, final CopyDetails copyDetails) + { + return new DefaultCopyBehaviourCallback() + { + /** + * If the targets parent is a Record Folder -- Do Not Allow Copy + * + * @param classQName + * @param copyDetails + * @return boolean + */ + @Override + public boolean getMustCopy(QName classQName, CopyDetails copyDetails) + { + return nodeService.getType(copyDetails.getTargetParentNodeRef()).equals(TYPE_RECORD_FOLDER) ? false : true; + } + }; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/RecordsManagementContainerType.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/RecordsManagementContainerType.java new file mode 100644 index 0000000000..d37400cf78 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/RecordsManagementContainerType.java @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2005-2011 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.model.rma; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.identifier.IdentifierService; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.repo.node.NodeServicePolicies; +import org.alfresco.repo.policy.Behaviour.NotificationFrequency; +import org.alfresco.repo.policy.annotation.Behaviour; +import org.alfresco.repo.policy.annotation.BehaviourBean; +import org.alfresco.repo.policy.annotation.BehaviourKind; +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:recordsManagementContainer behaviour bean. + * + * @author Roy Wetherall + * @since 2.2 + */ +@BehaviourBean +( + defaultType = "rma:recordsManagementContainer" +) +public class RecordsManagementContainerType extends BaseBehaviourBean + implements NodeServicePolicies.OnCreateChildAssociationPolicy +{ + /** identifier service */ + protected IdentifierService identifierService; + + /** + * @param identifierService identifier service + */ + public void setIdentifierService(IdentifierService identifierService) + { + this.identifierService = identifierService; + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.model.BaseTypeBehaviour#onCreateChildAssociation(org.alfresco.service.cmr.repository.ChildAssociationRef, boolean) + */ + @Behaviour + ( + kind = BehaviourKind.ASSOCIATION, + notificationFrequency = NotificationFrequency.TRANSACTION_COMMIT + ) + public void onCreateChildAssociation(final ChildAssociationRef childAssocRef, boolean isNewNode) + { + AuthenticationUtil.runAsSystem(new RunAsWork() + { + @Override + public Void doWork() throws Exception + { + // Get the elements of the created association + final NodeRef child = childAssocRef.getChildRef(); + if (nodeService.exists(child) == true) + { + QName childType = nodeService.getType(child); + + // We only care about "folder" or sub-types + if (dictionaryService.isSubClass(childType, ContentModel.TYPE_FOLDER) == true) + { + if (dictionaryService.isSubClass(childType, ContentModel.TYPE_SYSTEM_FOLDER) == true) + { + // this is a rule container, make sure it is an file plan component + nodeService.addAspect(child, ASPECT_FILE_PLAN_COMPONENT, null); + } + else + { + // We need to automatically cast the created folder to RM type if it is a plain folder + // This occurs if the RM folder has been created via IMap, WebDav, etc + if (nodeService.hasAspect(child, ASPECT_FILE_PLAN_COMPONENT) == false) + { + // TODO it may not always be a record folder ... perhaps if the current user is a admin it would be a record category?? + + // Assume any created folder is a rma:recordFolder + nodeService.setType(child, TYPE_RECORD_FOLDER); + } + + // Catch all to generate the rm id (assuming it doesn't already have one!) + setIdenifierProperty(child); + } + } + } + + return null; + } + }); + + } + + /** + * + * @param nodeRef + */ + protected void setIdenifierProperty(final NodeRef nodeRef) + { + AuthenticationUtil.runAsSystem(new RunAsWork() + { + public Object doWork() throws Exception + { + if (nodeService.hasAspect(nodeRef, ASPECT_FILE_PLAN_COMPONENT) == true && + nodeService.getProperty(nodeRef, PROP_IDENTIFIER) == null) + { + String id = identifierService.generateIdentifier(nodeRef); + nodeService.setProperty(nodeRef, RecordsManagementModel.PROP_IDENTIFIER, id); + } + return null; + } + }); + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/recordfolder/RecordFolderServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/recordfolder/RecordFolderServiceImpl.java index 1dd8668dd8..cdf30420fc 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/recordfolder/RecordFolderServiceImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/recordfolder/RecordFolderServiceImpl.java @@ -84,6 +84,7 @@ public class RecordFolderServiceImpl extends ServiceBaseImpl = new JavaBehaviour(this, "onCreateChildAssociation", NotificationFrequency.FIRST_EVENT); + private JavaBehaviour onCreateChildAssociationInRecordFolderFolder = new JavaBehaviour(this, "onCreateChildAssociationInRecordFolder", @@ -126,6 +127,7 @@ public class RecordFolderServiceImpl extends ServiceBaseImpl */ public void init() { + policyComponent.bindAssociationBehaviour( NodeServicePolicies.OnCreateChildAssociationPolicy.QNAME, TYPE_RECORD_CATEGORY, diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/role/FilePlanRoleService.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/role/FilePlanRoleService.java index 61e6f2b498..d1a29e0db4 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/role/FilePlanRoleService.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/role/FilePlanRoleService.java @@ -47,7 +47,21 @@ public interface FilePlanRoleService ROLE_EXTENDED_READERS, ROLE_EXTENDED_WRITERS ); + + /** + * Sets up the roles on a new file plan. + * + * @param filePlan file plan + */ + void setupFilePlanRoles(NodeRef filePlan); + /** + * Tears down the roles on a file plan. + * + * @param filePlan file plan + */ + void tearDownFilePlanRoles(NodeRef filePlan); + /** * Returns the name of the container group for all roles of a specified file * plan. diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/role/FilePlanRoleServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/role/FilePlanRoleServiceImpl.java index 908500eabd..95bddf438b 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/role/FilePlanRoleServiceImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/role/FilePlanRoleServiceImpl.java @@ -42,13 +42,8 @@ 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.ExtendedWriterDynamicAuthority; import org.alfresco.module.org_alfresco_module_rm.security.FilePlanAuthenticationService; -import org.alfresco.repo.node.NodeServicePolicies; -import org.alfresco.repo.policy.Behaviour.NotificationFrequency; -import org.alfresco.repo.policy.JavaBehaviour; -import org.alfresco.repo.policy.PolicyComponent; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authority.RMAuthority; -import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; @@ -95,9 +90,6 @@ public class FilePlanRoleServiceImpl implements FilePlanRoleService, /** Permission service */ private PermissionService permissionService; - /** Policy component */ - private PolicyComponent policyComponent; - /** File plan service */ private FilePlanService filePlanService; @@ -150,14 +142,6 @@ public class FilePlanRoleServiceImpl implements FilePlanRoleService, this.permissionService = permissionService; } - /** - * @param policyComponent policy component - */ - public void setPolicyComponent(PolicyComponent policyComponent) - { - this.policyComponent = policyComponent; - } - /** * @param nodeService node service */ @@ -208,39 +192,21 @@ public class FilePlanRoleServiceImpl implements FilePlanRoleService, } /** - * Initialisation method + * @see org.alfresco.module.org_alfresco_module_rm.role.FilePlanRoleService#initialiseFilePlan(org.alfresco.service.cmr.repository.NodeRef) */ - public void init() + @Override + public void setupFilePlanRoles(final NodeRef filePlan) { - policyComponent.bindClassBehaviour( - NodeServicePolicies.OnCreateNodePolicy.QNAME, - TYPE_FILE_PLAN, - new JavaBehaviour(this, "onCreateRootNode", NotificationFrequency.TRANSACTION_COMMIT)); - policyComponent.bindClassBehaviour( - NodeServicePolicies.OnDeleteNodePolicy.QNAME, - TYPE_FILE_PLAN, - new JavaBehaviour(this, "onDeleteRootNode", NotificationFrequency.TRANSACTION_COMMIT)); - } - - /** - * Create root node behaviour - * - * @param childAssocRef - */ - public void onCreateRootNode(ChildAssociationRef childAssocRef) - { - final NodeRef rmRootNode = childAssocRef.getChildRef(); - // Do not execute behaviour if this has been created in the archive store - if(rmRootNode.getStoreRef().equals(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE) == true) + if(filePlan.getStoreRef().equals(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE) == true) { // This is not the spaces store - probably the archive store return; } - if (nodeService.exists(rmRootNode) == true) + if (nodeService.exists(filePlan) == true) { - List systemContainers = AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork>() + List systemContainers = AuthenticationUtil.runAsSystem(new AuthenticationUtil.RunAsWork>() { public List doWork() { @@ -256,50 +222,44 @@ public class FilePlanRoleServiceImpl implements FilePlanRoleService, // Create "all" role group for root node String allRoles = authorityService.createAuthority( AuthorityType.GROUP, - getAllRolesGroupShortName(rmRootNode), + getAllRolesGroupShortName(filePlan), I18NUtil.getMessage(MSG_ALL_ROLES), new HashSet(Arrays.asList(RMAuthority.ZONE_APP_RM))); // Set the permissions - permissionService.setInheritParentPermissions(rmRootNode, false); - permissionService.setPermission(rmRootNode, allRoles, RMPermissionModel.READ_RECORDS, true); - permissionService.setPermission(rmRootNode, ExtendedReaderDynamicAuthority.EXTENDED_READER, RMPermissionModel.READ_RECORDS, true); - permissionService.setPermission(rmRootNode, ExtendedWriterDynamicAuthority.EXTENDED_WRITER, RMPermissionModel.FILING, true); + permissionService.setInheritParentPermissions(filePlan, false); + permissionService.setPermission(filePlan, allRoles, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(filePlan, ExtendedReaderDynamicAuthority.EXTENDED_READER, RMPermissionModel.READ_RECORDS, true); + permissionService.setPermission(filePlan, ExtendedWriterDynamicAuthority.EXTENDED_WRITER, RMPermissionModel.FILING, true); // Create the transfer and hold containers - systemContainers.add(filePlanService.createHoldContainer(rmRootNode)); - systemContainers.add(filePlanService.createTransferContainer(rmRootNode)); + systemContainers.add(filePlanService.createHoldContainer(filePlan)); + systemContainers.add(filePlanService.createTransferContainer(filePlan)); // Create the unfiled record container - systemContainers.add(filePlanService.createUnfiledContainer(rmRootNode)); + systemContainers.add(filePlanService.createUnfiledContainer(filePlan)); return systemContainers; } - }, AuthenticationUtil.getSystemUserName()); + }); // Bootstrap in the default set of roles for the newly created root node - bootstrapDefaultRoles(rmRootNode, systemContainers); + bootstrapDefaultRoles(filePlan, systemContainers); } } /** - * Delete root node behaviour - * - * @param childAssocRef + * @see org.alfresco.module.org_alfresco_module_rm.role.FilePlanRoleService#tearDownFilePlanRoles(org.alfresco.service.cmr.repository.NodeRef) */ - public void onDeleteRootNode(ChildAssociationRef childAssocRef, boolean isNodeArchived) + @Override + public void tearDownFilePlanRoles(final NodeRef filePlan) { - logger.debug("onDeleteRootNode called"); - - // get the deleted node - final NodeRef rmRootNode = childAssocRef.getChildRef(); - - AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + AuthenticationUtil.runAsSystem(new AuthenticationUtil.RunAsWork() { public Object doWork() { // cascade delete the 'all' roles group for the site - String allRolesGroup = authorityService.getName(AuthorityType.GROUP, getAllRolesGroupShortName(rmRootNode)); + String allRolesGroup = authorityService.getName(AuthorityType.GROUP, getAllRolesGroupShortName(filePlan)); Set groups = authorityService.getContainedAuthorities(AuthorityType.GROUP, allRolesGroup, true); for (String group : groups) { @@ -310,7 +270,7 @@ public class FilePlanRoleServiceImpl implements FilePlanRoleService, return null; } - }, AuthenticationUtil.getSystemUserName()); + }); } /** diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/security/FilePlanPermissionService.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/security/FilePlanPermissionService.java index 9dd37ed65b..12fdaa9382 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/security/FilePlanPermissionService.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/security/FilePlanPermissionService.java @@ -28,6 +28,12 @@ import org.alfresco.service.cmr.repository.NodeRef; */ public interface FilePlanPermissionService { + /** + * + * @param nodeRef + */ + void setupRecordCategoryPermissions(NodeRef recordCategory); + /** * Sets a permission on a file plan object. Assumes allow is true. Cascades permission down to record folder. * Cascades ReadRecord up to file plan. diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/security/FilePlanPermissionServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/security/FilePlanPermissionServiceImpl.java index a0c7b566b1..4b4b9a40cc 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/security/FilePlanPermissionServiceImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/security/FilePlanPermissionServiceImpl.java @@ -22,6 +22,7 @@ import java.util.HashSet; import java.util.List; 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.fileplan.FilePlanService; @@ -79,10 +80,6 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl */ public void init() { - policyComponent.bindClassBehaviour( - NodeServicePolicies.OnCreateNodePolicy.QNAME, - TYPE_RECORD_CATEGORY, - new JavaBehaviour(this, "onCreateRMContainer", NotificationFrequency.TRANSACTION_COMMIT)); policyComponent.bindClassBehaviour( NodeServicePolicies.OnCreateNodePolicy.QNAME, TYPE_RECORD_FOLDER, @@ -152,28 +149,33 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl { this.recordFolderService = recordFolderService; } - + /** - * @param childAssocRef + * @see org.alfresco.module.org_alfresco_module_rm.security.FilePlanPermissionService#setupRecordCategoryPermissions(org.alfresco.service.cmr.repository.NodeRef) */ - public void onCreateRMContainer(ChildAssociationRef childAssocRef) + @Override + public void setupRecordCategoryPermissions(final NodeRef recordCategory) { - final NodeRef recordCategory = childAssocRef.getChildRef(); - setUpPermissions(recordCategory); + ParameterCheck.mandatory("recordCategory", recordCategory); + + // assert that we have a record category in our hands + if (instanceOf(recordCategory, TYPE_RECORD_CATEGORY) == false) + { + throw new AlfrescoRuntimeException("Unable to setup record category permissions, because node is not a record category."); + } + + // init permissions + initPermissions(recordCategory); // Pull any permissions found on the parent (ie the record category) - final NodeRef parentNodeRef = childAssocRef.getParentRef(); + final NodeRef parentNodeRef = nodeService.getPrimaryParent(recordCategory).getParentRef(); if (parentNodeRef != null && nodeService.exists(parentNodeRef) == true) { - AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + AuthenticationUtil.runAsSystem(new AuthenticationUtil.RunAsWork() { public Object doWork() { - boolean fillingOnly = false; - if (filePlanService.isFilePlan(parentNodeRef) == true) - { - fillingOnly = true; - } + boolean fillingOnly = filePlanService.isFilePlan(parentNodeRef); // since this is not a root category, inherit from parent Set perms = permissionService.getAllSetPermissions(parentNodeRef); @@ -198,7 +200,7 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl return null; } - }, AuthenticationUtil.getSystemUserName()); + }); } } @@ -210,7 +212,7 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl final NodeRef folderNodeRef = childAssocRef.getChildRef(); // initialise the permissions - setUpPermissions(folderNodeRef); + initPermissions(folderNodeRef); // Pull any permissions found on the parent (ie the record category) final NodeRef catNodeRef = childAssocRef.getParentRef(); @@ -285,7 +287,7 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl NodeRef nodeRef = childAssocRef.getChildRef(); if (nodeService.exists(nodeRef) == true) { - setUpPermissions(nodeRef); + initPermissions(nodeRef); NodeRef parent = childAssocRef.getParentRef(); Set perms = permissionService.getAllSetPermissions(parent); @@ -324,7 +326,7 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl */ public void initialiseRecordPermissions(NodeRef record, NodeRef parent) { - setUpPermissions(record); + initPermissions(record); Set perms = permissionService.getAllSetPermissions(parent); for (AccessPermission perm : perms) @@ -402,10 +404,11 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl } /** - * - * @param nodeRef + * Initiliase the permissions for the given node. + * + * @param nodeRef node reference */ - public void setUpPermissions(final NodeRef nodeRef) + private void initPermissions(final NodeRef nodeRef) { if (nodeService.exists(nodeRef) == true) { diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/vital/VitalRecordService.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/vital/VitalRecordService.java index 3d1f4f5376..c5b88afb14 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/vital/VitalRecordService.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/vital/VitalRecordService.java @@ -33,8 +33,15 @@ public interface VitalRecordService static final Period PERIOD_NONE = new Period("none|0"); /** - * Gets the vital record definition details for the node. - * + * Setup the vital record definition for the given node. + * + * @param nodeRef node reference + */ + void setupVitalRecordDefinition(NodeRef nodeRef); + + /** + * Gets the vital record definition details for the node. + * * @param nodeRef node reference * @return VitalRecordDefinition vital record definition details */ diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/vital/VitalRecordServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/vital/VitalRecordServiceImpl.java index b8d640cc6b..30fe7c0a77 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/vital/VitalRecordServiceImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/vital/VitalRecordServiceImpl.java @@ -129,11 +129,6 @@ public class VitalRecordServiceImpl implements VitalRecordService, TYPE_RECORD_FOLDER, ContentModel.ASSOC_CONTAINS, onCreateChildAssociation); - policyComponent.bindAssociationBehaviour( - NodeServicePolicies.OnCreateChildAssociationPolicy.QNAME, - TYPE_RECORD_CATEGORY, - ContentModel.ASSOC_CONTAINS, - onCreateChildAssociation); } /** @@ -185,7 +180,7 @@ public class VitalRecordServiceImpl implements VitalRecordService, if (filePlanService.isRecordCategory(nodeRef) == true || recordFolderService.isRecordFolder(nodeRef) == true) { - inheritVitalRecordDefinition(nodeRef); + setupVitalRecordDefinition(nodeRef); } return null; @@ -202,11 +197,10 @@ public class VitalRecordServiceImpl implements VitalRecordService, } /** - * Helper method to set the inherited vital record definition details. - * - * @param nodeRef node reference + * @see org.alfresco.module.org_alfresco_module_rm.vital.VitalRecordService#setupVitalRecordDefinition(org.alfresco.service.cmr.repository.NodeRef) */ - private void inheritVitalRecordDefinition(NodeRef nodeRef) + @Override + public void setupVitalRecordDefinition(NodeRef nodeRef) { // get the current review period value Period currentReviewPeriod = (Period)nodeService.getProperty(nodeRef, PROP_REVIEW_PERIOD); diff --git a/rm-server/source/java/org/alfresco/repo/policy/annotation/AnnotatedBehaviourPostProcessor.java b/rm-server/source/java/org/alfresco/repo/policy/annotation/AnnotatedBehaviourPostProcessor.java new file mode 100644 index 0000000000..40760fcd18 --- /dev/null +++ b/rm-server/source/java/org/alfresco/repo/policy/annotation/AnnotatedBehaviourPostProcessor.java @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2005-2013 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.policy.annotation; + +import java.lang.reflect.Method; + +import org.alfresco.repo.policy.JavaBehaviour; +import org.alfresco.repo.policy.PolicyComponent; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.ParameterCheck; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanPostProcessor; + +/** + * Annotated behaviour bean post processor. + *

    + * Registers the annotated methods on behaviour beans with the policy component. + * + * @author Roy Wetherall + */ +public class AnnotatedBehaviourPostProcessor implements BeanPostProcessor +{ + /** logger */ + private static Log logger = LogFactory.getLog(AnnotatedBehaviourPostProcessor.class); + + /** policy component */ + private PolicyComponent policyComponent; + + /** namespace service */ + private NamespaceService namespaceService; + + /** + * @param policyComponent policy component + */ + public void setPolicyComponent(PolicyComponent policyComponent) + { + this.policyComponent = policyComponent; + } + + /** + * @param namespaceService namespace service + */ + public void setNamespaceService(NamespaceService namespaceService) + { + this.namespaceService = namespaceService; + } + + /** + * @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization(java.lang.Object, java.lang.String) + */ + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException + { + // register annotated behavior methods + registerBehaviours(bean, beanName); + + return bean; + } + + /** + * @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization(java.lang.Object, java.lang.String) + */ + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException + { + // do nothing + return bean; + } + + /** + * + * @param bean + * @param beanName + */ + private void registerBehaviours(Object bean, String beanName) + { + if (bean.getClass().isAnnotationPresent(BehaviourBean.class) == true) + { + BehaviourBean behaviourBean = bean.getClass().getAnnotation(BehaviourBean.class); + + if (logger.isDebugEnabled() == true) + { + logger.debug("Annotated behaviour post processing for " + beanName); + } + + Method[] methods = bean.getClass().getMethods(); + for (Method method : methods) + { + if (method.isAnnotationPresent(Behaviour.class) == true) + { + registerBehaviour(behaviourBean, bean, beanName, method); + } + } + } + } + + /** + * + * @param bean + * @param beanName + * @param method + * @param classBehaviour + */ + private void registerBehaviour(BehaviourBean behaviourBean, Object bean, String beanName, Method method) + { + Behaviour behaviour = method.getAnnotation(Behaviour.class); + QName policy = resolvePolicy(behaviour.policy(), method); + QName type = resolveType(behaviourBean, behaviour.type()); + + // assert that the policy and type have been set!! + ParameterCheck.mandatory("policy", policy); + ParameterCheck.mandatory("type", type); + + if (logger.isDebugEnabled() == true) + { + logger.debug(" ... registering " + behaviour.kind() + " behaviour for " + beanName + "." + method.getName() + + " for policy " + policy.toString() + + " and type " + type.toString()); + } + + JavaBehaviour javaBehaviour = new JavaBehaviour(bean, method.getName(), behaviour.notificationFrequency()); + + if (BehaviourKind.CLASS.equals(behaviour.kind()) == true) + { + policyComponent.bindClassBehaviour(policy, + type, + javaBehaviour); + } + else if (BehaviourKind.ASSOCIATION.equals(behaviour.kind()) == true) + { + policyComponent.bindAssociationBehaviour(policy, + type, + toQName(behaviour.assocType()), + javaBehaviour); + } + } + + /** + * + * @param policyName + * @param method + * @return + */ + private QName resolvePolicy(String policyName, Method method) + { + QName policy = null; + if (policyName.isEmpty() == true) + { + policy = QName.createQName(NamespaceService.ALFRESCO_URI, method.getName()); + } + else + { + policy = toQName(policyName); + } + + return policy; + } + + /** + * + * @param behaviourBean + * @param typeName + * @return + */ + private QName resolveType(BehaviourBean behaviourBean, String typeName) + { + QName type = null; + if (typeName.isEmpty() == true) + { + // get default + type = toQName(behaviourBean.defaultType()); + } + else + { + // convert set + type = toQName(typeName); + } + return type; + } + + /** + * + * @param name + * @return + */ + private QName toQName(String name) + { + return QName.createQName(name, namespaceService); + } + +} diff --git a/rm-server/source/java/org/alfresco/repo/policy/annotation/Behaviour.java b/rm-server/source/java/org/alfresco/repo/policy/annotation/Behaviour.java new file mode 100644 index 0000000000..619677be13 --- /dev/null +++ b/rm-server/source/java/org/alfresco/repo/policy/annotation/Behaviour.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2005-2013 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.policy.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.alfresco.repo.policy.Behaviour.NotificationFrequency; + +/** + * Behaviour method annotation. + * + * @author Roy Wetherall + */ +@Target(value = ElementType.METHOD) +@Retention(value = RetentionPolicy.RUNTIME) +public @interface Behaviour +{ + /** kind of behaviour */ + BehaviourKind kind(); + + /** qualified name of policy */ + String policy() default ""; + + /** qualified name of type/aspect */ + String type() default ""; + + /** qualified name of association */ + String assocType() default "cm:contains"; + + /** notification frequency */ + NotificationFrequency notificationFrequency() default NotificationFrequency.EVERY_EVENT; +} diff --git a/rm-server/source/java/org/alfresco/repo/policy/annotation/BehaviourBean.java b/rm-server/source/java/org/alfresco/repo/policy/annotation/BehaviourBean.java new file mode 100644 index 0000000000..9cae4088b3 --- /dev/null +++ b/rm-server/source/java/org/alfresco/repo/policy/annotation/BehaviourBean.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2005-2013 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.policy.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author Roy Wetherall + */ +@Target(value = ElementType.TYPE) +@Retention(value = RetentionPolicy.RUNTIME) +public @interface BehaviourBean +{ + /** qualified name of type/aspect */ + String defaultType() default ""; + +} diff --git a/rm-server/source/java/org/alfresco/repo/policy/annotation/BehaviourKind.java b/rm-server/source/java/org/alfresco/repo/policy/annotation/BehaviourKind.java new file mode 100644 index 0000000000..c76b664269 --- /dev/null +++ b/rm-server/source/java/org/alfresco/repo/policy/annotation/BehaviourKind.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2005-2013 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.policy.annotation; + +/** + * Enumeration describing the different kinds of behaviour. + * + * @author Roy Wetherall + * @since 2.2 + */ +public enum BehaviourKind +{ + CLASS, + ASSOCIATION +}