diff --git a/config/alfresco/action-services-context.xml b/config/alfresco/action-services-context.xml index a931fba337..4bf0309303 100644 --- a/config/alfresco/action-services-context.xml +++ b/config/alfresco/action-services-context.xml @@ -394,21 +394,12 @@ - - - - - - - - - - - - - - - + + + + + + {http://www.alfresco.org/model/content/1.0}content @@ -424,12 +415,9 @@ - - - - - - + + + diff --git a/config/alfresco/copy-services-context.xml b/config/alfresco/copy-services-context.xml index 4ea7d741ad..920c5a2f2f 100644 --- a/config/alfresco/copy-services-context.xml +++ b/config/alfresco/copy-services-context.xml @@ -13,15 +13,12 @@ - + - diff --git a/config/alfresco/core-services-context.xml b/config/alfresco/core-services-context.xml index 22a57e9fc9..f2a5968ce9 100644 --- a/config/alfresco/core-services-context.xml +++ b/config/alfresco/core-services-context.xml @@ -508,24 +508,11 @@ - - - - - - - - - - - - - - - - - - + + + + + @@ -791,54 +778,24 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + - - - - - - - - - + + + + diff --git a/config/alfresco/ibatis/alfresco-SqlMapConfig.xml b/config/alfresco/ibatis/alfresco-SqlMapConfig.xml index 49c3d28a9f..eab9189eaf 100644 --- a/config/alfresco/ibatis/alfresco-SqlMapConfig.xml +++ b/config/alfresco/ibatis/alfresco-SqlMapConfig.xml @@ -80,12 +80,12 @@ Inbound settings from iBatis + - diff --git a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/node-common-SqlMap.xml b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/node-common-SqlMap.xml index 7f57ecd1eb..1a24475249 100644 --- a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/node-common-SqlMap.xml +++ b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/node-common-SqlMap.xml @@ -748,7 +748,7 @@ - select node.id as id, store.protocol as protocol, @@ -756,11 +756,14 @@ node.uuid as uuid, node.node_deleted as node_deleted from - alf_node node + alf_node_aspects na + join alf_node node on (na.node_id = node.id) join alf_store store on (store.id = node.store_id) - join alf_node_aspects na on (na.node_id = node.id and na.node_id > ? and na.qname_id = ?) - order by - node.id + where + = #{idOne}]]> + + and na.qname_id in + #{item} diff --git a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/patch-common-SqlMap.xml b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/patch-common-SqlMap.xml index 75f9db6528..d95d011f05 100644 --- a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/patch-common-SqlMap.xml +++ b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/patch-common-SqlMap.xml @@ -62,13 +62,6 @@ - - - - - - - @@ -117,12 +110,6 @@ - - - - - - @@ -342,31 +329,6 @@ acl_id > ? - - - + + @@ -537,17 +509,6 @@ id = ? - - - update - avm_child_entries - set - name = ? - where - child_id = ? - and parent_id = ? - - INSERT INTO alf_node_properties ( diff --git a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/query-copy-common-SqlMap.xml b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/query-copy-common-SqlMap.xml index 01b482b2e5..cf08348861 100644 --- a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/query-copy-common-SqlMap.xml +++ b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/query-copy-common-SqlMap.xml @@ -8,31 +8,40 @@ - - - - - + + + + + + - + \ No newline at end of file diff --git a/config/alfresco/messages/patch-service.properties b/config/alfresco/messages/patch-service.properties index f4b97d624d..792c4323d4 100644 --- a/config/alfresco/messages/patch-service.properties +++ b/config/alfresco/messages/patch-service.properties @@ -419,3 +419,6 @@ patch.activitiesTemplatesUpdate.result=Updated {0} activities email templates. patch.avmToAdmRemoteStore.description=Migrates Share Surf config from AVM sitestore to DM Sites folder. patch.avmToAdmRemoteStore.complete=Completed Share Surf config migration. + +patch.copiedFromAspect.description=Adds peer associations for cm:copiedfrom and cm:workingcopy (new model) and removes cm:source property +patch.copiedFromAspect.result=Fixed cm:copiedfrom model for {0} nodes. See file {1} for details. diff --git a/config/alfresco/model/contentModel.xml b/config/alfresco/model/contentModel.xml index bb907bb6b4..0c0993dc88 100644 --- a/config/alfresco/model/contentModel.xml +++ b/config/alfresco/model/contentModel.xml @@ -880,19 +880,19 @@ Copied From - - - d:noderef - true - true - false - - true - false - true - - - + + + + false + true + + + cm:cmobject + false + false + + + @@ -908,7 +908,27 @@ d:text - + + + + + Checked Out + + + + true + false + + + cm:workingcopy + true + false + + + + + cm:lockable + diff --git a/config/alfresco/patch/patch-services-context.xml b/config/alfresco/patch/patch-services-context.xml index 8f35441964..08ee6171ef 100644 --- a/config/alfresco/patch/patch-services-context.xml +++ b/config/alfresco/patch/patch-services-context.xml @@ -45,27 +45,13 @@ - - - - - - - - - - - - - - - - - - - - - + + + + + + + @@ -1864,18 +1850,10 @@ - - - - - - - - - - - - + + + + @@ -2909,7 +2887,7 @@ patch.avmToAdmRemoteStore.description 0 5011 - 5012 + 5012 false @@ -2920,4 +2898,22 @@ /alfresco/site-data + + + patch.copiedFromAspect + patch.copiedFromAspect.description + 0 + 5012 + 5013 + false + false + + + + + + + + + \ No newline at end of file diff --git a/config/alfresco/public-services-context.xml b/config/alfresco/public-services-context.xml index 90e2a213eb..b181a91811 100644 --- a/config/alfresco/public-services-context.xml +++ b/config/alfresco/public-services-context.xml @@ -308,8 +308,9 @@ - - + + + org.alfresco.service.cmr.coci.CheckOutCheckInService @@ -318,15 +319,15 @@ - + - + - + diff --git a/config/alfresco/public-services-security-context.xml b/config/alfresco/public-services-security-context.xml index a26c6b5db2..176c539f37 100644 --- a/config/alfresco/public-services-security-context.xml +++ b/config/alfresco/public-services-security-context.xml @@ -104,23 +104,15 @@ - - - + - - + - - - - - - - - - + + + + Unlock @@ -544,7 +536,26 @@ - + + + + + + + org.alfresco.service.cmr.repository.CopyService.copy=ACL_ALLOW + org.alfresco.service.cmr.repository.CopyService.copyAndRename=ACL_ALLOW + org.alfresco.service.cmr.repository.CopyService.getOriginal=ACL_NODE.0.sys:base.ReadProperties,AFTER_ACL_NODE.sys:base.ReadProperties + org.alfresco.service.cmr.repository.CopyService.getCopies=ACL_NODE.0.sys:base.ReadProperties,AFTER_ACL_NODE.sys:base.ReadProperties + org.alfresco.service.cmr.repository.CopyService.*=ACL_DENY + + + + + + + + + @@ -638,7 +649,7 @@ - + @@ -648,6 +659,9 @@ org.alfresco.service.cmr.coci.CheckOutCheckInService.checkin=ACL_NODE.0.cm:lockable.CheckIn org.alfresco.service.cmr.coci.CheckOutCheckInService.cancelCheckout=ACL_NODE.0.cm:lockable.CancelCheckOut org.alfresco.service.cmr.coci.CheckOutCheckInService.getWorkingCopy=ACL_NODE.0.sys:base.Read + org.alfresco.service.cmr.coci.CheckOutCheckInService.getCheckedOut=ACL_NODE.0.sys:base.Read + org.alfresco.service.cmr.coci.CheckOutCheckInService.isWorkingCopy=ACL_NODE.0.sys:base.Read + org.alfresco.service.cmr.coci.CheckOutCheckInService.isCheckedOut=ACL_NODE.0.sys:base.Read org.alfresco.service.cmr.coci.CheckOutCheckInService.*=ACL_DENY diff --git a/config/alfresco/rule-services-context.xml b/config/alfresco/rule-services-context.xml index 15eee57035..6f630dfdf3 100644 --- a/config/alfresco/rule-services-context.xml +++ b/config/alfresco/rule-services-context.xml @@ -6,30 +6,15 @@ - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + false diff --git a/config/alfresco/subsystems/fileServers/default/network-protocol-context.xml b/config/alfresco/subsystems/fileServers/default/network-protocol-context.xml index 14ba2e70b9..7c00d77c88 100644 --- a/config/alfresco/subsystems/fileServers/default/network-protocol-context.xml +++ b/config/alfresco/subsystems/fileServers/default/network-protocol-context.xml @@ -52,6 +52,7 @@ + diff --git a/config/alfresco/version.properties b/config/alfresco/version.properties index cb8aa75949..deaacd344a 100644 --- a/config/alfresco/version.properties +++ b/config/alfresco/version.properties @@ -19,4 +19,4 @@ version.build=@build-number@ # Schema number -version.schema=5012 +version.schema=5013 diff --git a/source/java/org/alfresco/cmis/mapping/AbstractVersioningProperty.java b/source/java/org/alfresco/cmis/mapping/AbstractVersioningProperty.java index f0f86458ba..94aae8bfa9 100644 --- a/source/java/org/alfresco/cmis/mapping/AbstractVersioningProperty.java +++ b/source/java/org/alfresco/cmis/mapping/AbstractVersioningProperty.java @@ -39,16 +39,12 @@ public abstract class AbstractVersioningProperty extends AbstractProperty /** * Construct - * - * @param serviceRegistry - * @param propertyName */ protected AbstractVersioningProperty(ServiceRegistry serviceRegistry, String propertyName) { super(serviceRegistry, propertyName); } - public NodeRef getVersionSeries(NodeRef nodeRef) { if (nodeRef.getStoreRef().getProtocol().equals(VersionBaseModel.STORE_PROTOCOL)) @@ -56,20 +52,21 @@ public abstract class AbstractVersioningProperty extends AbstractProperty // Due to the remapping done for us by the versioned node services, we can simply look up the properties // containing the component parts of the node ref to map back to the original node Map properties = getServiceRegistry().getNodeService().getProperties(nodeRef); - return new NodeRef((String) properties.get(ContentModel.PROP_STORE_PROTOCOL), + nodeRef = new NodeRef((String) properties.get(ContentModel.PROP_STORE_PROTOCOL), (String) properties.get(ContentModel.PROP_STORE_IDENTIFIER), (String) properties .get(ContentModel.PROP_NODE_UUID)); } else if (isWorkingCopy(nodeRef)) { - return (NodeRef) getServiceRegistry().getNodeService().getProperty(nodeRef, ContentModel.PROP_COPY_REFERENCE); + NodeRef originalNodeRef = getServiceRegistry().getCopyService().getOriginal(nodeRef); + nodeRef = originalNodeRef == null ? nodeRef : originalNodeRef; } return nodeRef; } public boolean isWorkingCopy(NodeRef nodeRef) { - return getServiceRegistry().getNodeService().hasAspect(nodeRef, ContentModel.ASPECT_WORKING_COPY); + return getServiceRegistry().getCheckOutCheckInService().isWorkingCopy(nodeRef); } public boolean hasWorkingCopy(NodeRef nodeRef) diff --git a/source/java/org/alfresco/cmis/mapping/CMISServicesImpl.java b/source/java/org/alfresco/cmis/mapping/CMISServicesImpl.java index 2d7188b4d8..be1839840a 100644 --- a/source/java/org/alfresco/cmis/mapping/CMISServicesImpl.java +++ b/source/java/org/alfresco/cmis/mapping/CMISServicesImpl.java @@ -1341,11 +1341,8 @@ public class CMISServicesImpl implements CMISServices, ApplicationContextAware, return nodeService.getPrimaryParent(folderRef).getParentRef(); } - /* - * (non-Javadoc) - * @see org.alfresco.cmis.CMISServices#getVersionSeries(java.lang.String, java.lang.Class, boolean) - */ @SuppressWarnings("unchecked") + @Override public T getVersionSeries(String objectId, Class requiredType, boolean isVersionable) throws CMISConstraintException, CMISVersioningException, CMISObjectNotFoundException, CMISInvalidArgumentException, CMISPermissionDeniedException @@ -1367,12 +1364,13 @@ public class CMISServicesImpl implements CMISServices, ApplicationContextAware, { NodeRef nodeRef = (NodeRef) object; // Map working copy nodes back to where they were checked out from - if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_WORKING_COPY)) + NodeRef workingCopyNodeRef = checkOutCheckInService.getCheckedOut(nodeRef); + if (workingCopyNodeRef != null) { - result = (NodeRef) nodeService.getProperty(nodeRef, ContentModel.PROP_COPY_REFERENCE); + // It is a working copy + result = workingCopyNodeRef; } - // Preserve all other nodes - else + else // Preserve all other nodes { result = nodeRef; } diff --git a/source/java/org/alfresco/cmis/mapping/VersionSeriesIdProperty.java b/source/java/org/alfresco/cmis/mapping/VersionSeriesIdProperty.java index f6d52bdde1..a8c9202d6d 100644 --- a/source/java/org/alfresco/cmis/mapping/VersionSeriesIdProperty.java +++ b/source/java/org/alfresco/cmis/mapping/VersionSeriesIdProperty.java @@ -21,10 +21,9 @@ package org.alfresco.cmis.mapping; import java.io.Serializable; import org.alfresco.cmis.CMISDictionaryModel; -import org.alfresco.model.ContentModel; import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.coci.CheckOutCheckInService; import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; /** * @author dward @@ -33,28 +32,29 @@ public class VersionSeriesIdProperty extends AbstractVersioningProperty { /** * Construct - * - * @param serviceRegistry */ public VersionSeriesIdProperty(ServiceRegistry serviceRegistry) { super(serviceRegistry, CMISDictionaryModel.PROP_VERSION_SERIES_ID); } - /* - * (non-Javadoc) - * @see org.alfresco.cmis.property.PropertyAccessor#getValue(org.alfresco.service.cmr.repository.NodeRef) - */ + @Override public Serializable getValue(NodeRef nodeRef) { - NodeService nodeService = getServiceRegistry().getNodeService(); - if (isWorkingCopy(nodeRef)) + CheckOutCheckInService checkOutCheckInService = getServiceRegistry().getCheckOutCheckInService(); + NodeRef result = null; + if (checkOutCheckInService.isWorkingCopy(nodeRef)) { - return nodeService.getProperty(nodeRef, ContentModel.PROP_COPY_REFERENCE).toString(); + result = checkOutCheckInService.getCheckedOut(nodeRef); + if (result == null) + { + result = nodeRef; + } } else { - return getVersionSeries(nodeRef).toString(); + result = getVersionSeries(nodeRef); } + return result.toString(); } } diff --git a/source/java/org/alfresco/filesys/repo/ContentDiskDriver.java b/source/java/org/alfresco/filesys/repo/ContentDiskDriver.java index 17dcbd1709..ead78e9327 100644 --- a/source/java/org/alfresco/filesys/repo/ContentDiskDriver.java +++ b/source/java/org/alfresco/filesys/repo/ContentDiskDriver.java @@ -82,7 +82,6 @@ import org.alfresco.jlan.util.WildCard; import org.alfresco.model.ContentModel; import org.alfresco.repo.admin.SysAdminParams; import org.alfresco.repo.content.MimetypeMap; -import org.alfresco.repo.content.encoding.ContentCharsetFinder; import org.alfresco.repo.node.archive.NodeArchiveService; import org.alfresco.repo.policy.BehaviourFilter; import org.alfresco.repo.security.authentication.AuthenticationContext; @@ -90,11 +89,13 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.service.cmr.action.Action; import org.alfresco.service.cmr.action.ActionService; +import org.alfresco.service.cmr.coci.CheckOutCheckInService; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.lock.LockService; import org.alfresco.service.cmr.lock.LockType; import org.alfresco.service.cmr.lock.NodeLockedException; import org.alfresco.service.cmr.model.FileFolderService; +import org.alfresco.service.cmr.repository.AssociationRef; import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.ContentIOException; import org.alfresco.service.cmr.repository.ContentService; @@ -177,6 +178,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa private CifsHelper cifsHelper; private NamespaceService namespaceService; private NodeService nodeService; + private CheckOutCheckInService checkOutCheckInService; private SearchService searchService; private ContentService contentService; private MimetypeService mimetypeService; @@ -244,7 +246,15 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa { return this.nodeService; } - + + /** + * @return service to provide information on check-in and check-out + */ + public CheckOutCheckInService getCheckOutCheckInService() + { + return checkOutCheckInService; + } + /** * Return the content service * @@ -359,7 +369,15 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa { this.nodeService = nodeService; } - + + /** + * @param checkOutCheckInService used to check for checked out nodes + */ + public void setCheckOutCheckInService(CheckOutCheckInService checkOutCheckInService) + { + this.checkOutCheckInService = checkOutCheckInService; + } + /** * @param searchService the search service */ @@ -3198,15 +3216,12 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa // Check if the node is a working copy - if ( nodeService.hasAspect( targetNodeRef, ContentModel.ASPECT_WORKING_COPY)) { - + NodeRef mainNodeRef = checkOutCheckInService.getCheckedOut(targetNodeRef); + if ( mainNodeRef != null) + { // Check if the main document is still locked - - NodeRef mainNodeRef = (NodeRef) nodeService.getProperty( targetNodeRef, ContentModel.PROP_COPY_REFERENCE); - if ( mainNodeRef != null) { - LockType lockTyp = lockService.getLockType( mainNodeRef); - logger.debug(" Main node ref lock type = " + lockTyp); - } + LockType lockTyp = lockService.getLockType( mainNodeRef); + logger.debug(" Main node ref lock type = " + lockTyp); } } } @@ -4083,12 +4098,13 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa if ( nodeService.hasAspect( fromNode, ContentModel.ASPECT_COPIEDFROM)) { // Add the copied from aspect to the new file - - NodeRef copiedFromNode = (NodeRef) nodeService.getProperty( fromNode, ContentModel.PROP_COPY_REFERENCE); - Map copiedFromProperties = new HashMap(1); - copiedFromProperties.put(ContentModel.PROP_COPY_REFERENCE, copiedFromNode); - - nodeService.addAspect( toNode, ContentModel.ASPECT_COPIEDFROM, copiedFromProperties); + List assocs = nodeService.getSourceAssocs(fromNode, ContentModel.ASSOC_ORIGINAL); + if (assocs.size() > 0) + { + AssociationRef assoc = assocs.get(0); + NodeRef originalNodeRef = assoc.getTargetRef(); + nodeService.createAssociation(toNode, originalNodeRef, ContentModel.ASSOC_ORIGINAL); + } // Remove the copied from aspect from old working copy file @@ -4099,19 +4115,19 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME)) logger.debug(" Moved aspect " + ContentModel.ASPECT_COPIEDFROM + " to new document"); - // Check if the original node is locked - - if ( lockService.getLockType( copiedFromNode) == null) { - - // Add the lock back onto the original file - - lockService.lock( copiedFromNode, LockType.READ_ONLY_LOCK); - - // DEBUG - - if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME)) - logger.debug(" Re-locked copied from node " + copiedFromNode); - } +// // Check if the original node is locked +// +// if ( lockService.getLockType( copiedFromNode) == null) { +// +// // Add the lock back onto the original file +// +// lockService.lock( copiedFromNode, LockType.READ_ONLY_LOCK); +// +// // DEBUG +// +// if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME)) +// logger.debug(" Re-locked copied from node " + copiedFromNode); +// } } // Copy over all aspects from non-system namespaces (we will copy their properties later) diff --git a/source/java/org/alfresco/filesys/repo/ContentIOControlHandler.java b/source/java/org/alfresco/filesys/repo/ContentIOControlHandler.java index 2649603fc3..59d6d5c2e8 100644 --- a/source/java/org/alfresco/filesys/repo/ContentIOControlHandler.java +++ b/source/java/org/alfresco/filesys/repo/ContentIOControlHandler.java @@ -31,7 +31,6 @@ import org.alfresco.filesys.alfresco.DesktopTarget; import org.alfresco.filesys.alfresco.IOControl; import org.alfresco.filesys.alfresco.IOControlHandler; import org.alfresco.jlan.server.SrvSession; -import org.alfresco.jlan.server.auth.ClientInfo; import org.alfresco.jlan.server.filesys.IOControlNotImplementedException; import org.alfresco.jlan.server.filesys.NetworkFile; import org.alfresco.jlan.server.filesys.TreeConnection; @@ -41,6 +40,7 @@ import org.alfresco.jlan.smb.nt.NTIOCtl; import org.alfresco.jlan.util.DataBuffer; import org.alfresco.model.ContentModel; import org.alfresco.repo.security.authentication.AuthenticationException; +import org.alfresco.service.cmr.coci.CheckOutCheckInService; import org.alfresco.service.cmr.lock.LockType; import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.NodeRef; @@ -127,6 +127,14 @@ public class ContentIOControlHandler implements IOControlHandler return contentDriver.getNodeService(); } + /** + * @return the service to provide check-in and check-out data + */ + public final CheckOutCheckInService getCheckOutCheckInService() + { + return contentDriver.getCheckOutCheckInService(); + } + /** * Return the filesystem driver * @@ -374,14 +382,10 @@ public class ContentIOControlHandler implements IOControlHandler String owner = (String) getNodeService().getProperty( childNode, ContentModel.PROP_WORKING_COPY_OWNER); String copiedFrom = null; - if ( getNodeService().hasAspect( childNode, ContentModel.ASPECT_COPIEDFROM)) - { - // Get the path of the file the working copy was generated from - - NodeRef fromNode = (NodeRef) getNodeService().getProperty( childNode, ContentModel.PROP_COPY_REFERENCE); - if ( fromNode != null) - copiedFrom = (String) getNodeService().getProperty( fromNode, ContentModel.PROP_NAME); - } + // Get the path of the file the working copy was generated from + NodeRef fromNode = getCheckOutCheckInService().getCheckedOut(childNode); + if ( fromNode != null) + copiedFrom = (String) getNodeService().getProperty( fromNode, ContentModel.PROP_NAME); // Pack the owner and copied from values diff --git a/source/java/org/alfresco/opencmis/mapping/AbstractVersioningProperty.java b/source/java/org/alfresco/opencmis/mapping/AbstractVersioningProperty.java index 4354aac498..b8fe2e8d91 100644 --- a/source/java/org/alfresco/opencmis/mapping/AbstractVersioningProperty.java +++ b/source/java/org/alfresco/opencmis/mapping/AbstractVersioningProperty.java @@ -41,9 +41,6 @@ public abstract class AbstractVersioningProperty extends AbstractProperty /** * Construct - * - * @param serviceRegistry - * @param propertyName */ protected AbstractVersioningProperty(ServiceRegistry serviceRegistry, String propertyName) { @@ -59,13 +56,14 @@ public abstract class AbstractVersioningProperty extends AbstractProperty // containing the component parts of the node ref to map back to the // original node Map properties = getServiceRegistry().getNodeService().getProperties(nodeRef); - return new NodeRef((String) properties.get(ContentModel.PROP_STORE_PROTOCOL), + nodeRef = new NodeRef((String) properties.get(ContentModel.PROP_STORE_PROTOCOL), (String) properties.get(ContentModel.PROP_STORE_IDENTIFIER), (String) properties.get(ContentModel.PROP_NODE_UUID)); - } else if (isWorkingCopy(nodeRef)) + } + else if (isWorkingCopy(nodeRef)) { - return (NodeRef) getServiceRegistry().getNodeService().getProperty(nodeRef, - ContentModel.PROP_COPY_REFERENCE); + NodeRef originalNodeRef = getServiceRegistry().getCheckOutCheckInService().getCheckedOut(nodeRef); + nodeRef = originalNodeRef == null ? nodeRef : originalNodeRef; } return nodeRef; } diff --git a/source/java/org/alfresco/opencmis/mapping/IsImmutableProperty.java b/source/java/org/alfresco/opencmis/mapping/IsImmutableProperty.java index 20402bd908..af4f5276b6 100644 --- a/source/java/org/alfresco/opencmis/mapping/IsImmutableProperty.java +++ b/source/java/org/alfresco/opencmis/mapping/IsImmutableProperty.java @@ -33,21 +33,13 @@ public class IsImmutableProperty extends AbstractVersioningProperty { /** * Construct - * - * @param serviceRegistry */ public IsImmutableProperty(ServiceRegistry serviceRegistry) { super(serviceRegistry, PropertyIds.IS_IMMUTABLE); } - /* - * (non-Javadoc) - * - * @see - * org.alfresco.cmis.property.PropertyAccessor#getValue(org.alfresco.service - * .cmr.repository.NodeRef) - */ + @Override public Serializable getValue(NodeRef nodeRef) { if (!isCurrentVersion(nodeRef)) diff --git a/source/java/org/alfresco/opencmis/mapping/IsLatestVersionProperty.java b/source/java/org/alfresco/opencmis/mapping/IsLatestVersionProperty.java index b655a889fe..81118b1a80 100644 --- a/source/java/org/alfresco/opencmis/mapping/IsLatestVersionProperty.java +++ b/source/java/org/alfresco/opencmis/mapping/IsLatestVersionProperty.java @@ -36,21 +36,13 @@ public class IsLatestVersionProperty extends AbstractVersioningProperty { /** * Construct - * - * @param serviceRegistry */ public IsLatestVersionProperty(ServiceRegistry serviceRegistry) { super(serviceRegistry, PropertyIds.IS_LATEST_VERSION); } - /* - * (non-Javadoc) - * - * @see - * org.alfresco.cmis.property.PropertyAccessor#getValue(org.alfresco.service - * .cmr.repository.NodeRef) - */ + @Override public Serializable getValue(NodeRef nodeRef) { if (isWorkingCopy(nodeRef) || getVersionSeries(nodeRef).equals(nodeRef) && !hasWorkingCopy(nodeRef)) diff --git a/source/java/org/alfresco/opencmis/mapping/IsVersionSeriesCheckedOutProperty.java b/source/java/org/alfresco/opencmis/mapping/IsVersionSeriesCheckedOutProperty.java index 93b1d011cc..581db1a810 100644 --- a/source/java/org/alfresco/opencmis/mapping/IsVersionSeriesCheckedOutProperty.java +++ b/source/java/org/alfresco/opencmis/mapping/IsVersionSeriesCheckedOutProperty.java @@ -33,21 +33,13 @@ public class IsVersionSeriesCheckedOutProperty extends AbstractVersioningPropert { /** * Construct - * - * @param serviceRegistry */ public IsVersionSeriesCheckedOutProperty(ServiceRegistry serviceRegistry) { super(serviceRegistry, PropertyIds.IS_VERSION_SERIES_CHECKED_OUT); } - /* - * (non-Javadoc) - * - * @see - * org.alfresco.cmis.property.PropertyAccessor#getValue(org.alfresco.service - * .cmr.repository.NodeRef) - */ + @Override public Serializable getValue(NodeRef nodeRef) { return isWorkingCopy(nodeRef) || hasWorkingCopy(getVersionSeries(nodeRef)); diff --git a/source/java/org/alfresco/opencmis/mapping/VersionSeriesIdProperty.java b/source/java/org/alfresco/opencmis/mapping/VersionSeriesIdProperty.java index 0bfa80c456..394e0f72f4 100644 --- a/source/java/org/alfresco/opencmis/mapping/VersionSeriesIdProperty.java +++ b/source/java/org/alfresco/opencmis/mapping/VersionSeriesIdProperty.java @@ -20,10 +20,9 @@ package org.alfresco.opencmis.mapping; import java.io.Serializable; -import org.alfresco.model.ContentModel; import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.coci.CheckOutCheckInService; import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; import org.apache.chemistry.opencmis.commons.PropertyIds; /** @@ -33,30 +32,29 @@ public class VersionSeriesIdProperty extends AbstractVersioningProperty { /** * Construct - * - * @param serviceRegistry */ public VersionSeriesIdProperty(ServiceRegistry serviceRegistry) { super(serviceRegistry, PropertyIds.VERSION_SERIES_ID); } - /* - * (non-Javadoc) - * - * @see - * org.alfresco.cmis.property.PropertyAccessor#getValue(org.alfresco.service - * .cmr.repository.NodeRef) - */ + @Override public Serializable getValue(NodeRef nodeRef) { - NodeService nodeService = getServiceRegistry().getNodeService(); - if (isWorkingCopy(nodeRef)) + CheckOutCheckInService checkOutCheckInService = getServiceRegistry().getCheckOutCheckInService(); + NodeRef result = null; + if (checkOutCheckInService.isWorkingCopy(nodeRef)) { - return nodeService.getProperty(nodeRef, ContentModel.PROP_COPY_REFERENCE).toString(); - } else - { - return getVersionSeries(nodeRef).toString(); + result = checkOutCheckInService.getCheckedOut(nodeRef); + if (result == null) + { + result = nodeRef; + } } + else + { + result = getVersionSeries(nodeRef); + } + return result.toString(); } } diff --git a/source/java/org/alfresco/repo/action/executer/CopyActionExecuter.java b/source/java/org/alfresco/repo/action/executer/CopyActionExecuter.java index a661332ad0..7d8d40e9ca 100644 --- a/source/java/org/alfresco/repo/action/executer/CopyActionExecuter.java +++ b/source/java/org/alfresco/repo/action/executer/CopyActionExecuter.java @@ -23,13 +23,16 @@ package org.alfresco.repo.action.executer; import java.util.List; -import org.alfresco.model.ContentModel; +import org.alfresco.query.PagingRequest; +import org.alfresco.query.PagingResults; import org.alfresco.repo.action.ParameterDefinitionImpl; import org.alfresco.service.cmr.action.Action; import org.alfresco.service.cmr.action.ParameterDefinition; +import org.alfresco.service.cmr.coci.CheckOutCheckInService; import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.CopyService; +import org.alfresco.service.cmr.repository.CopyService.CopyInfo; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.rule.RuleServiceException; @@ -50,20 +53,12 @@ public class CopyActionExecuter extends ActionExecuterAbstractBase public static final String PARAM_DEEP_COPY = "deep-copy"; public static final String PARAM_OVERWRITE_COPY = "overwrite-copy"; - /** - * Node operations service - */ private CopyService copyService; - - /** - * The node service - */ private NodeService nodeService; + private CheckOutCheckInService checkOutCheckInService; /** * Sets the node service - * - * @param nodeService the node service */ public void setNodeService(NodeService nodeService) { @@ -72,19 +67,21 @@ public class CopyActionExecuter extends ActionExecuterAbstractBase /** * Sets the copy service - * - * @param copyService the copy service */ public void setCopyService(CopyService copyService) { this.copyService = copyService; } - - /** - * @see org.alfresco.repo.action.ParameterizedItemAbstractBase#addParameterDefinitions(java.util.List) - */ - @Override + /** + * Service to determine check-in or check-out status + */ + public void setCheckOutCheckInService(CheckOutCheckInService checkOutCheckInService) + { + this.checkOutCheckInService = checkOutCheckInService; + } + + @Override protected void addParameterDefinitions(List paramList) { paramList.add(new ParameterDefinitionImpl(PARAM_DESTINATION_FOLDER, DataTypeDefinition.NODE_REF, true, getParamDisplayLabel(PARAM_DESTINATION_FOLDER))); @@ -92,80 +89,76 @@ public class CopyActionExecuter extends ActionExecuterAbstractBase paramList.add(new ParameterDefinitionImpl(PARAM_OVERWRITE_COPY, DataTypeDefinition.BOOLEAN, false, getParamDisplayLabel(PARAM_OVERWRITE_COPY))); } - /** - * @see org.alfresco.repo.action.executer.ActionExecuter#execute(org.alfresco.repo.ref.NodeRef, org.alfresco.repo.ref.NodeRef) - */ + @Override public void executeImpl(Action ruleAction, NodeRef actionedUponNodeRef) { - if (this.nodeService.exists(actionedUponNodeRef) == true) - { + if (!nodeService.exists(actionedUponNodeRef)) + { + return; + } NodeRef destinationParent = (NodeRef)ruleAction.getParameterValue(PARAM_DESTINATION_FOLDER); // Get the deep copy value boolean deepCopy = false; - Boolean deepCopyValue = (Boolean)ruleAction.getParameterValue(PARAM_DEEP_COPY); - if (deepCopyValue != null) + Boolean deepCopyValue = (Boolean)ruleAction.getParameterValue(PARAM_DEEP_COPY); + if (deepCopyValue != null) + { + deepCopy = deepCopyValue.booleanValue(); + } + + // Get the overwirte value + boolean overwrite = true; + Boolean overwriteValue = (Boolean)ruleAction.getParameterValue(PARAM_OVERWRITE_COPY); + if (overwriteValue != null) + { + overwrite = overwriteValue.booleanValue(); + } + + // Since we are overwriting we need to figure out whether the destination node exists + NodeRef copyNodeRef = null; + if (overwrite == true) + { + // Try and find copies of the actioned upon node reference. + // Include the parent folder because that's where the copy will be if this action + // had done the first copy. + PagingResults copies = copyService.getCopies( + actionedUponNodeRef, + destinationParent, + new PagingRequest(1000)); + for (CopyInfo copyInfo : copies.getPage()) { - deepCopy = deepCopyValue.booleanValue(); - } - - // Get the overwirte value - boolean overwrite = true; - Boolean overwriteValue = (Boolean)ruleAction.getParameterValue(PARAM_OVERWRITE_COPY); - if (overwriteValue != null) - { - overwrite = overwriteValue.booleanValue(); - } - - // Since we are overwriting we need to figure out whether the destination node exists - NodeRef destinationNodeRef = null; - if (overwrite == true) - { - // Try and find copies of the actioned upon node reference - List copies = this.copyService.getCopies(actionedUponNodeRef); - if (copies != null && copies.isEmpty() == false) + NodeRef copy = copyInfo.getNodeRef(); + // We know that it is in the destination parent, but avoid working copies + if (checkOutCheckInService.isWorkingCopy(copy)) { - for (NodeRef copy : copies) - { - // Ignore if the copy is a working copy - if (this.nodeService.hasAspect(copy, ContentModel.ASPECT_WORKING_COPY) == false) - { - // We can assume that we are looking for a node created by this action so the primary parent will - // match the destination folder - NodeRef parent = this.nodeService.getPrimaryParent(copy).getParentRef(); - if (parent.equals(destinationParent) == true) - { - if (destinationNodeRef == null) - { - destinationNodeRef = copy; - } - else - { - throw new RuleServiceException(ERR_OVERWRITE); - } - } - - } - } + continue; + } + if (copyNodeRef == null) + { + copyNodeRef = copy; + } + else + { + throw new RuleServiceException(ERR_OVERWRITE); } } - - if (destinationNodeRef != null) - { - // Overwrite the state of the destination node ref with the actioned upon node state - this.copyService.copy(actionedUponNodeRef, destinationNodeRef); - } - else - { - ChildAssociationRef originalAssoc = nodeService.getPrimaryParent(actionedUponNodeRef); - // Create a new copy of the node - this.copyService.copyAndRename( - actionedUponNodeRef, - destinationParent, - originalAssoc.getTypeQName(), - originalAssoc.getQName(), - deepCopy); - } - } + } + + if (copyNodeRef != null) + { + // Overwrite the state of the destination node ref with the actioned upon node state + this.copyService.copy(actionedUponNodeRef, copyNodeRef); + } + else + { + ChildAssociationRef originalAssoc = nodeService.getPrimaryParent(actionedUponNodeRef); + // Create a new copy of the node + this.copyService.copyAndRename( + actionedUponNodeRef, + destinationParent, + originalAssoc.getTypeQName(), + originalAssoc.getQName(), + deepCopy); + } } } diff --git a/source/java/org/alfresco/repo/action/executer/TransformActionExecuter.java b/source/java/org/alfresco/repo/action/executer/TransformActionExecuter.java index b7573d4ed4..bcc0949200 100644 --- a/source/java/org/alfresco/repo/action/executer/TransformActionExecuter.java +++ b/source/java/org/alfresco/repo/action/executer/TransformActionExecuter.java @@ -21,9 +21,12 @@ package org.alfresco.repo.action.executer; import java.util.List; import org.alfresco.model.ContentModel; +import org.alfresco.query.PagingRequest; +import org.alfresco.query.PagingResults; import org.alfresco.repo.action.ParameterDefinitionImpl; import org.alfresco.service.cmr.action.Action; import org.alfresco.service.cmr.action.ParameterDefinition; +import org.alfresco.service.cmr.coci.CheckOutCheckInService; import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.repository.ContentReader; @@ -35,6 +38,7 @@ import org.alfresco.service.cmr.repository.NoTransformerException; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.TransformationOptions; +import org.alfresco.service.cmr.repository.CopyService.CopyInfo; import org.alfresco.service.cmr.rule.RuleServiceException; import org.alfresco.service.namespace.QName; import org.apache.commons.logging.Log; @@ -47,20 +51,16 @@ import org.apache.commons.logging.LogFactory; */ public class TransformActionExecuter extends ActionExecuterAbstractBase { - - /** Error messages */ + /* Error messages */ public static final String ERR_OVERWRITE = "Unable to overwrite copy because more than one have been found."; private static final String CONTENT_READER_NOT_FOUND_MESSAGE = "Can not find Content Reader for document. Operation can't be performed"; private static final String TRANSFORMING_ERROR_MESSAGE = "Some error occurred during document transforming. Error message: "; private static final String TRANSFORMER_NOT_EXISTS_MESSAGE_PATTERN = "Transformer for '%s' source mime type and '%s' target mime type was not found. Operation can't be performed"; - /** - * The logger - */ private static Log logger = LogFactory.getLog(TransformActionExecuter.class); - /** + /* * Action constants */ public static final String NAME = "transform"; @@ -70,19 +70,18 @@ public class TransformActionExecuter extends ActionExecuterAbstractBase public static final String PARAM_ASSOC_QNAME = "assoc-name"; public static final String PARAM_OVERWRITE_COPY = "overwrite-copy"; - /** + /* * Injected services */ private DictionaryService dictionaryService; private NodeService nodeService; + private CheckOutCheckInService checkOutCheckInService; private ContentService contentService; private CopyService copyService; private MimetypeService mimetypeService; /** * Set the mime type service - * - * @param mimetypeService the mime type service */ public void setMimetypeService(MimetypeService mimetypeService) { @@ -91,18 +90,22 @@ public class TransformActionExecuter extends ActionExecuterAbstractBase /** * Set the node service - * - * @param nodeService set the node service */ public void setNodeService(NodeService nodeService) { this.nodeService = nodeService; } - + + /** + * Set the service to determine check-in and check-out status + */ + public void setCheckOutCheckInService(CheckOutCheckInService checkOutCheckInService) + { + this.checkOutCheckInService = checkOutCheckInService; + } + /** * Set the dictionary service - * - * @param dictionaryService the dictionary service */ public void setDictionaryService(DictionaryService dictionaryService) { @@ -111,8 +114,6 @@ public class TransformActionExecuter extends ActionExecuterAbstractBase /** * Set the content service - * - * @param contentService the content service */ public void setContentService(ContentService contentService) { @@ -121,8 +122,6 @@ public class TransformActionExecuter extends ActionExecuterAbstractBase /** * Set the copy service - * - * @param copyService the copy service */ public void setCopyService(CopyService copyService) { @@ -198,32 +197,35 @@ public class TransformActionExecuter extends ActionExecuterAbstractBase NodeRef copyNodeRef = null; if (overwrite == true) { - // Try and find copies of the actioned upon node reference - List copies = this.copyService.getCopies(actionedUponNodeRef); - if (copies != null && copies.isEmpty() == false) + // Try and find copies of the actioned upon node reference. + // Include the parent folder because that's where the copy will be if this action + // had done the first copy. + PagingResults copies = copyService.getCopies( + actionedUponNodeRef, + destinationParent, + new PagingRequest(1000)); + for (CopyInfo copyInfo : copies.getPage()) { - for (NodeRef copy : copies) + NodeRef copy = copyInfo.getNodeRef(); + String copyName = copyInfo.getName(); + // We know that it is in the destination parent, but avoid working copies + if (checkOutCheckInService.isWorkingCopy(copy)) { - // Ignore if the copy is a working copy - if (this.nodeService.hasAspect(copy, ContentModel.ASPECT_WORKING_COPY) == false) - { - // We can assume that we are looking for a node created by this action so the primary parent will - // match the destination folder and the name will be the same - NodeRef parent = this.nodeService.getPrimaryParent(copy).getParentRef(); - String copyName = (String)this.nodeService.getProperty(copy, ContentModel.PROP_NAME); - if (parent.equals(destinationParent) == true && copyName.equals(newName) == true) - { - if (copyNodeRef == null) - { - copyNodeRef = copy; - } - else - { - throw new RuleServiceException(ERR_OVERWRITE); - } - } - - } + // It is a working copy + continue; + } + else if (!newName.equals(copyName)) + { + // The copy's name is not what this action would have set it to + continue; + } + if (copyNodeRef == null) + { + copyNodeRef = copy; + } + else + { + throw new RuleServiceException(ERR_OVERWRITE); } } } diff --git a/source/java/org/alfresco/repo/admin/patch/impl/CopiedFromAspectPatch.java b/source/java/org/alfresco/repo/admin/patch/impl/CopiedFromAspectPatch.java new file mode 100644 index 0000000000..a83e9e2964 --- /dev/null +++ b/source/java/org/alfresco/repo/admin/patch/impl/CopiedFromAspectPatch.java @@ -0,0 +1,438 @@ +/* + * Copyright (C) 2005-2010 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.admin.patch.impl; + +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.admin.patch.AbstractPatch; +import org.alfresco.repo.admin.patch.PatchExecuter; +import org.alfresco.repo.batch.BatchProcessWorkProvider; +import org.alfresco.repo.batch.BatchProcessor; +import org.alfresco.repo.batch.BatchProcessor.BatchProcessWorker; +import org.alfresco.repo.batch.BatchProcessor.BatchProcessWorkerAdaptor; +import org.alfresco.repo.domain.node.NodeDAO; +import org.alfresco.repo.domain.node.NodeDAO.NodeRefQueryCallback; +import org.alfresco.repo.domain.patch.PatchDAO; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.Pair; +import org.alfresco.util.TempFileProvider; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.extensions.surf.util.I18NUtil; + +/** + * Patch to break the link between {@link ContentModel#ASPECT_WORKING_COPY working copies} + * and {@link ContentModel#ASPECT_COPIEDFROM copies}. + *

+ * Formerly, when a document was copied, it was given a cm:source property on the + * cm:copiedfrom aspect - a d:noderef property. During checkout, the + * working copy was given the cm:workingcopy aspect and the cm:copiedfrom + * aspect was assumed to be present. However, the ordinality of the cm:copiedfrom's + * cm:source property didn't match up with the checkin-checkout 1:1 relationship. + *

+ * This patch works in two parts: + *

+ * cm:copiedfrom
+ *

    + *
  • cm:source is transformed into a peer association, cm:original
  • + *
  • The aspect is removed where the source no longer exists
  • + *
+ *

+ * cm:workingcopy
+ *

    + *
  • cm:source is transformed into a peer association, cm:workingcopylink
  • + *
  • The original is given aspect cm:checkedout
  • + *
  • The copy keeps cm:workingcopy
  • + *
+ * + * @author Derek Hulley + * @since 4.0 + */ +public class CopiedFromAspectPatch extends AbstractPatch +{ + private static final String MSG_SUCCESS = "patch.copiedFromAspect.result"; + + private PatchDAO patchDAO; + private NodeDAO nodeDAO; + private DictionaryService dictionaryService; + + private int batchThreads = 2; + private int batchSize = 1000; + private int batchMaxQueryRange = 10000; + + private static Log logger = LogFactory.getLog(CopiedFromAspectPatch.class); + private static Log progress_logger = LogFactory.getLog(PatchExecuter.class); + + public CopiedFromAspectPatch() + { + } + + /** + * @param patchDAO additional queries + */ + public void setPatchDAO(PatchDAO patchDAO) + { + this.patchDAO = patchDAO; + } + + /** + * @param nodeDAO provides query support + */ + public void setNodeDAO(NodeDAO nodeDAO) + { + this.nodeDAO = nodeDAO; + } + + /** + * @param dictionaryService type and aspect resolution + */ + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + /** + * @param batchThreads the number of threads that will write child association changes + */ + public void setBatchThreads(int batchThreads) + { + this.batchThreads = batchThreads; + } + + /** + * @param batchSize the number of child associations that will be modified per transaction + */ + public void setBatchSize(int batchSize) + { + this.batchSize = batchSize; + } + + /** + * @param batchMaxQueryRange the largest ID range that the work provider can query for. + * Lower this if the DB resultset retrieval causes memory issues + * prior to the {@link #setBatchQuerySize(int) query limit} being + * applied. + */ + public void setBatchMaxQueryRange(int batchMaxQueryRange) + { + this.batchMaxQueryRange = batchMaxQueryRange; + } + + @Override + protected void checkProperties() + { + super.checkProperties(); + checkPropertyNotNull(patchDAO, "patchDAO"); + checkPropertyNotNull(nodeDAO, "nodeDAO"); + checkPropertyNotNull(dictionaryService, "dictionaryService"); + checkPropertyNotNull(applicationEventPublisher, "applicationEventPublisher"); + } + + private CopiedFromAspectPatch write(FileChannel file, Object obj) + { + try + { + file.write(ByteBuffer.wrap(obj.toString().getBytes("UTF-8"))); + } + catch (IOException e) + { + logger.error("Failed to write object to file: " + obj.toString()); + } + return this; + } + private CopiedFromAspectPatch writeLine(FileChannel file, Object obj) + { + write(file, obj); + write(file, "\n"); + return this; + } + + @Override + protected String applyInternal() throws Exception + { + // put the log file into a long life temp directory + File tempDir = TempFileProvider.getLongLifeTempDir("patches"); + File logFile = new File(tempDir, "CopiedFromAspectPatch.log"); + + // open the file for appending + RandomAccessFile outputFile = new RandomAccessFile(logFile, "rw"); + FileChannel file = outputFile.getChannel(); + try + { + // move to the end of the file + file.position(file.size()); + // add a newline and it's ready + writeLine(file, "").writeLine(file, ""); + writeLine(file, "CopiedFromAspectPatch.log executing on " + new Date()); + if (logger.isDebugEnabled()) + { + logger.debug("Starting CopiedFromAspectPatch. [Q=Query; P=Process]"); + } + + int updated = process(file); + // done + String msg = I18NUtil.getMessage(MSG_SUCCESS, updated, logFile); + return msg; + } + finally + { + try { file.close(); } catch (IOException e) {} + } + } + + /** + * Does the actual work, writing results to the given file channel + + * @return Returns a status message after completion + */ + private int process(final FileChannel file) + { + // Authentication + final String user = AuthenticationUtil.getRunAsUser(); + + Set qnames = new HashSet(); + qnames.add(ContentModel.ASPECT_COPIEDFROM); + qnames.add(ContentModel.ASPECT_WORKING_COPY); + + // Instance to provide raw data to process + BatchProcessWorkProvider> workProvider = new WorkProvider(qnames); + + // Instance to handle each item of work + BatchProcessWorker> worker = new BatchProcessWorkerAdaptor>() + { + @Override + public void beforeProcess() throws Throwable + { + AuthenticationUtil.setRunAsUser(user); + } + @Override + public void process(Pair entry) throws Throwable + { + CopiedFromAspectPatch.this.process(file, entry); + } + @Override + public void afterProcess() throws Throwable + { + AuthenticationUtil.clearCurrentSecurityContext(); + } + }; + + BatchProcessor> batchProcessor = new BatchProcessor>( + "CopiedFromAspectPatch", + transactionService.getRetryingTransactionHelper(), + workProvider, + this.batchThreads, this.batchSize, + null, + progress_logger, + 1000); + int updated = batchProcessor.process(worker, true); + return updated; + } + + /** + * Work provider that performs incremental queries to find nodes with the + * required aspects. + * + * @author Derek Hulley + * @since 4.0 + */ + private class WorkProvider implements BatchProcessWorkProvider> + { + private long maxId = Long.MAX_VALUE; + private long workCount = Long.MAX_VALUE; + private long currentId = 0L; + private final Set aspectQNames; + + private WorkProvider(Set aspectQNames) + { + this.aspectQNames = aspectQNames; + } + + @Override + public synchronized int getTotalEstimatedWorkSize() + { + if (maxId == Long.MAX_VALUE) + { + maxId = patchDAO.getMaxAdmNodeID(); + if (logger.isDebugEnabled()) + { + logger.debug("\tQ: Max node id: " + maxId); + } + } + if (workCount == Long.MAX_VALUE) + { + workCount = patchDAO.getCountNodesWithAspects(aspectQNames); + if (logger.isDebugEnabled()) + { + logger.debug("\tQ: Work count: " + workCount); + } + } + return (int) workCount; + } + + @Override + public synchronized Collection> getNextWork() + { + // Record the results + final List> results = new ArrayList>(batchMaxQueryRange); + // Record the node IDs for bulk loading + final List nodeIds = new ArrayList(batchMaxQueryRange); + + NodeRefQueryCallback callback = new NodeRefQueryCallback() + { + @Override + public boolean handle(Pair nodePair) + { + if (logger.isDebugEnabled()) + { + logger.debug("\tQ: Recording node work: " + nodePair); + } + results.add(nodePair); + nodeIds.add(nodePair.getFirst()); + return true; + } + }; + // Keep querying until we have enough results to give back + while (currentId <= maxId) + { + nodeDAO.getNodesWithAspects( + aspectQNames, + currentId, + currentId + batchMaxQueryRange, + callback); + // Increment the minimum ID + currentId += batchMaxQueryRange; + } + // Preload the nodes for quicker access + nodeDAO.cacheNodesById(nodeIds); + // Done + return results; + } + } + + private static final QName PROP_SOURCE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "source"); + /** + * Does the per-node manipulation as stated in the class's docs + * + * @param file the file to write output to + * @param nodePair the node to operate on + */ + private void process(FileChannel file, Pair nodePair) + { + if (logger.isDebugEnabled()) + { + logger.debug("\tP: Processing node: " + nodePair); + } + NodeRef nodeRef = nodePair.getSecond(); + // First check if the source property is present and valid + NodeRef sourceNodeRef = DefaultTypeConverter.INSTANCE.convert( + NodeRef.class, + nodeService.getProperty(nodeRef, PROP_SOURCE)); + // Does the source exist? + if (sourceNodeRef == null || !nodeService.exists(sourceNodeRef)) + { + if ( + nodeService.hasAspect(nodeRef, ContentModel.ASPECT_COPIEDFROM) && + nodeService.getSourceAssocs(nodeRef, ContentModel.ASSOC_ORIGINAL).size() == 0) + { + // There is no association pointing back to the original and the source node is invalid + if (logger.isDebugEnabled()) + { + logger.debug("\tP: Removing cm:copiedfrom: " + nodePair); + } + writeLine(file, "Removing cm:copiedfrom from node: " + nodePair); + nodeService.removeAspect(nodeRef, ContentModel.ASPECT_COPIEDFROM); + } + if ( + nodeService.hasAspect(nodeRef, ContentModel.ASPECT_WORKING_COPY) && + nodeService.getSourceAssocs(nodeRef, ContentModel.ASSOC_WORKING_COPY_LINK).size() == 0) + { + // There is no association from the checked out node and the source node is invalid + if (logger.isDebugEnabled()) + { + logger.debug("\tP: Removing cm:workingcopy: " + nodePair); + } + writeLine(file, "Removing cm:workingcopy from node: " + nodePair); + nodeService.removeAspect(nodeRef, ContentModel.ASPECT_WORKING_COPY); + } + // If nothing was done, then it's a node with the new data model and we can leave it + } + else + { + // The cm:source property points to a valid node. + // This needs to be fixed up to use the new model. + if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_COPIEDFROM)) + { + if (nodeService.getSourceAssocs(nodeRef, ContentModel.ASSOC_ORIGINAL).size() > 0) + { + // The association is already present, so just remove the property (we'll do that later) + } + else + { + if (logger.isDebugEnabled()) + { + logger.debug("\tP: Adding association cm:original: " + nodePair); + } + writeLine(file, "Adding association cm:original: " + nodePair); + // Create the association + nodeService.createAssociation(nodeRef, sourceNodeRef, ContentModel.ASSOC_ORIGINAL); + } + } + if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_WORKING_COPY)) + { + if (nodeService.getSourceAssocs(nodeRef, ContentModel.ASSOC_WORKING_COPY_LINK).size() > 0) + { + // The association is already present, so just remove the property (we'll do that later) + } + else + { + if (logger.isDebugEnabled()) + { + logger.debug("\tP: Adding aspect cm:checkedout: " + sourceNodeRef); + logger.debug("\tP: Adding association cm:workingcopylink: " + nodePair); + } + writeLine(file, "Adding aspect cm:checkedout: " + sourceNodeRef); + writeLine(file, "Adding association cm:workingcopylink to " + nodePair); + // Add aspect to source + nodeService.addAspect(sourceNodeRef, ContentModel.ASPECT_CHECKED_OUT, null); + // Create the association + nodeService.createAssociation(sourceNodeRef, nodeRef, ContentModel.ASSOC_WORKING_COPY_LINK); + } + } + } + // Remove the property if it exists + nodeService.removeProperty(nodeRef, PROP_SOURCE); + } +} diff --git a/source/java/org/alfresco/repo/admin/patch/impl/DuplicateEntry.java b/source/java/org/alfresco/repo/admin/patch/impl/DuplicateEntry.java deleted file mode 100644 index 56bf1790d6..0000000000 --- a/source/java/org/alfresco/repo/admin/patch/impl/DuplicateEntry.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (C) 2005-2010 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.admin.patch.impl; - -/** - * Wrapper for duplicate entries - * - * @author Dmitry Velichkevich - */ -public class DuplicateEntry -{ - private static final int ODD_MULTIPLICATOR = 37; - - private Long id; - - private Long parentId; - - private String name; - - private Long amount; - - public DuplicateEntry() - { - } - - public DuplicateEntry(Long id, Long parentId, String name, Long amount) - { - this.id = id; - this.parentId = parentId; - this.name = name; - this.amount = amount; - } - - public Long getId() - { - return id; - } - - public void setId(Long id) - { - this.id = id; - } - - public Long getParentId() - { - return parentId; - } - - public void setParentId(Long parentId) - { - this.parentId = parentId; - } - - public String getName() - { - return name; - } - - public void setName(String name) - { - this.name = name; - } - - public Long getAmount() - { - return amount; - } - - public void setAmount(Long amount) - { - this.amount = amount; - } - - @Override - public boolean equals(Object obj) - { - if (!(obj instanceof DuplicateEntry)) - { - return false; - } - DuplicateEntry converted = (DuplicateEntry) obj; - return (id == converted.getId()) && (parentId == converted.getParentId()) && ((null == name) ? (null == converted.getName()) : (name.equals(converted.getName()))); - } - - @Override - public int hashCode() - { - int result = (int) id.intValue(); - result = DuplicateEntry.ODD_MULTIPLICATOR * result + (int) parentId.longValue(); - result = DuplicateEntry.ODD_MULTIPLICATOR * result + (int) amount.longValue(); - result = DuplicateEntry.ODD_MULTIPLICATOR * result + ((null != name) ? (name.hashCode()) : (0)); - return result; - } -} diff --git a/source/java/org/alfresco/repo/audit/access/AccessAuditorTest.java b/source/java/org/alfresco/repo/audit/access/AccessAuditorTest.java index a4645ed013..d2a034827c 100644 --- a/source/java/org/alfresco/repo/audit/access/AccessAuditorTest.java +++ b/source/java/org/alfresco/repo/audit/access/AccessAuditorTest.java @@ -495,7 +495,6 @@ public class AccessAuditorTest assertEquals("addNodeAspect", origMap.get("action")); // createNode createContent readContent updateNodeProperties addNodeAspect copyNode checkOut createVersion assertContains("updateNodeProperties", origMap.get("sub-actions")); - assertContains("readContent", origMap.get("sub-actions")); assertEquals("cm:content", origMap.get("type")); assertEquals("/cm:homeFolder/cm:folder1/cm:content1", origMap.get("path")); @@ -503,7 +502,6 @@ public class AccessAuditorTest assertEquals("CHECK OUT", workMap.get("action")); assertContains("createNode", workMap.get("sub-actions")); assertContains("createContent", workMap.get("sub-actions")); - assertContains("readContent", workMap.get("sub-actions")); assertContains("updateNodeProperties", workMap.get("sub-actions")); assertContains("addNodeAspect", workMap.get("sub-actions")); assertContains("copyNode", workMap.get("sub-actions")); @@ -545,7 +543,6 @@ public class AccessAuditorTest assertContains("createVersion", origMap.get("sub-actions")); assertContains("updateNodeProperties", origMap.get("sub-actions")); assertContains("checkIn", origMap.get("sub-actions")); - assertContains("readContent", origMap.get("sub-actions")); assertEquals("/cm:homeFolder/cm:folder1/cm:content1", origMap.get("path")); assertEquals("cm:content", origMap.get("type")); } diff --git a/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImpl.java b/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImpl.java index 1884987a48..af81eccb38 100644 --- a/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImpl.java +++ b/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImpl.java @@ -48,6 +48,7 @@ import org.alfresco.service.cmr.model.FileExistsException; import org.alfresco.service.cmr.model.FileFolderService; import org.alfresco.service.cmr.model.FileNotFoundException; import org.alfresco.service.cmr.repository.AspectMissingException; +import org.alfresco.service.cmr.repository.AssociationRef; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.CopyService; @@ -55,12 +56,12 @@ import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.rule.RuleService; import org.alfresco.service.cmr.rule.RuleType; -import org.alfresco.service.cmr.search.ResultSet; -import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.cmr.security.AuthenticationService; import org.alfresco.service.cmr.security.OwnableService; import org.alfresco.service.cmr.version.VersionService; import org.alfresco.service.namespace.QName; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.extensions.surf.util.I18NUtil; /** @@ -95,48 +96,16 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService */ private static final String EXTENSION_CHARACTER = "."; - /** - * The node service - */ + private static Log logger = LogFactory.getLog(CheckOutCheckInServiceImpl.class); + private NodeService nodeService; - - /** - * The version service - */ private VersionService versionService; - - /** - * The lock service - */ private LockService lockService; - - /** - * The copy service - */ private CopyService copyService; - - /** - * The file folder service - */ private FileFolderService fileFolderService; - - /** Ownable service */ private OwnableService ownableService; - - /** - * The search service - */ - private SearchService searchService; - - /** Policy component */ private PolicyComponent policyComponent; - - /** - * The authentication service - */ private AuthenticationService authenticationService; - - /** Rule service */ private RuleService ruleService; /** @@ -157,8 +126,6 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService /** * Set the node service - * - * @param nodeService the node service */ public void setNodeService(NodeService nodeService) { @@ -167,8 +134,6 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService /** * Set the version service - * - * @param versionService the version service */ public void setVersionService(VersionService versionService) { @@ -177,7 +142,6 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService /** * Set the ownable service - * @param ownableService ownable service */ public void setOwnableService(OwnableService ownableService) { @@ -186,8 +150,6 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService /** * Sets the lock service - * - * @param lockService the lock service */ public void setLockService(LockService lockService) { @@ -196,8 +158,6 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService /** * Sets the copy service - * - * @param copyService the copy service */ public void setCopyService(CopyService copyService) { @@ -206,28 +166,14 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService /** * Sets the authentication service - * - * @param authenticationService the authentication service */ public void setAuthenticationService(AuthenticationService authenticationService) { this.authenticationService = authenticationService; } - /** - * Set the search service - * - * @param searchService the search service - */ - public void setSearchService(SearchService searchService) - { - this.searchService = searchService; - } - /** * Set the file folder service - * - * @param fileFolderService the file folder service */ public void setFileFolderService(FileFolderService fileFolderService) { @@ -236,8 +182,6 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService /** * Sets the versionable aspect behaviour implementation - * - * @param versionableAspect the versionable aspect behaviour implementation */ public void setVersionableAspect(VersionableAspect versionableAspect) { @@ -295,10 +239,10 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService /** * Invoke the before check out policy * - * @param nodeRef - * @param destinationParentNodeRef - * @param destinationAssocTypeQName - * @param destinationAssocQName + * @param nodeRef the node to be checked out + * @param destinationParentNodeRef the parent of the working copy + * @param destinationAssocTypeQName the working copy's primary association type + * @param destinationAssocQName the working copy's primary association name */ private void invokeBeforeCheckOut( NodeRef nodeRef, @@ -314,14 +258,13 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService { policy.beforeCheckOut(nodeRef, destinationParentNodeRef, destinationAssocTypeQName, destinationAssocQName); } - } } /** * Invoke on the on check out policy * - * @param workingCopy + * @param workingCopy the new working copy */ private void invokeOnCheckOut(NodeRef workingCopy) { @@ -333,15 +276,14 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService { policy.onCheckOut(workingCopy); } - } } /** * Invoke before check in policy * - * @param workingCopyNodeRef - * @param versionProperties + * @param workingCopyNodeRef the current working copy to check in + * @param versionProperties * @param contentUrl * @param keepCheckedOut */ @@ -359,14 +301,13 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService { policy.beforeCheckIn(workingCopyNodeRef, versionProperties, contentUrl, keepCheckedOut); } - } } /** * Invoke on check in policy * - * @param nodeRef + * @param nodeRef the node being checked in */ private void invokeOnCheckIn(NodeRef nodeRef) { @@ -378,14 +319,13 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService { policy.onCheckIn(nodeRef); } - } } /** * Invoke before cancel check out * - * @param workingCopy + * @param workingCopy the working copy that will be destroyed */ private void invokeBeforeCancelCheckOut(NodeRef workingCopy) { @@ -404,7 +344,7 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService /** * Invoke on cancel check out * - * @param nodeRef + * @param nodeRef the working copy that will be destroyed */ private void invokeOnCancelCheckOut(NodeRef nodeRef) { @@ -416,34 +356,41 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService { policy.onCancelCheckOut(nodeRef); } - } } - - /** - * @see org.alfresco.service.cmr.coci.CheckOutCheckInService#checkout(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName, org.alfresco.service.namespace.QName) - */ + + @Override + public NodeRef checkout(NodeRef nodeRef) + { + // Find the primary parent in order to determine where to put the copy + ChildAssociationRef childAssocRef = nodeService.getPrimaryParent(nodeRef); + + // Checkout the working copy to the same destination + return checkout(nodeRef, childAssocRef.getParentRef(), childAssocRef.getTypeQName(), childAssocRef.getQName()); + } + + @Override public NodeRef checkout( final NodeRef nodeRef, final NodeRef destinationParentNodeRef, final QName destinationAssocTypeQName, QName destinationAssocQName) { - LockType lockType = this.lockService.getLockType(nodeRef); - if (LockType.READ_ONLY_LOCK.equals(lockType) == true || getWorkingCopy(nodeRef) != null) + if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_CHECKED_OUT)) { throw new CheckOutCheckInServiceException(MSG_ALREADY_CHECKEDOUT); } // Make sure we are no checking out a working copy node - if (this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_WORKING_COPY) == true) + if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_WORKING_COPY)) { throw new CheckOutCheckInServiceException(MSG_ERR_ALREADY_WORKING_COPY); } behaviourFilter.disableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE); behaviourFilter.disableBehaviour(destinationParentNodeRef, ContentModel.ASPECT_AUDITABLE); - try { + try + { return doCheckout(nodeRef, destinationParentNodeRef, destinationAssocTypeQName, destinationAssocQName); } finally @@ -453,27 +400,23 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService } } - /** - * @param nodeRef - * @param destinationParentNodeRef - * @param destinationAssocTypeQName - * @param destinationAssocQName - * @return - */ - private NodeRef doCheckout(final NodeRef nodeRef, final NodeRef destinationParentNodeRef, - final QName destinationAssocTypeQName, QName destinationAssocQName) + private NodeRef doCheckout( + final NodeRef nodeRef, + final NodeRef destinationParentNodeRef, + final QName destinationAssocTypeQName, + QName destinationAssocQName) { // Apply the lock aspect if required - if (this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_LOCKABLE) == false) + if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_LOCKABLE) == false) { - this.nodeService.addAspect(nodeRef, ContentModel.ASPECT_LOCKABLE, null); + nodeService.addAspect(nodeRef, ContentModel.ASPECT_LOCKABLE, null); } // Invoke before check out policy invokeBeforeCheckOut(nodeRef, destinationParentNodeRef, destinationAssocTypeQName, destinationAssocQName); // Rename the working copy - String copyName = (String)this.nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); + String copyName = (String)nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); copyName = createWorkingCopyName(copyName); // Get the user @@ -487,7 +430,7 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService final QName copyQName = QName.createQName(destinationAssocQName.getNamespaceURI(), QName.createValidLocalName(copyName)); // Find the primary parent - ChildAssociationRef childAssocRef = this.nodeService.getPrimaryParent(nodeRef); + ChildAssociationRef childAssocRef = nodeService.getPrimaryParent(nodeRef); // If destination parent for working copy is the same as the parent of the source node // then working copy should be created even if the user has no permissions to create children in @@ -521,12 +464,14 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService // Update the working copy name - this.nodeService.setProperty(workingCopy, ContentModel.PROP_NAME, copyName); + nodeService.setProperty(workingCopy, ContentModel.PROP_NAME, copyName); // Apply the working copy aspect to the working copy Map workingCopyProperties = new HashMap(1); workingCopyProperties.put(ContentModel.PROP_WORKING_COPY_OWNER, userName); - this.nodeService.addAspect(workingCopy, ContentModel.ASPECT_WORKING_COPY, workingCopyProperties); + nodeService.addAspect(workingCopy, ContentModel.ASPECT_WORKING_COPY, workingCopyProperties); + nodeService.addAspect(nodeRef, ContentModel.ASPECT_CHECKED_OUT, null); + nodeService.createAssociation(nodeRef, workingCopy, ContentModel.ASSOC_WORKING_COPY_LINK); } finally { @@ -534,7 +479,7 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService } // Lock the original node - this.lockService.lock(nodeRef, LockType.READ_ONLY_LOCK); + lockService.lock(nodeRef, LockType.READ_ONLY_LOCK); // Invoke on check out policy invokeOnCheckOut(workingCopy); @@ -559,160 +504,15 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService } } - /** - * @see org.alfresco.service.cmr.coci.CheckOutCheckInService#checkout(org.alfresco.service.cmr.repository.NodeRef) - */ - public NodeRef checkout(NodeRef nodeRef) - { - // Find the primary parent in order to determine where to put the copy - ChildAssociationRef childAssocRef = this.nodeService.getPrimaryParent(nodeRef); - - // Checkout the working copy to the same destination - return checkout(nodeRef, childAssocRef.getParentRef(), childAssocRef.getTypeQName(), childAssocRef.getQName()); - } - - /** - * @see org.alfresco.repo.version.operations.VersionOperationsService#checkin(org.alfresco.repo.ref.NodeRef, Map, java.lang.String, boolean) - */ + @Override public NodeRef checkin( NodeRef workingCopyNodeRef, - Map versionProperties, - String contentUrl, - boolean keepCheckedOut) + Map versionProperties) { - NodeRef nodeRef = null; - - // Check that we have been handed a working copy - if (this.nodeService.hasAspect(workingCopyNodeRef, ContentModel.ASPECT_WORKING_COPY) == false) - { - // Error since we have not been passed a working copy - throw new AspectMissingException(ContentModel.ASPECT_WORKING_COPY, workingCopyNodeRef); - } - - // Check that the working node still has the copy aspect applied - if (this.nodeService.hasAspect(workingCopyNodeRef, ContentModel.ASPECT_COPIEDFROM) == true) - { - // Invoke policy - invokeBeforeCheckIn(workingCopyNodeRef, versionProperties, contentUrl, keepCheckedOut); - - Map workingCopyProperties = nodeService.getProperties(workingCopyNodeRef); - // Try and get the original node reference - nodeRef = (NodeRef) workingCopyProperties.get(ContentModel.PROP_COPY_REFERENCE); - if(nodeRef == null) - { - // Error since the original node can not be found - throw new CheckOutCheckInServiceException(MSG_ERR_BAD_COPY); - } - - try - { - // Release the lock - this.lockService.unlock(nodeRef); - } - catch (UnableToReleaseLockException exception) - { - throw new CheckOutCheckInServiceException(MSG_ERR_NOT_OWNER, exception); - } - - if (contentUrl != null) - { - ContentData contentData = (ContentData) workingCopyProperties.get(ContentModel.PROP_CONTENT); - if (contentData == null) - { - throw new AlfrescoRuntimeException(MSG_ERR_WORKINGCOPY_HAS_NO_MIMETYPE, new Object[]{workingCopyNodeRef}); - } - else - { - contentData = new ContentData( - contentUrl, - contentData.getMimetype(), - contentData.getSize(), - contentData.getEncoding()); - } - // Set the content url value onto the working copy - this.nodeService.setProperty( - workingCopyNodeRef, - ContentModel.PROP_CONTENT, - contentData); - } - - // Copy the contents of the working copy onto the original - this.copyService.copy(workingCopyNodeRef, nodeRef); - - // Handle name change on working copy (only for folders/files) - if (fileFolderService.getFileInfo(workingCopyNodeRef) != null) - { - String origName = (String)this.nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); - String name = (String)this.nodeService.getProperty(workingCopyNodeRef, ContentModel.PROP_NAME); - if (hasWorkingCopyNameChanged(name, origName)) - { - // ensure working copy has working copy label in its name to avoid name clash - if (!name.contains(" " + getWorkingCopyLabel())) - { - try - { - fileFolderService.rename(workingCopyNodeRef, createWorkingCopyName(name)); - } - catch (FileExistsException e) - { - throw new CheckOutCheckInServiceException(e, MSG_ERR_CANNOT_RENAME, name, createWorkingCopyName(name)); - } - catch (FileNotFoundException e) - { - throw new CheckOutCheckInServiceException(e, MSG_ERR_CANNOT_RENAME, name, createWorkingCopyName(name)); - } - } - try - { - // rename original to changed working name - fileFolderService.rename(nodeRef, getNameFromWorkingCopyName(name)); - } - catch (FileExistsException e) - { - throw new CheckOutCheckInServiceException(e, MSG_ERR_CANNOT_RENAME, origName, getNameFromWorkingCopyName(name)); - } - catch (FileNotFoundException e) - { - throw new CheckOutCheckInServiceException(e, MSG_ERR_CANNOT_RENAME, name, getNameFromWorkingCopyName(name)); - } - } - } - - if (versionProperties != null && this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE) == true) - { - // Create the new version - this.versionService.createVersion(nodeRef, versionProperties); - } - - if (keepCheckedOut == false) - { - // Delete the working copy - this.nodeService.deleteNode(workingCopyNodeRef); - - // Remove the lock aspect (copied from working copy) - this.nodeService.removeAspect(nodeRef, ContentModel.ASPECT_LOCKABLE); - } - else - { - // Re-lock the original node - this.lockService.lock(nodeRef, LockType.READ_ONLY_LOCK); - } - - // Invoke policy - invokeOnCheckIn(nodeRef); - } - else - { - // Error since the copy aspect is missing - throw new AspectMissingException(ContentModel.ASPECT_COPIEDFROM, workingCopyNodeRef); - } - - return nodeRef; + return checkin(workingCopyNodeRef, versionProperties, null, false); } - /** - * @see org.alfresco.service.cmr.coci.CheckOutCheckInService#checkin(org.alfresco.service.cmr.repository.NodeRef, Map, java.lang.String) - */ + @Override public NodeRef checkin( NodeRef workingCopyNodeRef, Map versionProperties, @@ -721,110 +521,252 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService return checkin(workingCopyNodeRef, versionProperties, contentUrl, false); } - /** - * @see org.alfresco.service.cmr.coci.CheckOutCheckInService#checkin(org.alfresco.service.cmr.repository.NodeRef, Map) - */ + @Override public NodeRef checkin( NodeRef workingCopyNodeRef, - Map versionProperties) + Map versionProperties, + String contentUrl, + boolean keepCheckedOut) { - return checkin(workingCopyNodeRef, versionProperties, null, false); - } - - /** - * @see org.alfresco.service.cmr.coci.CheckOutCheckInService#cancelCheckout(org.alfresco.service.cmr.repository.NodeRef) - */ - public NodeRef cancelCheckout(NodeRef workingCopyNodeRef) - { - NodeRef nodeRef = null; - // Check that we have been handed a working copy - if (this.nodeService.hasAspect(workingCopyNodeRef, ContentModel.ASPECT_WORKING_COPY) == false) + if (!nodeService.hasAspect(workingCopyNodeRef, ContentModel.ASPECT_WORKING_COPY)) { // Error since we have not been passed a working copy throw new AspectMissingException(ContentModel.ASPECT_WORKING_COPY, workingCopyNodeRef); } - // Ensure that the node has the copy aspect - if (this.nodeService.hasAspect(workingCopyNodeRef, ContentModel.ASPECT_COPIEDFROM) == true) + // Get the checked out node + NodeRef nodeRef = getCheckedOut(workingCopyNodeRef); + if (nodeRef == null) { - // Invoke policy - invokeBeforeCancelCheckOut(workingCopyNodeRef); + // Error since the original node can not be found + throw new CheckOutCheckInServiceException(MSG_ERR_BAD_COPY); + } + + // Invoke policy + invokeBeforeCheckIn(workingCopyNodeRef, versionProperties, contentUrl, keepCheckedOut); + + try + { + // Release the lock + lockService.unlock(nodeRef); + } + catch (UnableToReleaseLockException exception) + { + throw new CheckOutCheckInServiceException(MSG_ERR_NOT_OWNER, exception); + } - // Get the original node - nodeRef = (NodeRef)this.nodeService.getProperty(workingCopyNodeRef, ContentModel.PROP_COPY_REFERENCE); - if (nodeRef == null) + if (contentUrl != null) + { + ContentData contentData = (ContentData) nodeService.getProperty(workingCopyNodeRef, ContentModel.PROP_CONTENT); + if (contentData == null) { - // Error since the original node can not be found - throw new CheckOutCheckInServiceException(MSG_ERR_BAD_COPY); - } - behaviourFilter.disableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE); - try{ + throw new AlfrescoRuntimeException(MSG_ERR_WORKINGCOPY_HAS_NO_MIMETYPE, new Object[]{workingCopyNodeRef}); + } + else + { + contentData = new ContentData( + contentUrl, + contentData.getMimetype(), + contentData.getSize(), + contentData.getEncoding()); + } + // Set the content url value onto the working copy + nodeService.setProperty( + workingCopyNodeRef, + ContentModel.PROP_CONTENT, + contentData); + } + + // Copy the contents of the working copy onto the original + this.copyService.copy(workingCopyNodeRef, nodeRef); + + // Handle name change on working copy (only for folders/files) + if (fileFolderService.getFileInfo(workingCopyNodeRef) != null) + { + String origName = (String)nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); + String name = (String)nodeService.getProperty(workingCopyNodeRef, ContentModel.PROP_NAME); + if (hasWorkingCopyNameChanged(name, origName)) + { + // ensure working copy has working copy label in its name to avoid name clash + if (!name.contains(" " + getWorkingCopyLabel())) + { + try + { + fileFolderService.rename(workingCopyNodeRef, createWorkingCopyName(name)); + } + catch (FileExistsException e) + { + throw new CheckOutCheckInServiceException(e, MSG_ERR_CANNOT_RENAME, name, createWorkingCopyName(name)); + } + catch (FileNotFoundException e) + { + throw new CheckOutCheckInServiceException(e, MSG_ERR_CANNOT_RENAME, name, createWorkingCopyName(name)); + } + } + try + { + // rename original to changed working name + fileFolderService.rename(nodeRef, getNameFromWorkingCopyName(name)); + } + catch (FileExistsException e) + { + throw new CheckOutCheckInServiceException(e, MSG_ERR_CANNOT_RENAME, origName, getNameFromWorkingCopyName(name)); + } + catch (FileNotFoundException e) + { + throw new CheckOutCheckInServiceException(e, MSG_ERR_CANNOT_RENAME, name, getNameFromWorkingCopyName(name)); + } + } + } + + if (versionProperties != null && nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE)) + { + // Create the new version + this.versionService.createVersion(nodeRef, versionProperties); + } + + if (keepCheckedOut == false) + { + // Delete the working copy + behaviourFilter.disableBehaviour(workingCopyNodeRef, ContentModel.ASPECT_WORKING_COPY); + try + { + // Clean up original node + // Note: Lock has already been removed. So no lockService.unlock(nodeRef); + nodeService.removeAspect(nodeRef, ContentModel.ASPECT_CHECKED_OUT); - // Release the lock on the original node - this.lockService.unlock(nodeRef); - // Delete the working copy - this.nodeService.deleteNode(workingCopyNodeRef); - - // Invoke policy - invokeOnCancelCheckOut(nodeRef); + nodeService.deleteNode(workingCopyNodeRef); } finally { - behaviourFilter.enableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE); + // Just for symmetry; the node is gone + behaviourFilter.enableBehaviour(workingCopyNodeRef, ContentModel.ASPECT_WORKING_COPY); } } else { - // Error since the copy aspect is missing - throw new AspectMissingException(ContentModel.ASPECT_COPIEDFROM, workingCopyNodeRef); + // Re-lock the original node + lockService.lock(nodeRef, LockType.READ_ONLY_LOCK); + } + + // Invoke policy + invokeOnCheckIn(nodeRef); + + return nodeRef; + } + + @Override + public NodeRef cancelCheckout(NodeRef workingCopyNodeRef) + { + // Check that we have been handed a working copy + if (!nodeService.hasAspect(workingCopyNodeRef, ContentModel.ASPECT_WORKING_COPY)) + { + // Error since we have not been passed a working copy + throw new AspectMissingException(ContentModel.ASPECT_WORKING_COPY, workingCopyNodeRef); + } + + // Get the checked out node + NodeRef nodeRef = getCheckedOut(workingCopyNodeRef); + if (nodeRef == null) + { + // Error since the original node can not be found + throw new CheckOutCheckInServiceException(MSG_ERR_BAD_COPY); + } + + // Invoke policy + invokeBeforeCancelCheckOut(workingCopyNodeRef); + + behaviourFilter.disableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE); + behaviourFilter.disableBehaviour(workingCopyNodeRef, ContentModel.ASPECT_WORKING_COPY); + try + { + // Release the lock on the original node + lockService.unlock(nodeRef); + nodeService.removeAspect(nodeRef, ContentModel.ASPECT_CHECKED_OUT); + + // Delete the working copy + nodeService.deleteNode(workingCopyNodeRef); + + // Invoke policy + invokeOnCancelCheckOut(nodeRef); + } + catch (UnableToReleaseLockException exception) + { + throw new CheckOutCheckInServiceException(MSG_ERR_NOT_OWNER, exception); + } + finally + { + behaviourFilter.enableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE); } return nodeRef; } - /** - * @see org.alfresco.service.cmr.coci.CheckOutCheckInService#getWorkingCopy(org.alfresco.service.cmr.repository.NodeRef) - */ + @Override public NodeRef getWorkingCopy(NodeRef nodeRef) { NodeRef workingCopy = null; - - // Do a search to find the working copy document - ResultSet resultSet = null; - - try + if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_CHECKED_OUT)) { - resultSet = this.searchService.query( - nodeRef.getStoreRef(), - SearchService.LANGUAGE_LUCENE, - "+ASPECT:\"" + ContentModel.ASPECT_WORKING_COPY.toString() + "\" +@\\{http\\://www.alfresco.org/model/content/1.0\\}" + ContentModel.PROP_COPY_REFERENCE.getLocalName() + ":\"" + nodeRef.toString() + "\""); - if (resultSet.getNodeRefs().size() != 0) + List assocs = nodeService.getTargetAssocs(nodeRef, ContentModel.ASSOC_WORKING_COPY_LINK); + // It is a 1:1 relationship + if (assocs.size() > 0) { - workingCopy = resultSet.getNodeRef(0); - } - } - finally - { - if (resultSet != null) - { - resultSet.close(); + if (assocs.size() > 1) + { + logger.warn("Found multiple " + ContentModel.ASSOC_WORKING_COPY_LINK + " association from node: " + nodeRef); + } + workingCopy = assocs.get(0).getTargetRef(); } } return workingCopy; } + @Override + public NodeRef getCheckedOut(NodeRef nodeRef) + { + NodeRef original = null; + if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_WORKING_COPY)) + { + List assocs = nodeService.getSourceAssocs(nodeRef, ContentModel.ASSOC_WORKING_COPY_LINK); + // It is a 1:1 relationship + if (assocs.size() > 0) + { + if (assocs.size() > 1) + { + logger.warn("Found multiple " + ContentModel.ASSOC_WORKING_COPY_LINK + " associations to node: " + nodeRef); + } + original = assocs.get(0).getSourceRef(); + } + } + + return original; + } + + @Override + public boolean isWorkingCopy(NodeRef nodeRef) + { + return nodeService.hasAspect(nodeRef, ContentModel.ASPECT_WORKING_COPY); + } + + @Override + public boolean isCheckedOut(NodeRef nodeRef) + { + return nodeService.hasAspect(nodeRef, ContentModel.ASPECT_CHECKED_OUT); + } + /** * Create working copy name * * @param name name * @return working copy name */ - public String createWorkingCopyName(String name) + public static String createWorkingCopyName(String name) { - if (this.getWorkingCopyLabel() != null && this.getWorkingCopyLabel().length() != 0) + if (getWorkingCopyLabel() != null && getWorkingCopyLabel().length() != 0) { if (name != null && name.length() != 0) { @@ -851,7 +793,6 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService /** * Get original name from working copy name * - * @param workingCopyName * @return original name */ private String getNameFromWorkingCopyName(String workingCopyName) @@ -887,9 +828,8 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService * * @return the working copy label */ - public String getWorkingCopyLabel() + public static String getWorkingCopyLabel() { return I18NUtil.getMessage(MSG_WORKING_COPY_LABEL); } - } diff --git a/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImplTest.java b/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImplTest.java index 54901611c0..ed35f3d6e3 100644 --- a/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImplTest.java +++ b/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImplTest.java @@ -19,7 +19,9 @@ package org.alfresco.repo.coci; import java.io.Serializable; +import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.alfresco.model.ContentModel; @@ -30,6 +32,7 @@ import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransacti import org.alfresco.repo.version.VersionModel; import org.alfresco.service.cmr.coci.CheckOutCheckInService; import org.alfresco.service.cmr.lock.LockService; +import org.alfresco.service.cmr.repository.AssociationRef; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.ContentReader; @@ -78,6 +81,8 @@ public class CheckOutCheckInServiceImplTest extends BaseSpringTest private NodeRef rootNodeRef; private NodeRef nodeRef; private String userNodeRef; + private NodeRef folderNodeRef; + private NodeRef fileNodeRef; /** * Types and properties used by the tests @@ -106,7 +111,7 @@ public class CheckOutCheckInServiceImplTest extends BaseSpringTest { // Set the services this.nodeService = (NodeService)this.applicationContext.getBean("nodeService"); - this.cociService = (CheckOutCheckInService)this.applicationContext.getBean("checkOutCheckInService"); + this.cociService = (CheckOutCheckInService)this.applicationContext.getBean("CheckoutCheckinService"); this.contentService = (ContentService)this.applicationContext.getBean("contentService"); this.versionService = (VersionService)this.applicationContext.getBean("versionService"); this.authenticationService = (MutableAuthenticationService)this.applicationContext.getBean("authenticationService"); @@ -120,19 +125,19 @@ public class CheckOutCheckInServiceImplTest extends BaseSpringTest authenticationComponent.setSystemUserAsCurrentUser(); // Create the store and get the root node reference - this.storeRef = this.nodeService.createStore(StoreRef.PROTOCOL_WORKSPACE, "Test_" + System.currentTimeMillis()); - this.rootNodeRef = this.nodeService.getRootNode(storeRef); + this.storeRef = nodeService.createStore(StoreRef.PROTOCOL_WORKSPACE, "Test_" + System.currentTimeMillis()); + this.rootNodeRef = nodeService.getRootNode(storeRef); // Create the node used for tests - ChildAssociationRef childAssocRef = this.nodeService.createNode( + ChildAssociationRef childAssocRef = nodeService.createNode( rootNodeRef, ContentModel.ASSOC_CHILDREN, QName.createQName("test"), ContentModel.TYPE_CONTENT); this.nodeRef = childAssocRef.getChildRef(); - this.nodeService.addAspect(this.nodeRef, ContentModel.ASPECT_TITLED, null); - this.nodeService.setProperty(this.nodeRef, ContentModel.PROP_NAME, TEST_VALUE_NAME); - this.nodeService.setProperty(this.nodeRef, PROP2_QNAME, TEST_VALUE_2); + nodeService.addAspect(this.nodeRef, ContentModel.ASPECT_TITLED, null); + nodeService.setProperty(this.nodeRef, ContentModel.PROP_NAME, TEST_VALUE_NAME); + nodeService.setProperty(this.nodeRef, PROP2_QNAME, TEST_VALUE_2); // Add the initial content to the node ContentWriter contentWriter = this.contentService.getWriter(this.nodeRef, ContentModel.PROP_CONTENT, true); @@ -141,8 +146,8 @@ public class CheckOutCheckInServiceImplTest extends BaseSpringTest contentWriter.putContent(CONTENT_1); // Add the lock and version aspects to the created node - this.nodeService.addAspect(this.nodeRef, ContentModel.ASPECT_VERSIONABLE, null); - this.nodeService.addAspect(this.nodeRef, ContentModel.ASPECT_LOCKABLE, null); + nodeService.addAspect(this.nodeRef, ContentModel.ASPECT_VERSIONABLE, null); + nodeService.addAspect(this.nodeRef, ContentModel.ASPECT_LOCKABLE, null); // Create and authenticate the user this.userName = "cociTest" + GUID.generate(); @@ -152,6 +157,23 @@ public class CheckOutCheckInServiceImplTest extends BaseSpringTest permissionService.setPermission(this.rootNodeRef, this.userName, PermissionService.ALL_PERMISSIONS, true); permissionService.setPermission(this.nodeRef, this.userName, PermissionService.ALL_PERMISSIONS, true); + + folderNodeRef = nodeService.createNode( + rootNodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName("test"), + ContentModel.TYPE_FOLDER, + Collections.singletonMap(ContentModel.PROP_NAME, "folder")).getChildRef(); + fileNodeRef = nodeService.createNode( + folderNodeRef, + ContentModel.ASSOC_CONTAINS, + QName.createQName("test"), + ContentModel.TYPE_CONTENT, + Collections.singletonMap(ContentModel.PROP_NAME, "file")).getChildRef(); + contentWriter = this.contentService.getWriter(fileNodeRef, ContentModel.PROP_CONTENT, true); + contentWriter.setMimetype("text/plain"); + contentWriter.setEncoding("UTF-8"); + contentWriter.putContent(CONTENT_1); } /** @@ -181,7 +203,7 @@ public class CheckOutCheckInServiceImplTest extends BaseSpringTest private NodeRef checkout() { // Check out the node - NodeRef workingCopy = this.cociService.checkout( + NodeRef workingCopy = cociService.checkout( this.nodeRef, this.rootNodeRef, ContentModel.ASSOC_CHILDREN, @@ -191,16 +213,16 @@ public class CheckOutCheckInServiceImplTest extends BaseSpringTest //System.out.println(NodeStoreInspector.dumpNodeStore(this.nodeService, this.storeRef)); // Ensure that the working copy and copy aspect has been applied - assertTrue(this.nodeService.hasAspect(workingCopy, ContentModel.ASPECT_WORKING_COPY)); - assertTrue(this.nodeService.hasAspect(workingCopy, ContentModel.ASPECT_COPIEDFROM)); + assertTrue(nodeService.hasAspect(workingCopy, ContentModel.ASPECT_WORKING_COPY)); + assertTrue(nodeService.hasAspect(workingCopy, ContentModel.ASPECT_COPIEDFROM)); // Check that the working copy owner has been set correctly - assertEquals(this.userNodeRef, this.nodeService.getProperty(workingCopy, ContentModel.PROP_WORKING_COPY_OWNER)); + assertEquals(this.userNodeRef, nodeService.getProperty(workingCopy, ContentModel.PROP_WORKING_COPY_OWNER)); // Check that the working copy name has been set correctly - String name = (String)this.nodeService.getProperty(this.nodeRef, PROP_NAME_QNAME); - String workingCopyLabel = ((CheckOutCheckInServiceImpl)this.cociService).createWorkingCopyName(name); - String workingCopyName = (String)this.nodeService.getProperty(workingCopy, PROP_NAME_QNAME); + String name = (String)nodeService.getProperty(this.nodeRef, PROP_NAME_QNAME); + String workingCopyLabel = CheckOutCheckInServiceImpl.createWorkingCopyName(name); + String workingCopyName = (String)nodeService.getProperty(workingCopy, PROP_NAME_QNAME); assertEquals(workingCopyLabel, workingCopyName); // Ensure that the content has been copied correctly @@ -226,13 +248,13 @@ public class CheckOutCheckInServiceImplTest extends BaseSpringTest // Test standard check-in Map versionProperties = new HashMap(); versionProperties.put(Version.PROP_DESCRIPTION, "This is a test version"); - this.cociService.checkin(workingCopy, versionProperties); + cociService.checkin(workingCopy, versionProperties); // Test check-in with content NodeRef workingCopy3 = checkout(); - this.nodeService.setProperty(workingCopy3, PROP_NAME_QNAME, TEST_VALUE_2); - this.nodeService.setProperty(workingCopy3, PROP2_QNAME, TEST_VALUE_3); + nodeService.setProperty(workingCopy3, PROP_NAME_QNAME, TEST_VALUE_2); + nodeService.setProperty(workingCopy3, PROP2_QNAME, TEST_VALUE_3); ContentWriter tempWriter = this.contentService.getWriter(workingCopy3, ContentModel.PROP_CONTENT, false); assertNotNull(tempWriter); tempWriter.putContent(CONTENT_2); @@ -240,7 +262,7 @@ public class CheckOutCheckInServiceImplTest extends BaseSpringTest Map versionProperties3 = new HashMap(); versionProperties3.put(Version.PROP_DESCRIPTION, "description"); versionProperties3.put(VersionModel.PROP_VERSION_TYPE, VersionType.MAJOR); - NodeRef origNodeRef = this.cociService.checkin(workingCopy3, versionProperties3, contentUrl, true); + NodeRef origNodeRef = cociService.checkin(workingCopy3, versionProperties3, contentUrl, true); assertNotNull(origNodeRef); // Check the checked in content @@ -262,38 +284,53 @@ public class CheckOutCheckInServiceImplTest extends BaseSpringTest assertEquals(CONTENT_2, versionContentReader.getContentString()); // Check that the name is not updated during the check-in - assertEquals(TEST_VALUE_2, this.nodeService.getProperty(versionNodeRef, PROP_NAME_QNAME)); - assertEquals(TEST_VALUE_2, this.nodeService.getProperty(origNodeRef, PROP_NAME_QNAME)); + assertEquals(TEST_VALUE_2, nodeService.getProperty(versionNodeRef, PROP_NAME_QNAME)); + assertEquals(TEST_VALUE_2, nodeService.getProperty(origNodeRef, PROP_NAME_QNAME)); // Check that the other properties are updated during the check-in - assertEquals(TEST_VALUE_3, this.nodeService.getProperty(versionNodeRef, PROP2_QNAME)); - assertEquals(TEST_VALUE_3, this.nodeService.getProperty(origNodeRef, PROP2_QNAME)); + assertEquals(TEST_VALUE_3, nodeService.getProperty(versionNodeRef, PROP2_QNAME)); + assertEquals(TEST_VALUE_3, nodeService.getProperty(origNodeRef, PROP2_QNAME)); // Cancel the check out after is has been left checked out - this.cociService.cancelCheckout(workingCopy3); + cociService.cancelCheckout(workingCopy3); // Test keep checked out flag NodeRef workingCopy2 = checkout(); Map versionProperties2 = new HashMap(); versionProperties2.put(Version.PROP_DESCRIPTION, "Another version test"); - this.cociService.checkin(workingCopy2, versionProperties2, null, true); - this.cociService.checkin(workingCopy2, new HashMap(), null, true); + cociService.checkin(workingCopy2, versionProperties2, null, true); + cociService.checkin(workingCopy2, new HashMap(), null, true); } + public void testCheckInWithNameChange() + { + // Check out the file + NodeRef fileWorkingCopyNodeRef = cociService.checkout(fileNodeRef); + // Make sure we can get the checked out node + NodeRef fileWorkingCopyNodeRefCheck = cociService.getWorkingCopy(fileNodeRef); + assertEquals("Working copy not found ", fileWorkingCopyNodeRef, fileWorkingCopyNodeRefCheck); + + // Rename the working copy + nodeService.setProperty(fileWorkingCopyNodeRef, ContentModel.PROP_NAME, "renamed"); + + // Check in + cociService.checkin(fileWorkingCopyNodeRef, null); + } + public void testCheckOutCheckInWithTranslatableAspect() { // Create a node to be used as the translation - NodeRef translationNodeRef = this.nodeService.createNode( + NodeRef translationNodeRef = nodeService.createNode( rootNodeRef, ContentModel.ASSOC_CHILDREN, QName.createQName("translation"), ContentModel.TYPE_CONTENT).getChildRef(); - this.nodeService.addAspect(this.nodeRef, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "translatable"), null); - this.nodeService.createAssociation(this.nodeRef, translationNodeRef, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "translations")); + nodeService.addAspect(this.nodeRef, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "translatable"), null); + nodeService.createAssociation(this.nodeRef, translationNodeRef, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "translations")); // Check it out - NodeRef workingCopy = this.cociService.checkout( + NodeRef workingCopy = cociService.checkout( this.nodeRef, this.rootNodeRef, ContentModel.ASSOC_CHILDREN, @@ -303,7 +340,7 @@ public class CheckOutCheckInServiceImplTest extends BaseSpringTest // Check it back in again Map versionProperties = new HashMap(); versionProperties.put(Version.PROP_DESCRIPTION, "This is a test version"); - this.cociService.checkin(workingCopy, versionProperties); + cociService.checkin(workingCopy, versionProperties); } /** @@ -316,7 +353,7 @@ public class CheckOutCheckInServiceImplTest extends BaseSpringTest bagOfProps.put(ContentModel.PROP_CONTENT, new ContentData(null, MimetypeMap.MIMETYPE_TEXT_PLAIN, 0L, "UTF-8")); // Create a new node - ChildAssociationRef childAssocRef = this.nodeService.createNode( + ChildAssociationRef childAssocRef = nodeService.createNode( rootNodeRef, ContentModel.ASSOC_CHILDREN, QName.createQName("test"), @@ -325,8 +362,8 @@ public class CheckOutCheckInServiceImplTest extends BaseSpringTest NodeRef noVersionNodeRef = childAssocRef.getChildRef(); // Check out and check in - NodeRef workingCopy = this.cociService.checkout(noVersionNodeRef); - this.cociService.checkin(workingCopy, new HashMap()); + NodeRef workingCopy = cociService.checkout(noVersionNodeRef); + cociService.checkin(workingCopy, new HashMap()); // Check that the origional node has no version history dispite sending verion props assertNull(this.versionService.getVersionHistory(noVersionNodeRef)); @@ -350,7 +387,7 @@ public class CheckOutCheckInServiceImplTest extends BaseSpringTest // Good the origional is locked } - NodeRef origNodeRef = this.cociService.cancelCheckout(workingCopy); + NodeRef origNodeRef = cociService.cancelCheckout(workingCopy); assertEquals(this.nodeRef, origNodeRef); // The origional should no longer be locked @@ -358,7 +395,7 @@ public class CheckOutCheckInServiceImplTest extends BaseSpringTest } /** - * Test the deleting a wokring copy node removed the lock on the origional node + * Test the deleting a wokring copy node removed the lock on the original node */ public void testAutoCancelCheckOut() { @@ -368,38 +405,44 @@ public class CheckOutCheckInServiceImplTest extends BaseSpringTest try { this.lockService.checkForLock(this.nodeRef); - fail("The origional should be locked now."); + fail("The original should be locked now."); } catch (Throwable exception) { - // Good the origional is locked + // Good the original is locked } // Delete the working copy - this.nodeService.deleteNode(workingCopy); + nodeService.deleteNode(workingCopy); - // The origional should no longer be locked + // The original should no longer be locked this.lockService.checkForLock(this.nodeRef); } /** - * Test the getWorkingCopy method + * @see CheckOutCheckInService#getWorkingCopy(NodeRef) + * @see CheckOutCheckInService#getCheckedOut(NodeRef) */ - public void testGetWorkingCopy() + public void testBidirectionalReferences() { - NodeRef origNodeRef = this.nodeService.createNode( + final NodeRef origNodeRef = nodeService.createNode( this.rootNodeRef, ContentModel.ASSOC_CHILDREN, QName.createQName("test2"), ContentModel.TYPE_CONTENT).getChildRef(); - - NodeRef wk1 = this.cociService.getWorkingCopy(origNodeRef); + NodeRef wk1 = cociService.getWorkingCopy(origNodeRef); assertNull(wk1); // Check the document out - final NodeRef workingCopy = this.cociService.checkout(origNodeRef); + final NodeRef workingCopy = cociService.checkout(origNodeRef); + assertTrue("Expect cm:workingcopy aspect", nodeService.hasAspect(workingCopy, ContentModel.ASPECT_WORKING_COPY)); + assertTrue("Expect cm:checkedOut aspect", nodeService.hasAspect(origNodeRef, ContentModel.ASPECT_CHECKED_OUT)); + List targetAssocs = nodeService.getTargetAssocs(origNodeRef, ContentModel.ASSOC_WORKING_COPY_LINK); + assertEquals("Expect a 1:1 relationship", 1, targetAssocs.size()); + List sourceAssocs = nodeService.getSourceAssocs(workingCopy, ContentModel.ASSOC_WORKING_COPY_LINK); + assertEquals("Expect a 1:1 relationship", 1, sourceAssocs.size()); // Need to commit the transaction in order to get the indexer to run setComplete(); @@ -412,25 +455,27 @@ public class CheckOutCheckInServiceImplTest extends BaseSpringTest { public Object execute() { - NodeRef wk2 = CheckOutCheckInServiceImplTest.this.cociService.getWorkingCopy(finalNodeRef); + NodeRef wk2 = cociService.getWorkingCopy(finalNodeRef); assertNotNull(wk2); assertEquals(workingCopy, wk2); + NodeRef orig2 = cociService.getCheckedOut(wk2); + assertNotNull(orig2); + assertEquals(origNodeRef, orig2); - CheckOutCheckInServiceImplTest.this.cociService.cancelCheckout(workingCopy); + cociService.cancelCheckout(workingCopy); return null; } }); - NodeRef wk3 = this.cociService.getWorkingCopy(this.nodeRef); - assertNull(wk3); + NodeRef wk3 = cociService.getWorkingCopy(this.nodeRef); + assertNull(wk3); } - /** * Test the getWorkingCopy method */ public void testETWOTWO_733() { - NodeRef origNodeRef = this.nodeService.createNode( + NodeRef origNodeRef = nodeService.createNode( this.rootNodeRef, ContentModel.ASSOC_CHILDREN, QName.createQName("test2"), @@ -444,11 +489,11 @@ public class CheckOutCheckInServiceImplTest extends BaseSpringTest QName.createQName("test6"), false); - NodeRef wk1 = this.cociService.getWorkingCopy(origNodeRef); + NodeRef wk1 = cociService.getWorkingCopy(origNodeRef); assertNull(wk1); // Check the document out - final NodeRef workingCopy = this.cociService.checkout(origNodeRef); + final NodeRef workingCopy = cociService.checkout(origNodeRef); // Need to commit the transaction in order to get the indexer to run setComplete(); @@ -461,23 +506,23 @@ public class CheckOutCheckInServiceImplTest extends BaseSpringTest { public Object execute() { - NodeRef wk2 = CheckOutCheckInServiceImplTest.this.cociService.getWorkingCopy(finalNodeRef); + NodeRef wk2 = cociService.getWorkingCopy(finalNodeRef); assertNotNull(wk2); assertEquals(workingCopy, wk2); - CheckOutCheckInServiceImplTest.this.cociService.cancelCheckout(workingCopy); + cociService.cancelCheckout(workingCopy); return null; } }); - NodeRef wk3 = this.cociService.getWorkingCopy(this.nodeRef); + NodeRef wk3 = cociService.getWorkingCopy(this.nodeRef); assertNull(wk3); } public void testAR1056() { // Check out the node - NodeRef workingCopy = this.cociService.checkout( + NodeRef workingCopy = cociService.checkout( this.nodeRef, this.rootNodeRef, ContentModel.ASSOC_CHILDREN, @@ -487,7 +532,7 @@ public class CheckOutCheckInServiceImplTest extends BaseSpringTest // Try and check the same node out again try { - this.cociService.checkout( + cociService.checkout( this.nodeRef, this.rootNodeRef, ContentModel.ASSOC_CHILDREN, @@ -496,9 +541,8 @@ public class CheckOutCheckInServiceImplTest extends BaseSpringTest } catch (Exception exception) { - // Good because we shouldnt be able to checkout a document twice + // Good because we shouldn't be able to checkout a document twice } - } public void testMultipleCheckoutsCheckInsWithPropChange() @@ -506,7 +550,7 @@ public class CheckOutCheckInServiceImplTest extends BaseSpringTest // Note: this test assumes cm:autoVersionProps=true by default (refer to cm:versionableAspect in contentModel.xml) // Create a new node - ChildAssociationRef childAssocRef = this.nodeService.createNode( + ChildAssociationRef childAssocRef = nodeService.createNode( rootNodeRef, ContentModel.ASSOC_CHILDREN, QName.createQName("test"), @@ -515,7 +559,7 @@ public class CheckOutCheckInServiceImplTest extends BaseSpringTest final NodeRef testNodeRef = childAssocRef.getChildRef(); // Add the version aspect to the created node - this.nodeService.addAspect(testNodeRef, ContentModel.ASPECT_VERSIONABLE, null); + nodeService.addAspect(testNodeRef, ContentModel.ASPECT_VERSIONABLE, null); setComplete(); endTransaction(); @@ -598,9 +642,9 @@ public class CheckOutCheckInServiceImplTest extends BaseSpringTest String adminUser = AuthenticationUtil.getAdminUserName(); AuthenticationUtil.setFullyAuthenticatedUser(adminUser); - Serializable initModifieer = nodeService.getProperty(nodeRef, ContentModel.PROP_MODIFIER); - Serializable initModifieed = nodeService.getProperty(nodeRef, ContentModel.PROP_MODIFIED); - assertFalse("The initial modifier should not be Admin!", adminUser.equals(initModifieer)); + Serializable initModifier = nodeService.getProperty(nodeRef, ContentModel.PROP_MODIFIER); + Serializable initModified = nodeService.getProperty(nodeRef, ContentModel.PROP_MODIFIED); + assertFalse("The initial modifier should not be Admin!", adminUser.equals(initModifier)); NodeRef copy = cociService.checkout( nodeRef, @@ -608,16 +652,16 @@ public class CheckOutCheckInServiceImplTest extends BaseSpringTest ContentModel.ASSOC_CHILDREN, QName.createQName("workingCopy")); - Serializable modifieer = nodeService.getProperty(nodeRef, ContentModel.PROP_MODIFIER); - assertEquals("Checkout should not cause the modifier to change!", initModifieer, modifieer); - Serializable modifieed = nodeService.getProperty(nodeRef, ContentModel.PROP_MODIFIED); - assertEquals("Checkout should not cause the modified date to change!", initModifieed, modifieed); + Serializable modifier = nodeService.getProperty(nodeRef, ContentModel.PROP_MODIFIER); + assertEquals("Checkout should not cause the modifier to change!", initModifier, modifier); + Serializable modified = nodeService.getProperty(nodeRef, ContentModel.PROP_MODIFIED); + assertEquals("Checkout should not cause the modified date to change!", initModified, modified); cociService.cancelCheckout(copy); - modifieer = nodeService.getProperty(nodeRef, ContentModel.PROP_MODIFIER); - assertEquals("Checkout should not cause the modifier to change!", initModifieer, modifieer); - modifieed = nodeService.getProperty(nodeRef, ContentModel.PROP_MODIFIED); - assertEquals("Checkout should not cause the modified date to change!", initModifieed, modifieed); + modifier = nodeService.getProperty(nodeRef, ContentModel.PROP_MODIFIER); + assertEquals("Cancel checkout should not cause the modifier to change!", initModifier, modifier); + modified = nodeService.getProperty(nodeRef, ContentModel.PROP_MODIFIED); + assertEquals("Cancel checkout should not cause the modified date to change!", initModified, modified); copy = cociService.checkout( nodeRef, @@ -628,8 +672,8 @@ public class CheckOutCheckInServiceImplTest extends BaseSpringTest versionProperties.put(Version.PROP_DESCRIPTION, "This is a test version"); cociService.checkin(copy, versionProperties); - modifieer = nodeService.getProperty(nodeRef, ContentModel.PROP_MODIFIER); - assertEquals("The modifier should change to Admin after checkin!", adminUser, modifieer); + modifier = nodeService.getProperty(nodeRef, ContentModel.PROP_MODIFIER); + assertEquals("The modifier should change to Admin after checkin!", adminUser, modifier); } public void testCheckOutPermissions_ALF7680_ALF535() @@ -645,7 +689,7 @@ public class CheckOutCheckInServiceImplTest extends BaseSpringTest NodeRef node = createNodeWithPermission(folder1, userName, PermissionService.EDITOR); // Check out the node - NodeRef workingCopy = this.cociService.checkout( + NodeRef workingCopy = cociService.checkout( node, folder1, ContentModel.ASSOC_CHILDREN, @@ -653,10 +697,10 @@ public class CheckOutCheckInServiceImplTest extends BaseSpringTest // Ensure that the working copy was created and current user was set as owner assertNotNull(workingCopy); - assertTrue(this.nodeService.hasAspect(workingCopy, ContentModel.ASPECT_WORKING_COPY)); - assertEquals(this.userNodeRef, this.nodeService.getProperty(workingCopy, ContentModel.PROP_WORKING_COPY_OWNER)); + assertTrue(nodeService.hasAspect(workingCopy, ContentModel.ASPECT_WORKING_COPY)); + assertEquals(this.userNodeRef, nodeService.getProperty(workingCopy, ContentModel.PROP_WORKING_COPY_OWNER)); - this.cociService.cancelCheckout(workingCopy); + cociService.cancelCheckout(workingCopy); /* * Testing working copy creation in a different folder. @@ -668,7 +712,7 @@ public class CheckOutCheckInServiceImplTest extends BaseSpringTest NodeRef folder2 = createFolderWithPermission(rootNodeRef, userName, PermissionService.ALL_PERMISSIONS); // Check out the node - workingCopy = this.cociService.checkout( + workingCopy = cociService.checkout( node, folder2, ContentModel.ASSOC_CHILDREN, @@ -676,10 +720,10 @@ public class CheckOutCheckInServiceImplTest extends BaseSpringTest // Ensure that the working copy was created and current user was set as owner assertNotNull(workingCopy); - assertTrue(this.nodeService.hasAspect(workingCopy, ContentModel.ASPECT_WORKING_COPY)); - assertEquals(this.userNodeRef, this.nodeService.getProperty(workingCopy, ContentModel.PROP_WORKING_COPY_OWNER)); + assertTrue(nodeService.hasAspect(workingCopy, ContentModel.ASPECT_WORKING_COPY)); + assertEquals(this.userNodeRef, nodeService.getProperty(workingCopy, ContentModel.PROP_WORKING_COPY_OWNER)); - this.cociService.cancelCheckout(workingCopy); + cociService.cancelCheckout(workingCopy); /* * Testing working copy creation in a different folder. @@ -692,7 +736,7 @@ public class CheckOutCheckInServiceImplTest extends BaseSpringTest try { // Check out the node - workingCopy = this.cociService.checkout( + workingCopy = cociService.checkout( node, folder3, ContentModel.ASSOC_CHILDREN, @@ -717,7 +761,7 @@ public class CheckOutCheckInServiceImplTest extends BaseSpringTest try { // Check out the node - workingCopy = this.cociService.checkout( + workingCopy = cociService.checkout( node2, folder3, ContentModel.ASSOC_CHILDREN, @@ -739,7 +783,7 @@ public class CheckOutCheckInServiceImplTest extends BaseSpringTest authenticationComponent.setSystemUserAsCurrentUser(); // Create the folder - NodeRef folder = this.nodeService.createNode( + NodeRef folder = nodeService.createNode( parent, ContentModel.ASSOC_CHILDREN, QName.createQName("TestFolder" + GUID.generate()), diff --git a/source/java/org/alfresco/repo/coci/WorkingCopyAspect.java b/source/java/org/alfresco/repo/coci/WorkingCopyAspect.java index 403a40e115..846719efd8 100644 --- a/source/java/org/alfresco/repo/coci/WorkingCopyAspect.java +++ b/source/java/org/alfresco/repo/coci/WorkingCopyAspect.java @@ -28,42 +28,30 @@ import org.alfresco.repo.copy.CopyBehaviourCallback; import org.alfresco.repo.copy.CopyDetails; import org.alfresco.repo.copy.CopyServicePolicies; import org.alfresco.repo.copy.DefaultCopyBehaviourCallback; +import org.alfresco.repo.node.NodeServicePolicies; import org.alfresco.repo.policy.JavaBehaviour; import org.alfresco.repo.policy.PolicyComponent; +import org.alfresco.service.cmr.coci.CheckOutCheckInService; import org.alfresco.service.cmr.lock.LockService; -import org.alfresco.service.cmr.lock.LockStatus; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.util.GUID; public class WorkingCopyAspect implements CopyServicePolicies.OnCopyNodePolicy { - /** - * Policy component - */ private PolicyComponent policyComponent; - - /** - * The node service - */ private NodeService nodeService; - - /** - * The lock service - */ private LockService lockService; + private CheckOutCheckInService checkOutCheckInService; /** * The working copy aspect copy behaviour callback. - * */ + */ private WorkingCopyAspectCopyBehaviourCallback workingCopyAspectCopyBehaviourCallback = new WorkingCopyAspectCopyBehaviourCallback(); /** * Sets the policy component - * - * @param policyComponent the policy component */ public void setPolicyComponent(PolicyComponent policyComponent) { @@ -72,8 +60,6 @@ public class WorkingCopyAspect implements CopyServicePolicies.OnCopyNodePolicy /** * Set the node service - * - * @param nodeService the node service */ public void setNodeService(NodeService nodeService) { @@ -82,14 +68,20 @@ public class WorkingCopyAspect implements CopyServicePolicies.OnCopyNodePolicy /** * Set the lock service - * - * @param lockService the lock service */ public void setLockService(LockService lockService) { this.lockService = lockService; } - + + /** + * @param checkOutCheckInService the service dealing with working copies + */ + public void setCheckOutCheckInService(CheckOutCheckInService checkOutCheckInService) + { + this.checkOutCheckInService = checkOutCheckInService; + } + /** * Initialise method */ @@ -97,19 +89,24 @@ public class WorkingCopyAspect implements CopyServicePolicies.OnCopyNodePolicy { // Register copy behaviour for the working copy aspect this.policyComponent.bindClassBehaviour( - QName.createQName(NamespaceService.ALFRESCO_URI, "getCopyCallback"), + CopyServicePolicies.OnCopyNodePolicy.QNAME, ContentModel.TYPE_CMOBJECT, new JavaBehaviour(this, "getCopyCallback")); this.policyComponent.bindClassBehaviour( - QName.createQName(NamespaceService.ALFRESCO_URI, "getCopyCallback"), + CopyServicePolicies.OnCopyNodePolicy.QNAME, ContentModel.ASPECT_WORKING_COPY, new JavaBehaviour(this, "getCopyCallback")); + this.policyComponent.bindClassBehaviour( + CopyServicePolicies.OnCopyNodePolicy.QNAME, + ContentModel.ASPECT_CHECKED_OUT, + new JavaBehaviour(this, "getCopyCallback")); // register onBeforeDelete class behaviour for the working copy aspect this.policyComponent.bindClassBehaviour( - QName.createQName(NamespaceService.ALFRESCO_URI, "beforeDeleteNode"), + NodeServicePolicies.BeforeDeleteNodePolicy.QNAME, ContentModel.ASPECT_WORKING_COPY, - new JavaBehaviour(this, "beforeDeleteNode")); + new JavaBehaviour(this, "beforeDeleteWorkingCopy")); + // register onBeforeDelete class behaviour for the checked-out aspect } /** @@ -117,23 +114,13 @@ public class WorkingCopyAspect implements CopyServicePolicies.OnCopyNodePolicy * * @param nodeRef the node reference about to be deleted */ - public void beforeDeleteNode(NodeRef nodeRef) + public void beforeDeleteWorkingCopy(NodeRef nodeRef) { - // Prior to deleting a working copy the lock on the origional node should be released - // Note: we do not call cancelCheckOut since this will also attempt to delete the node is question - if (this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_WORKING_COPY) == true && - this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_COPIEDFROM) == true) + NodeRef checkedOutNodeRef = checkOutCheckInService.getCheckedOut(nodeRef); + if (checkedOutNodeRef != null) { - // Get the origional node - NodeRef origNodeRef = (NodeRef)this.nodeService.getProperty(nodeRef, ContentModel.PROP_COPY_REFERENCE); - if (origNodeRef != null) - { - if (this.lockService.getLockStatus(origNodeRef).equals(LockStatus.NO_LOCK) == false) - { - // Release the lock on the origional node - this.lockService.unlock(origNodeRef); - } - } + lockService.unlock(checkedOutNodeRef); + nodeService.removeAspect(checkedOutNodeRef, ContentModel.ASPECT_CHECKED_OUT); } } @@ -175,7 +162,9 @@ public class WorkingCopyAspect implements CopyServicePolicies.OnCopyNodePolicy * Prevents copying off the {@link ContentModel#PROP_NAME cm:name} property. */ @Override - public Map getCopyProperties(QName classQName, CopyDetails copyDetails, + public Map getCopyProperties( + QName classQName, + CopyDetails copyDetails, Map properties) { if (classQName.equals(ContentModel.ASPECT_WORKING_COPY)) @@ -187,13 +176,12 @@ public class WorkingCopyAspect implements CopyServicePolicies.OnCopyNodePolicy // Generate a new name for a new copy of a working copy String newName = null; - // This is a copy of a working copy to a new node (not a check in). Try to derive a new name from the - // node it is checked out from - if (copyDetails.isTargetNodeIsNew() && copyDetails.getSourceNodeAspectQNames().contains(ContentModel.ASPECT_COPIEDFROM)) + if (copyDetails.isTargetNodeIsNew()) { - NodeRef checkedOutFrom = (NodeRef) copyDetails.getSourceNodeProperties().get( - ContentModel.PROP_COPY_REFERENCE); - if (nodeService.exists(checkedOutFrom)) + // This is a copy of a working copy to a new node (not a check in). Try to derive a new name from the + // node it is checked out from + NodeRef checkedOutFrom = checkOutCheckInService.getCheckedOut(copyDetails.getSourceNodeRef()); + if (checkedOutFrom != null) { String oldName = (String) nodeService.getProperty(checkedOutFrom, ContentModel.PROP_NAME); int extIndex = oldName.lastIndexOf('.'); @@ -201,6 +189,10 @@ public class WorkingCopyAspect implements CopyServicePolicies.OnCopyNodePolicy + "_" + GUID.generate() + oldName.substring(extIndex); } } + else + { + // This is a check-in i.e. a copy to an existing node, so keep a null cm:name + } if (newName == null) { diff --git a/source/java/org/alfresco/repo/copy/CopyServiceImpl.java b/source/java/org/alfresco/repo/copy/CopyServiceImpl.java index 529f9b0f4d..758a585019 100644 --- a/source/java/org/alfresco/repo/copy/CopyServiceImpl.java +++ b/source/java/org/alfresco/repo/copy/CopyServiceImpl.java @@ -32,7 +32,6 @@ import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.query.CannedQuery; import org.alfresco.query.CannedQueryFactory; -import org.alfresco.query.CannedQueryPageDetails; import org.alfresco.query.CannedQueryParameters; import org.alfresco.query.PagingRequest; import org.alfresco.query.PagingResults; @@ -43,6 +42,7 @@ import org.alfresco.repo.copy.CopyBehaviourCallback.ChildAssocCopyAction; import org.alfresco.repo.copy.CopyBehaviourCallback.ChildAssocRecurseAction; import org.alfresco.repo.copy.CopyBehaviourCallback.CopyAssociationDetails; import org.alfresco.repo.copy.CopyBehaviourCallback.CopyChildAssociationDetails; +import org.alfresco.repo.copy.query.AbstractCopyCannedQueryFactory; import org.alfresco.repo.policy.ClassPolicyDelegate; import org.alfresco.repo.policy.JavaBehaviour; import org.alfresco.repo.policy.PolicyComponent; @@ -61,8 +61,6 @@ import org.alfresco.service.cmr.repository.CopyServiceException; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.rule.RuleService; -import org.alfresco.service.cmr.search.ResultSet; -import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.cmr.security.AccessPermission; import org.alfresco.service.cmr.security.AccessStatus; import org.alfresco.service.cmr.security.PermissionService; @@ -90,7 +88,6 @@ public class CopyServiceImpl implements CopyService /* Query names */ private static final String QUERY_FACTORY_GET_COPIES = "getCopiesCannedQueryFactory"; - private static final String QUERY_FACTORY_GET_COPIED = "getCopiesCannedQueryFactory"; /* I18N labels */ private static final String COPY_OF_LABEL = "copy_service.copy_of_label"; @@ -100,7 +97,6 @@ public class CopyServiceImpl implements CopyService private NodeService internalNodeService; private NamedObjectRegistry> cannedQueryRegistry; private DictionaryService dictionaryService; - private SearchService searchService; private PolicyComponent policyComponent; private RuleService ruleService; private PermissionService permissionService; @@ -148,14 +144,6 @@ public class CopyServiceImpl implements CopyService this.policyComponent = policyComponent; } - /** - * @param searchService the search service - */ - public void setSearchService(SearchService searchService) - { - this.searchService = searchService; - } - /** * @param ruleService the rule service */ @@ -192,15 +180,15 @@ public class CopyServiceImpl implements CopyService // Register policy behaviours this.policyComponent.bindClassBehaviour( - QName.createQName(NamespaceService.ALFRESCO_URI, "getCopyCallback"), + CopyServicePolicies.OnCopyNodePolicy.QNAME, ContentModel.ASPECT_COPIEDFROM, new JavaBehaviour(this, "getCallbackForCopiedFromAspect")); this.policyComponent.bindClassBehaviour( - QName.createQName(NamespaceService.ALFRESCO_URI, "getCopyCallback"), + CopyServicePolicies.OnCopyNodePolicy.QNAME, ContentModel.TYPE_FOLDER, new JavaBehaviour(this, "getCallbackForFolderType")); this.policyComponent.bindClassBehaviour( - QName.createQName(NamespaceService.ALFRESCO_URI, "getCopyCallback"), + CopyServicePolicies.OnCopyNodePolicy.QNAME, ContentModel.ASPECT_OWNABLE, new JavaBehaviour(this, "getCallbackForOwnableAspect")); } @@ -376,34 +364,40 @@ public class CopyServiceImpl implements CopyService invokeCopyComplete(sourceNodeRef, targetNodeRef, false, copiedNodeRefs); } + @Override + public NodeRef getOriginal(NodeRef nodeRef) + { + List assocs = internalNodeService.getTargetAssocs(nodeRef, ContentModel.ASSOC_ORIGINAL); + if (assocs.size() > 1) + { + logger.warn("Multiple cm:orignal associations from node: " + nodeRef); + } + if (assocs.size() == 0) + { + return null; + } + else + { + return assocs.get(0).getTargetRef(); + } + } + @Override public List getCopies(NodeRef nodeRef) { - List copies = new ArrayList(); - - // Do a search to find the origional document - ResultSet resultSet = null; - try + PagingRequest pagingRequest = new PagingRequest(1000); + PagingResults page = getCopies(nodeRef, pagingRequest); + if (page.hasMoreItems()) { - resultSet = this.searchService.query( - nodeRef.getStoreRef(), - SearchService.LANGUAGE_LUCENE, - "+@\\{http\\://www.alfresco.org/model/content/1.0\\}" + ContentModel.PROP_COPY_REFERENCE.getLocalName() + ":\"" + nodeRef.toString() + "\""); - - for (NodeRef copy : resultSet.getNodeRefs()) - { - copies.add(copy); - } + logger.warn("Trimmed page size for deprecated getCopies() call."); } - finally + List pageResults = page.getPage(); + List results = new ArrayList(pageResults.size()); + for (CopyInfo copyInfo : pageResults) { - if (resultSet != null) - { - resultSet.close(); - } + results.add(copyInfo.getNodeRef()); } - - return copies; + return results; } @Override @@ -411,9 +405,9 @@ public class CopyServiceImpl implements CopyService { CannedQueryFactory queryFactory = cannedQueryRegistry.getNamedObject(QUERY_FACTORY_GET_COPIES); CannedQueryParameters params = new CannedQueryParameters( - originalNodeRef, - new CannedQueryPageDetails(pagingRequest), - null); + new AbstractCopyCannedQueryFactory.CopyCannedQueryDetail(originalNodeRef), + null, + pagingRequest); CannedQuery query = queryFactory.getCannedQuery(params); return query.execute(); } @@ -421,14 +415,14 @@ public class CopyServiceImpl implements CopyService @Override public PagingResults getCopies( NodeRef originalNodeRef, - NodeRef copyParentNodeRef, Set copyNodeAspectsToIgnore, + NodeRef copyParentNodeRef, PagingRequest pagingRequest) { CannedQueryFactory queryFactory = cannedQueryRegistry.getNamedObject(QUERY_FACTORY_GET_COPIES); CannedQueryParameters params = new CannedQueryParameters( - originalNodeRef, - new CannedQueryPageDetails(pagingRequest), - null); + new AbstractCopyCannedQueryFactory.CopyCannedQueryDetail(originalNodeRef, copyParentNodeRef), + null, + pagingRequest); CannedQuery query = queryFactory.getCannedQuery(params); return query.execute(); } @@ -593,10 +587,16 @@ public class CopyServiceImpl implements CopyService // Copy residual properties copyResidualProperties(copyDetails, copyTarget); - // Apply the copy aspect to the new node - Map copyProperties = new HashMap(); - copyProperties.put(ContentModel.PROP_COPY_REFERENCE, sourceNodeRef); - internalNodeService.addAspect(copyTarget, ContentModel.ASPECT_COPIEDFROM, copyProperties); + // Link the new node to the original, but ensure that we only keep track of the last copy + List originalAssocs = internalNodeService.getTargetAssocs(copyTarget, ContentModel.ASSOC_ORIGINAL); + for (AssociationRef originalAssoc : originalAssocs) + { + internalNodeService.removeAssociation( + originalAssoc.getSourceRef(), + originalAssoc.getTargetRef(), + ContentModel.ASSOC_ORIGINAL); + } + internalNodeService.createAssociation(copyTarget, sourceNodeRef, ContentModel.ASSOC_ORIGINAL); // Copy permissions copyPermissions(sourceNodeRef, copyTarget); @@ -1389,5 +1389,5 @@ public class CopyServiceImpl implements CopyService public CopyBehaviourCallback getCallbackForOwnableAspect(QName classRef, CopyDetails copyDetails) { return DoNothingCopyBehaviourCallback.getInstance(); - } + } } diff --git a/source/java/org/alfresco/repo/copy/CopyServiceImplTest.java b/source/java/org/alfresco/repo/copy/CopyServiceImplTest.java index 3df2a7d5e4..ae5f1d25fa 100644 --- a/source/java/org/alfresco/repo/copy/CopyServiceImplTest.java +++ b/source/java/org/alfresco/repo/copy/CopyServiceImplTest.java @@ -43,6 +43,7 @@ import org.alfresco.repo.dictionary.M2ChildAssociation; import org.alfresco.repo.dictionary.M2Model; import org.alfresco.repo.dictionary.M2Property; import org.alfresco.repo.dictionary.M2Type; +import org.alfresco.repo.node.integrity.IntegrityChecker; import org.alfresco.repo.rule.RuleModel; import org.alfresco.repo.security.authentication.AuthenticationComponent; import org.alfresco.repo.security.authentication.AuthenticationUtil; @@ -77,6 +78,7 @@ import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.RegexQNamePattern; import org.alfresco.service.transaction.TransactionService; import org.alfresco.util.ApplicationContextHelper; +import org.alfresco.util.Pair; import org.alfresco.util.PropertyMap; import org.springframework.context.ApplicationContext; import org.springframework.extensions.surf.util.I18NUtil; @@ -477,14 +479,14 @@ public class CopyServiceImplTest extends TestCase /** * Test copy new node within store */ - public void DISABLED_testCopyToNewNode() + public void testCopyToNewNode() { PagingRequest pageRequest = new PagingRequest(10); PagingResults copies = null; // Check that the node has no copies copies = copyService.getCopies(sourceNodeRef, pageRequest); - assertEquals("Incorrect number of copies", 1, copies.getPage().size()); + assertEquals("Incorrect number of copies", 0, copies.getPage().size()); // Copy to new node without copying children NodeRef copy = copyService.copy( @@ -535,7 +537,87 @@ public class CopyServiceImplTest extends TestCase //System.out.println( // NodeStoreInspector.dumpNodeStore(nodeService, storeRef)); - } + } + + public void testCopiedFromAspect() + { + IntegrityChecker integrityChecker = (IntegrityChecker) ctx.getBean("integrityChecker"); + + // Create the node used for copying + ChildAssociationRef childAssocRef = nodeService.createNode( + rootNodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName("{test}test"), + TEST_TYPE_QNAME, + createTypePropertyBag()); + NodeRef nodeRef = childAssocRef.getChildRef(); + + PagingRequest pageRequest = new PagingRequest(10); + pageRequest.setRequestTotalCountMax(200); + PagingResults copies = null; + + NodeRef firstCopy = null; + + for (int i = 1; i <= 100; i++) + { + NodeRef copyNodeRef = copyService.copy( + nodeRef, + rootNodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName("{test}copyAssoc")); + if (firstCopy == null) + { + firstCopy = copyNodeRef; + } + copies = copyService.getCopies(nodeRef, pageRequest); + assertEquals("Total count not correct", new Pair(i, i), copies.getTotalResultCount()); + assertEquals("Incorrect number of copies", (i > 10 ? 10 : i), copies.getPage().size()); + + // Since the results are paged, make sure that we have the correct results while we only have a page + boolean found = (i > 10) ? true : false; + for (CopyInfo copy : copies.getPage()) + { + if (found) // Might not be checking if we are over a page + { + break; + } + if (copy.getNodeRef().equals(copyNodeRef)) + { + found = true; + } + } + assertTrue("Did not find the copy in the list of copies.", found); + + // Run integrity checks to ensure that commit has a chance + integrityChecker.checkIntegrity(); + + // Now query for copies in current parent location + copies = copyService.getCopies(nodeRef, rootNodeRef, pageRequest); + assertEquals("Total count not correct", new Pair(i, i), copies.getTotalResultCount()); + assertEquals("Incorrect number of copies", (i > 10 ? 10 : i), copies.getPage().size()); + + // Check that the original node can be retrieved + NodeRef originalCheck = copyService.getOriginal(copyNodeRef); + assertEquals("Original is not as expected. ", nodeRef, originalCheck); + + // Check that the parent node can be included + copies = copyService.getCopies(nodeRef, rootNodeRef, pageRequest); + assertEquals("Total count not correct", new Pair(i, i), copies.getTotalResultCount()); + assertEquals("Incorrect number of copies", (i > 10 ? 10 : i), copies.getPage().size()); + + // And query against some other parent node + copies = copyService.getCopies(nodeRef, sourceNodeRef, pageRequest); // Some arbitrary parent + assertEquals("Expected to find no copies", 0, copies.getPage().size()); + } + + // Should be able to delete the original + nodeService.deleteNode(nodeRef); + // Run integrity checks to ensure that commit has a chance + integrityChecker.checkIntegrity(); + // Should be no original + NodeRef originalCheck = copyService.getOriginal(firstCopy); + assertNull("Original should not be present. ", originalCheck); + } public void testCopyNodeWithRules() { @@ -1069,9 +1151,10 @@ public class CopyServiceImplTest extends TestCase // Check that the copy aspect has been applied to the copy boolean hasCopyAspect = nodeService.hasAspect(destinationNodeRef, ContentModel.ASPECT_COPIEDFROM); assertTrue("Missing aspect: " + ContentModel.ASPECT_COPIEDFROM, hasCopyAspect); - NodeRef copyNodeRef = (NodeRef)nodeService.getProperty(destinationNodeRef, ContentModel.PROP_COPY_REFERENCE); - assertNotNull(copyNodeRef); - assertEquals(sourceNodeRef, copyNodeRef); + List assocs = nodeService.getTargetAssocs(destinationNodeRef, ContentModel.ASSOC_ORIGINAL); + assertEquals("Expectd exactly one reference back to original", 1, assocs.size()); + NodeRef checkSourceNodeRef = assocs.get(0).getTargetRef(); + assertEquals("Copy refers to incorrect original source", sourceNodeRef, checkSourceNodeRef); } else { diff --git a/source/java/org/alfresco/repo/copy/CrossRepositoryCopyServiceImpl.java b/source/java/org/alfresco/repo/copy/CrossRepositoryCopyServiceImpl.java index 077d5bef52..e8ac05b54e 100644 --- a/source/java/org/alfresco/repo/copy/CrossRepositoryCopyServiceImpl.java +++ b/source/java/org/alfresco/repo/copy/CrossRepositoryCopyServiceImpl.java @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2005-2010 Alfresco Software Limited. * * This file is part of Alfresco @@ -14,392 +14,396 @@ * 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.copy; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.Serializable; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.model.ContentModel; -import org.alfresco.repo.avm.AVMNodeConverter; -import org.alfresco.service.cmr.avm.AVMNodeDescriptor; -import org.alfresco.service.cmr.avm.AVMService; -import org.alfresco.service.cmr.dictionary.DictionaryService; -import org.alfresco.service.cmr.model.FileFolderService; -import org.alfresco.service.cmr.model.FileInfo; -import org.alfresco.service.cmr.repository.ChildAssociationRef; -import org.alfresco.service.cmr.repository.ContentData; -import org.alfresco.service.cmr.repository.ContentReader; -import org.alfresco.service.cmr.repository.ContentService; -import org.alfresco.service.cmr.repository.ContentWriter; -import org.alfresco.service.cmr.repository.CopyService; -import org.alfresco.service.cmr.repository.CrossRepositoryCopyService; -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.namespace.QName; -import org.alfresco.util.Pair; - -/** - * Cross Repository Copying. - * - * @author britt - */ -public class CrossRepositoryCopyServiceImpl implements CrossRepositoryCopyService -{ - /** - * The NodeService reference. - */ - private NodeService fNodeService; - - /** - * The FileFolderService reference. - */ - private FileFolderService fFileFolderService; - - /** - * The regular CopyService reference. - */ - private CopyService fCopyService; - - /** - * The AVMService. - */ - private AVMService fAVMService; - - /** - * The ContentService. - */ - private ContentService fContentService; - - /** - * The DictionaryService. - */ - private DictionaryService fDictionaryService; - - /** - * A default constructor. - */ - public CrossRepositoryCopyServiceImpl() - { - } - - // Setters for Spring. - - public void setAvmService(AVMService service) - { - fAVMService = service; - } - - public void setContentService(ContentService service) - { - fContentService = service; - } - - public void setCopyService(CopyService service) - { - fCopyService = service; - } - - public void setDictionaryService(DictionaryService service) - { - fDictionaryService = service; - } - - public void setFileFolderService(FileFolderService service) - { - fFileFolderService = service; - } - - public void setNodeService(NodeService service) - { - fNodeService = service; - } - - /** - * This copies recursively src, which may be a container or a content type to dst, which must be a container. Copied - * nodes will have the copied from aspect applied to them. - * - * @param src - * The node to copy. - * @param dst - * The container to copy it into. - * @param name - * The name to give the copy. - */ - public void copy(NodeRef src, NodeRef dst, String name) - { - StoreRef srcStoreRef = src.getStoreRef(); - StoreRef dstStoreRef = dst.getStoreRef(); - if (srcStoreRef.getProtocol().equals(StoreRef.PROTOCOL_AVM)) - { - if (dstStoreRef.getProtocol().equals(StoreRef.PROTOCOL_AVM)) - { - copyAVMToAVM(src, dst, name); - } - else if (dstStoreRef.getProtocol().equals(StoreRef.PROTOCOL_WORKSPACE)) - { - copyAVMToRepo(src, dst, name); - } - } - else if (srcStoreRef.getProtocol().equals(StoreRef.PROTOCOL_WORKSPACE)) - { - if (dstStoreRef.getProtocol().equals(StoreRef.PROTOCOL_AVM)) - { - copyRepoToAVM(src, dst, name); - } - else if (dstStoreRef.getProtocol().equals(StoreRef.PROTOCOL_WORKSPACE)) - { - copyRepoToRepo(src, dst, name); - } - } - } - - /** - * Handle copying from AVM to AVM - * - * @param src - * Source node. - * @param dst - * Destination directory node. - * @param name - * Name to give copy. - */ - private void copyAVMToAVM(NodeRef src, NodeRef dst, String name) - { - Pair srcStorePath = AVMNodeConverter.ToAVMVersionPath(src); - Pair dstStorePath = AVMNodeConverter.ToAVMVersionPath(dst); - fAVMService.copy(srcStorePath.getFirst(), srcStorePath.getSecond(), dstStorePath.getSecond(), name); - } - - /** - * Handle copying from AVM to Repo. - * - * @param src - * Source node. - * @param dst - * Destination Container. - * @param name - * The name to give the copy. - */ - private void copyAVMToRepo(NodeRef src, NodeRef dst, String name) - { - Pair versionPath = AVMNodeConverter.ToAVMVersionPath(src); - AVMNodeDescriptor desc = fAVMService.lookup(versionPath.getFirst(), versionPath.getSecond()); - NodeRef existing = fFileFolderService.searchSimple(dst, name); - if (desc.isFile()) - { - if (existing != null && !fNodeService.getType(existing).equals(ContentModel.TYPE_CONTENT)) - { - fFileFolderService.delete(existing); - existing = null; - } - NodeRef childRef = null; - if (existing == null) - { - childRef = fFileFolderService.create(dst, name, ContentModel.TYPE_CONTENT).getNodeRef(); - } - else - { - childRef = existing; - } - InputStream in = fAVMService.getFileInputStream(desc); - ContentData cd = fAVMService.getContentDataForRead(versionPath.getFirst(), desc.getPath()); - ContentWriter writer = fContentService.getWriter(childRef, ContentModel.PROP_CONTENT, true); - writer.setEncoding(cd.getEncoding()); - writer.setMimetype(cd.getMimetype()); - OutputStream out = writer.getContentOutputStream(); - copyData(in, out); - copyPropsAndAspectsAVMToRepo(src, childRef); - } - else - { - if (existing != null && !fNodeService.getType(existing).equals(ContentModel.TYPE_FOLDER)) - { - fFileFolderService.delete(existing); - existing = null; - } - NodeRef childRef = null; - if (existing == null) - { - childRef = fFileFolderService.create(dst, name, ContentModel.TYPE_FOLDER).getNodeRef(); - } - else - { - childRef = existing; - } - copyPropsAndAspectsAVMToRepo(src, childRef); - Map listing = fAVMService.getDirectoryListing(desc); - for (Map.Entry entry : listing.entrySet()) - { - NodeRef srcChild = AVMNodeConverter.ToNodeRef(versionPath.getFirst(), entry.getValue().getPath()); - copyAVMToRepo(srcChild, childRef, entry.getKey()); - } - } - } - - /** - * Helper that copies aspects and properties. - * - * @param src - * The source AVM node. - * @param dst - * The destination Repo node. - */ - private void copyPropsAndAspectsAVMToRepo(NodeRef src, NodeRef dst) - { - Map props = fNodeService.getProperties(src); - fNodeService.setProperties(dst, props); - Set aspects = fNodeService.getAspects(src); - Map empty = new HashMap(); - for (QName aspect : aspects) - { - fNodeService.addAspect(dst, aspect, empty); - } - if (!fNodeService.hasAspect(dst, ContentModel.ASPECT_COPIEDFROM)) - { - empty.put(ContentModel.PROP_COPY_REFERENCE, src); - fNodeService.addAspect(dst, ContentModel.ASPECT_COPIEDFROM, empty); - } - else - { - fNodeService.setProperty(dst, ContentModel.PROP_COPY_REFERENCE, src); - } - } - - /** - * Handle copying from Repo to AVM. - * - * @param src - * The source node. - * @param dst - * The destingation directory. - * @param name - * The name to give the copy. - */ - private void copyRepoToAVM(NodeRef src, NodeRef dst, String name) - { - QName srcType = fNodeService.getType(src); - Pair versionPath = AVMNodeConverter.ToAVMVersionPath(dst); - String childPath = AVMNodeConverter.ExtendAVMPath(versionPath.getSecond(), name); - NodeRef childNodeRef = AVMNodeConverter.ToNodeRef(-1, childPath); - if (fDictionaryService.isSubClass(srcType, ContentModel.TYPE_CONTENT)) - { - ContentReader reader = fContentService.getReader(src, ContentModel.PROP_CONTENT); - InputStream in = reader.getContentInputStream(); - AVMNodeDescriptor desc = fAVMService.lookup(-1, childPath); - if (desc != null && !desc.isFile()) - { - fAVMService.removeNode(childPath); - desc = null; - } - if (desc == null) - { - try - { - fAVMService.createFile(versionPath.getSecond(), name).close(); - } - catch (IOException e) - { - throw new AlfrescoRuntimeException("I/O Error.", e); - } - } - ContentWriter writer = fAVMService.getContentWriter(childPath, true); - writer.setEncoding(reader.getEncoding()); - writer.setMimetype(reader.getMimetype()); - OutputStream out = writer.getContentOutputStream(); - copyData(in, out); - copyPropsAndAspectsRepoToAVM(src, childNodeRef, childPath); - return; - } - if (fDictionaryService.isSubClass(srcType, ContentModel.TYPE_FOLDER)) - { - AVMNodeDescriptor desc = fAVMService.lookup(-1, childPath); - if (desc != null && !desc.isDirectory()) - { - fAVMService.removeNode(childPath); - desc = null; - } - if (desc == null) - { - fAVMService.createDirectory(versionPath.getSecond(), name); - } - copyPropsAndAspectsRepoToAVM(src, childNodeRef, childPath); - List listing = fFileFolderService.list(src); - for (FileInfo info : listing) - { - copyRepoToAVM(info.getNodeRef(), childNodeRef, info.getName()); - } - return; - } - } - - /** - * Helper to copy properties and aspects. - * - * @param src - * The source node. - * @param dst - * The destination node. - * @param dstPath - * The destination AVM path. - */ - private void copyPropsAndAspectsRepoToAVM(NodeRef src, NodeRef dst, String dstPath) - { - Map props = fNodeService.getProperties(src); - fNodeService.setProperties(dst, props); - Set aspects = fNodeService.getAspects(src); - for (QName aspect : aspects) - { - fAVMService.addAspect(dstPath, aspect); - } - if (!fAVMService.hasAspect(-1, dstPath, ContentModel.ASPECT_COPIEDFROM)) - { - fAVMService.addAspect(dstPath, ContentModel.ASPECT_COPIEDFROM); - } - fNodeService.setProperty(dst, ContentModel.PROP_COPY_REFERENCE, src); - } - - /** - * Handle copying from Repo to Repo. - * - * @param src - * The source node. - * @param dst - * The destination container. - * @param name - * The name to give the copy. - */ - private void copyRepoToRepo(NodeRef src, NodeRef dst, String name) - { - ChildAssociationRef assocRef = fNodeService.getPrimaryParent(src); - fCopyService.copyAndRename(src, dst, ContentModel.ASSOC_CONTAINS, assocRef.getQName(), true); - } - - private void copyData(InputStream in, OutputStream out) - { - try - { - byte[] buff = new byte[8192]; - int read = 0; - while ((read = in.read(buff)) != -1) - { - out.write(buff, 0, read); - } - in.close(); - out.close(); - } - catch (IOException e) - { - throw new AlfrescoRuntimeException("I/O Error.", e); - } - } -} + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.copy; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Serializable; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.repo.avm.AVMNodeConverter; +import org.alfresco.service.cmr.avm.AVMNodeDescriptor; +import org.alfresco.service.cmr.avm.AVMService; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.model.FileFolderService; +import org.alfresco.service.cmr.model.FileInfo; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.ContentData; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.CopyService; +import org.alfresco.service.cmr.repository.CrossRepositoryCopyService; +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.namespace.QName; +import org.alfresco.util.Pair; + +/** + * Cross Repository Copying. + * + * @author britt + */ +public class CrossRepositoryCopyServiceImpl implements CrossRepositoryCopyService +{ + /** + * The NodeService reference. + */ + private NodeService fNodeService; + + /** + * The FileFolderService reference. + */ + private FileFolderService fFileFolderService; + + /** + * The regular CopyService reference. + */ + private CopyService fCopyService; + + /** + * The AVMService. + */ + private AVMService fAVMService; + + /** + * The ContentService. + */ + private ContentService fContentService; + + /** + * The DictionaryService. + */ + private DictionaryService fDictionaryService; + + /** + * A default constructor. + */ + public CrossRepositoryCopyServiceImpl() + { + } + + // Setters for Spring. + + public void setAvmService(AVMService service) + { + fAVMService = service; + } + + public void setContentService(ContentService service) + { + fContentService = service; + } + + public void setCopyService(CopyService service) + { + fCopyService = service; + } + + public void setDictionaryService(DictionaryService service) + { + fDictionaryService = service; + } + + public void setFileFolderService(FileFolderService service) + { + fFileFolderService = service; + } + + public void setNodeService(NodeService service) + { + fNodeService = service; + } + + /** + * This copies recursively src, which may be a container or a content type to dst, which must be a container. Copied + * nodes will have the copied from aspect applied to them. + * + * @param src + * The node to copy. + * @param dst + * The container to copy it into. + * @param name + * The name to give the copy. + */ + public void copy(NodeRef src, NodeRef dst, String name) + { + StoreRef srcStoreRef = src.getStoreRef(); + StoreRef dstStoreRef = dst.getStoreRef(); + if (srcStoreRef.getProtocol().equals(StoreRef.PROTOCOL_AVM)) + { + if (dstStoreRef.getProtocol().equals(StoreRef.PROTOCOL_AVM)) + { + copyAVMToAVM(src, dst, name); + } + else if (dstStoreRef.getProtocol().equals(StoreRef.PROTOCOL_WORKSPACE)) + { + copyAVMToRepo(src, dst, name); + } + } + else if (srcStoreRef.getProtocol().equals(StoreRef.PROTOCOL_WORKSPACE)) + { + if (dstStoreRef.getProtocol().equals(StoreRef.PROTOCOL_AVM)) + { + copyRepoToAVM(src, dst, name); + } + else if (dstStoreRef.getProtocol().equals(StoreRef.PROTOCOL_WORKSPACE)) + { + copyRepoToRepo(src, dst, name); + } + } + } + + /** + * Handle copying from AVM to AVM + * + * @param src + * Source node. + * @param dst + * Destination directory node. + * @param name + * Name to give copy. + */ + private void copyAVMToAVM(NodeRef src, NodeRef dst, String name) + { + Pair srcStorePath = AVMNodeConverter.ToAVMVersionPath(src); + Pair dstStorePath = AVMNodeConverter.ToAVMVersionPath(dst); + fAVMService.copy(srcStorePath.getFirst(), srcStorePath.getSecond(), dstStorePath.getSecond(), name); + } + + /** + * Handle copying from AVM to Repo. + * + * @param src + * Source node. + * @param dst + * Destination Container. + * @param name + * The name to give the copy. + */ + private void copyAVMToRepo(NodeRef src, NodeRef dst, String name) + { + Pair versionPath = AVMNodeConverter.ToAVMVersionPath(src); + AVMNodeDescriptor desc = fAVMService.lookup(versionPath.getFirst(), versionPath.getSecond()); + NodeRef existing = fFileFolderService.searchSimple(dst, name); + if (desc.isFile()) + { + if (existing != null && !fNodeService.getType(existing).equals(ContentModel.TYPE_CONTENT)) + { + fFileFolderService.delete(existing); + existing = null; + } + NodeRef childRef = null; + if (existing == null) + { + childRef = fFileFolderService.create(dst, name, ContentModel.TYPE_CONTENT).getNodeRef(); + } + else + { + childRef = existing; + } + InputStream in = fAVMService.getFileInputStream(desc); + ContentData cd = fAVMService.getContentDataForRead(versionPath.getFirst(), desc.getPath()); + ContentWriter writer = fContentService.getWriter(childRef, ContentModel.PROP_CONTENT, true); + writer.setEncoding(cd.getEncoding()); + writer.setMimetype(cd.getMimetype()); + OutputStream out = writer.getContentOutputStream(); + copyData(in, out); + copyPropsAndAspectsAVMToRepo(src, childRef); + } + else + { + if (existing != null && !fNodeService.getType(existing).equals(ContentModel.TYPE_FOLDER)) + { + fFileFolderService.delete(existing); + existing = null; + } + NodeRef childRef = null; + if (existing == null) + { + childRef = fFileFolderService.create(dst, name, ContentModel.TYPE_FOLDER).getNodeRef(); + } + else + { + childRef = existing; + } + copyPropsAndAspectsAVMToRepo(src, childRef); + Map listing = fAVMService.getDirectoryListing(desc); + for (Map.Entry entry : listing.entrySet()) + { + NodeRef srcChild = AVMNodeConverter.ToNodeRef(versionPath.getFirst(), entry.getValue().getPath()); + copyAVMToRepo(srcChild, childRef, entry.getKey()); + } + } + } + + /** + * Helper that copies aspects and properties. + * + * @param src + * The source AVM node. + * @param dst + * The destination Repo node. + */ + private void copyPropsAndAspectsAVMToRepo(NodeRef src, NodeRef dst) + { + Map props = fNodeService.getProperties(src); + fNodeService.setProperties(dst, props); + Set aspects = fNodeService.getAspects(src); + Map empty = new HashMap(); + for (QName aspect : aspects) + { + fNodeService.addAspect(dst, aspect, empty); + } +// 4.0: Derek Hulley: The cm:copiedFrom aspect is not that important and +// AVM doesn't support associations +// if (!fNodeService.hasAspect(dst, ContentModel.ASPECT_COPIEDFROM)) +// { +// empty.put(ContentModel.PROP_COPY_REFERENCE, src); +// fNodeService.addAspect(dst, ContentModel.ASPECT_COPIEDFROM, empty); +// } +// else +// { +// fNodeService.setProperty(dst, ContentModel.PROP_COPY_REFERENCE, src); +// } + } + + /** + * Handle copying from Repo to AVM. + * + * @param src + * The source node. + * @param dst + * The destingation directory. + * @param name + * The name to give the copy. + */ + private void copyRepoToAVM(NodeRef src, NodeRef dst, String name) + { + QName srcType = fNodeService.getType(src); + Pair versionPath = AVMNodeConverter.ToAVMVersionPath(dst); + String childPath = AVMNodeConverter.ExtendAVMPath(versionPath.getSecond(), name); + NodeRef childNodeRef = AVMNodeConverter.ToNodeRef(-1, childPath); + if (fDictionaryService.isSubClass(srcType, ContentModel.TYPE_CONTENT)) + { + ContentReader reader = fContentService.getReader(src, ContentModel.PROP_CONTENT); + InputStream in = reader.getContentInputStream(); + AVMNodeDescriptor desc = fAVMService.lookup(-1, childPath); + if (desc != null && !desc.isFile()) + { + fAVMService.removeNode(childPath); + desc = null; + } + if (desc == null) + { + try + { + fAVMService.createFile(versionPath.getSecond(), name).close(); + } + catch (IOException e) + { + throw new AlfrescoRuntimeException("I/O Error.", e); + } + } + ContentWriter writer = fAVMService.getContentWriter(childPath, true); + writer.setEncoding(reader.getEncoding()); + writer.setMimetype(reader.getMimetype()); + OutputStream out = writer.getContentOutputStream(); + copyData(in, out); + copyPropsAndAspectsRepoToAVM(src, childNodeRef, childPath); + return; + } + if (fDictionaryService.isSubClass(srcType, ContentModel.TYPE_FOLDER)) + { + AVMNodeDescriptor desc = fAVMService.lookup(-1, childPath); + if (desc != null && !desc.isDirectory()) + { + fAVMService.removeNode(childPath); + desc = null; + } + if (desc == null) + { + fAVMService.createDirectory(versionPath.getSecond(), name); + } + copyPropsAndAspectsRepoToAVM(src, childNodeRef, childPath); + List listing = fFileFolderService.list(src); + for (FileInfo info : listing) + { + copyRepoToAVM(info.getNodeRef(), childNodeRef, info.getName()); + } + return; + } + } + + /** + * Helper to copy properties and aspects. + * + * @param src + * The source node. + * @param dst + * The destination node. + * @param dstPath + * The destination AVM path. + */ + private void copyPropsAndAspectsRepoToAVM(NodeRef src, NodeRef dst, String dstPath) + { + Map props = fNodeService.getProperties(src); + fNodeService.setProperties(dst, props); + Set aspects = fNodeService.getAspects(src); + for (QName aspect : aspects) + { + fAVMService.addAspect(dstPath, aspect); + } +// 4.0: Derek Hulley: The cm:copiedFrom aspect is not that important and +// AVM doesn't support associations +// if (!fAVMService.hasAspect(-1, dstPath, ContentModel.ASPECT_COPIEDFROM)) +// { +// fAVMService.addAspect(dstPath, ContentModel.ASPECT_COPIEDFROM); +// } +// fNodeService.setProperty(dst, ContentModel.PROP_COPY_REFERENCE, src); + } + + /** + * Handle copying from Repo to Repo. + * + * @param src + * The source node. + * @param dst + * The destination container. + * @param name + * The name to give the copy. + */ + private void copyRepoToRepo(NodeRef src, NodeRef dst, String name) + { + ChildAssociationRef assocRef = fNodeService.getPrimaryParent(src); + fCopyService.copyAndRename(src, dst, ContentModel.ASSOC_CONTAINS, assocRef.getQName(), true); + } + + private void copyData(InputStream in, OutputStream out) + { + try + { + byte[] buff = new byte[8192]; + int read = 0; + while ((read = in.read(buff)) != -1) + { + out.write(buff, 0, read); + } + in.close(); + out.close(); + } + catch (IOException e) + { + throw new AlfrescoRuntimeException("I/O Error.", e); + } + } +} diff --git a/source/java/org/alfresco/repo/copy/query/AbstractCopyCannedQueryFactory.java b/source/java/org/alfresco/repo/copy/query/AbstractCopyCannedQueryFactory.java index 18ff92568e..f554e83b07 100644 --- a/source/java/org/alfresco/repo/copy/query/AbstractCopyCannedQueryFactory.java +++ b/source/java/org/alfresco/repo/copy/query/AbstractCopyCannedQueryFactory.java @@ -19,9 +19,13 @@ package org.alfresco.repo.copy.query; import org.alfresco.query.AbstractCannedQueryFactory; +import org.alfresco.query.CannedQueryParameters; import org.alfresco.repo.domain.node.NodeDAO; import org.alfresco.repo.domain.qname.QNameDAO; import org.alfresco.repo.domain.query.CannedQueryDAO; +import org.alfresco.repo.security.permissions.impl.acegi.MethodSecurityBean; +import org.alfresco.service.cmr.repository.CopyService.CopyInfo; +import org.alfresco.service.cmr.repository.NodeRef; /** * Support for Canned Queries for copy @@ -34,6 +38,7 @@ public abstract class AbstractCopyCannedQueryFactory extends AbstractCannedQu protected NodeDAO nodeDAO; protected QNameDAO qnameDAO; protected CannedQueryDAO cannedQueryDAO; + protected MethodSecurityBean methodSecurity; public void setNodeDAO(NodeDAO nodeDAO) { @@ -47,4 +52,56 @@ public abstract class AbstractCopyCannedQueryFactory extends AbstractCannedQu { this.cannedQueryDAO = cannedQueryDAO; } + + public void setMethodSecurity(MethodSecurityBean methodSecurity) + { + this.methodSecurity = methodSecurity; + } + + /** + * Parameter bean to use for copy queries + * + * @author Derek Hulley + * @since 4.0 + */ + public static class CopyCannedQueryDetail + { + /*package*/ final NodeRef originalNodeRef; + /*package*/ final NodeRef copyParentNodeRef; + /** + * @param originalNodeRef the original node + */ + public CopyCannedQueryDetail(NodeRef originalNodeRef) + { + this(originalNodeRef, null); + } + /** + * @param originalNodeRef the original node + * @param copyParentNodeRef the copied node's primary parent (optional) + * @param copyNodeAspectsToIgnore aspects on the copied node that effectively hide it + * (null or empty allowed) + */ + public CopyCannedQueryDetail(NodeRef originalNodeRef, NodeRef copyParentNodeRef) + { + super(); + if (originalNodeRef == null) + { + throw new IllegalArgumentException("Must supply an originalNodeRef"); + } + this.originalNodeRef = originalNodeRef; + this.copyParentNodeRef = copyParentNodeRef; + } + } + + protected CopyCannedQueryDetail getDetail(CannedQueryParameters parameters) + { + try + { + return (CopyCannedQueryDetail) parameters.getParameterBean(); + } + catch (ClassCastException e) + { + throw new IllegalArgumentException("Expected " + CopyCannedQueryDetail.class); + } + } } \ No newline at end of file diff --git a/source/java/org/alfresco/repo/copy/query/CopyEntity.java b/source/java/org/alfresco/repo/copy/query/CopyEntity.java new file mode 100644 index 0000000000..ab40faa2c5 --- /dev/null +++ b/source/java/org/alfresco/repo/copy/query/CopyEntity.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2005-2010 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.copy.query; + +import org.alfresco.repo.domain.node.NodeEntity; + +/** + * Bean class to data about copied nodes + * + * @author Derek Hulley + * @since 4.0 + */ +public class CopyEntity +{ + private NodeEntity copy; + private String copyName; + + public NodeEntity getCopy() + { + return copy; + } + public void setCopy(NodeEntity copy) + { + this.copy = copy; + } + public String getCopyName() + { + return copyName; + } + public void setCopyName(String copyName) + { + this.copyName = copyName; + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/copy/query/CopyParametersEntity.java b/source/java/org/alfresco/repo/copy/query/CopyParametersEntity.java index 5478cb650a..ddc9b3c974 100644 --- a/source/java/org/alfresco/repo/copy/query/CopyParametersEntity.java +++ b/source/java/org/alfresco/repo/copy/query/CopyParametersEntity.java @@ -29,7 +29,8 @@ import java.util.List; public class CopyParametersEntity { private Long originalNodeId; - private Long copiedFromAssocTypeId; + private Long originalAssocTypeId; + private Long namePropId; private Long copyParentNodeId; private List copyAspectIdsToIgnore; @@ -41,13 +42,21 @@ public class CopyParametersEntity { this.originalNodeId = originalNodeId; } - public Long getCopiedFromAssocTypeId() + public Long getOriginalAssocTypeId() { - return copiedFromAssocTypeId; + return originalAssocTypeId; } - public void setCopiedFromAssocTypeId(Long copiedFromAssocTypeId) + public void setOriginalAssocTypeId(Long originalAssocTypeId) { - this.copiedFromAssocTypeId = copiedFromAssocTypeId; + this.originalAssocTypeId = originalAssocTypeId; + } + public Long getNamePropId() + { + return namePropId; + } + public void setNamePropId(Long namePropId) + { + this.namePropId = namePropId; } public Long getCopyParentNodeId() { @@ -65,4 +74,8 @@ public class CopyParametersEntity { this.copyAspectIdsToIgnore = copyAspectIdsToIgnore; } + public boolean getTrue() + { + return true; + } } \ No newline at end of file diff --git a/source/java/org/alfresco/repo/copy/query/GetCopiesCannedQueryFactory.java b/source/java/org/alfresco/repo/copy/query/GetCopiesCannedQueryFactory.java index c4a117d8c5..bdf7c6c056 100644 --- a/source/java/org/alfresco/repo/copy/query/GetCopiesCannedQueryFactory.java +++ b/source/java/org/alfresco/repo/copy/query/GetCopiesCannedQueryFactory.java @@ -18,10 +18,20 @@ */ package org.alfresco.repo.copy.query; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.alfresco.model.ContentModel; import org.alfresco.query.CannedQuery; import org.alfresco.query.CannedQueryParameters; +import org.alfresco.repo.security.permissions.impl.acegi.AbstractCannedQueryPermissions; +import org.alfresco.repo.security.permissions.impl.acegi.MethodSecurityBean; import org.alfresco.service.cmr.repository.CopyService; import org.alfresco.service.cmr.repository.CopyService.CopyInfo; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.Pair; /** * Factory producing queries for the {@link CopyService} @@ -34,6 +44,75 @@ public class GetCopiesCannedQueryFactory extends AbstractCopyCannedQueryFactory< @Override public CannedQuery getCannedQuery(CannedQueryParameters parameters) { - throw new UnsupportedOperationException(); + return new GetCopiesCannedQuery(parameters, methodSecurity); + } + + /** + * Query to find nodes copied from a given node, optionally filtering out + * based on specific values. + * + * @author Derek Hulley + * @since 4.0 + */ + private class GetCopiesCannedQuery extends AbstractCannedQueryPermissions + { + private GetCopiesCannedQuery(CannedQueryParameters parameters, MethodSecurityBean methodSecurity) + { + super(parameters, methodSecurity); + } + + @Override + protected List queryAndFilter(CannedQueryParameters parameters) + { + CopyCannedQueryDetail detail = GetCopiesCannedQueryFactory.this.getDetail(parameters); + // Build parameters + CopyParametersEntity queryParameters = new CopyParametersEntity(); + // Original node + Pair originalNodePair = nodeDAO.getNodePair(detail.originalNodeRef); + if (originalNodePair == null) + { + return Collections.emptyList(); // Shortcut + } + queryParameters.setOriginalNodeId(originalNodePair.getFirst()); + // cm:original association type ID + Pair assocTypeQNamePair = qnameDAO.getQName(ContentModel.ASSOC_ORIGINAL); + if (assocTypeQNamePair == null) + { + return Collections.emptyList(); // Shortcut + } + queryParameters.setOriginalAssocTypeId(assocTypeQNamePair.getFirst()); + // cm:name property ID + Pair propQNamePair = qnameDAO.getQName(ContentModel.PROP_NAME); + if (propQNamePair == null) + { + return Collections.emptyList(); // Shortcut + } + queryParameters.setNamePropId(propQNamePair.getFirst()); + // Copied parent node + if (detail.copyParentNodeRef != null) + { + Pair copyParentNodePair = nodeDAO.getNodePair(detail.copyParentNodeRef); + if (copyParentNodePair == null) + { + return Collections.emptyList(); // Shortcut + } + queryParameters.setCopyParentNodeId(copyParentNodePair.getFirst()); + } + // Now query + int resultsRequired = parameters.getResultsRequired(); + List copies = cannedQueryDAO.executeQuery( + "alfresco.query.copy", "select_GetCopies", + queryParameters, + 0, resultsRequired); + // Convert them + List results = new ArrayList(copies.size()); + for (CopyEntity copy : copies) + { + CopyInfo result = new CopyInfo(copy.getCopy().getNodeRef(), copy.getCopyName()); + results.add(result); + } + // Done + return results; + } } } \ No newline at end of file diff --git a/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java b/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java index 8996c57620..bc6730c6e0 100644 --- a/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java +++ b/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java @@ -2309,16 +2309,19 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO return deleteCount > 0; } - public void getNodesWithAspect(QName aspectQName, Long minNodeId, int count, NodeRefQueryCallback resultsCallback) + public void getNodesWithAspects( + Set aspectQNames, + Long minNodeId, Long maxNodeId, + NodeRefQueryCallback resultsCallback) { - Pair qnamePair = qnameDAO.getQName(aspectQName); - if (qnamePair == null) + Set qnameIdsSet = qnameDAO.convertQNamesToIds(aspectQNames, false); + if (qnameIdsSet.size() == 0) { // No point running a query return; } - Long qnameId = qnamePair.getFirst(); - selectNodesWithAspect(qnameId, minNodeId, resultsCallback); + List qnameIds = new ArrayList(qnameIdsSet); + selectNodesWithAspects(qnameIds, minNodeId, maxNodeId, resultsCallback); } /** @@ -3684,7 +3687,10 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO protected abstract Set selectNodeAspectIds(Long nodeId); protected abstract void insertNodeAspect(Long nodeId, Long qnameId); protected abstract int deleteNodeAspects(Long nodeId, Set qnameIds); - protected abstract void selectNodesWithAspect(Long qnameId, Long minNodeId, NodeRefQueryCallback resultsCallback); + protected abstract void selectNodesWithAspects( + List qnameIds, + Long minNodeId, Long maxNodeId, + NodeRefQueryCallback resultsCallback); protected abstract Long insertNodeAssoc(Long sourceNodeId, Long targetNodeId, Long assocTypeQNameId, int assocIndex); protected abstract int updateNodeAssoc(Long id, int assocIndex); protected abstract int deleteNodeAssoc(Long sourceNodeId, Long targetNodeId, Long assocTypeQNameId); diff --git a/source/java/org/alfresco/repo/domain/node/NodeDAO.java b/source/java/org/alfresco/repo/domain/node/NodeDAO.java index 537302468c..229d143fe7 100644 --- a/source/java/org/alfresco/repo/domain/node/NodeDAO.java +++ b/source/java/org/alfresco/repo/domain/node/NodeDAO.java @@ -246,7 +246,18 @@ public interface NodeDAO extends NodeBulkLoader public boolean removeNodeAspects(Long nodeId, Set aspectQNames); - public void getNodesWithAspect(QName aspectQName, Long minNodeId, int count, NodeRefQueryCallback resultsCallback); + /** + * Get nodes with aspects between the given ranges + * + * @param aspectQNames the aspects that must be on the nodes + * @param minNodeId the minimum node ID (inclusive) + * @param maxNodeId the maximum node ID (exclusive) + * @param resultsCallback callback to process results + */ + public void getNodesWithAspects( + Set aspectQNames, + Long minNodeId, Long maxNodeId, + NodeRefQueryCallback resultsCallback); /* * Node Assocs diff --git a/source/java/org/alfresco/repo/domain/node/NodeDAOTest.java b/source/java/org/alfresco/repo/domain/node/NodeDAOTest.java index 6bd3b5fc71..816a73a886 100644 --- a/source/java/org/alfresco/repo/domain/node/NodeDAOTest.java +++ b/source/java/org/alfresco/repo/domain/node/NodeDAOTest.java @@ -18,6 +18,7 @@ */ package org.alfresco.repo.domain.node; +import java.util.Collections; import java.util.List; import junit.framework.TestCase; @@ -139,7 +140,10 @@ public class NodeDAOTest extends TestCase return false; } }; - nodeDAO.getNodesWithAspect(ContentModel.ASPECT_AUDITABLE, 1L, 5, callback); + nodeDAO.getNodesWithAspects( + Collections.singleton(ContentModel.ASPECT_AUDITABLE), + 1L, 1000L, + callback); } public void testGetPrimaryChildAcls() throws Throwable diff --git a/source/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java b/source/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java index 47e9ac0578..2ab3900368 100644 --- a/source/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java +++ b/source/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java @@ -96,7 +96,7 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl private static final String INSERT_NODE_ASPECT = "alfresco.node.insert.insert_NodeAspect"; private static final String DELETE_NODE_ASPECTS = "alfresco.node.delete_NodeAspects"; private static final String DELETE_NODE_PROPERTIES = "alfresco.node.delete_NodeProperties"; - private static final String SELECT_NODES_WITH_ASPECT_ID = "alfresco.node.select_NodesWithAspectId"; + private static final String SELECT_NODES_WITH_ASPECT_IDS = "alfresco.node.select_NodesWithAspectIds"; private static final String INSERT_NODE_ASSOC = "alfresco.node.insert.insert_NodeAssoc"; private static final String UPDATE_NODE_ASSOC = "alfresco.node.update_NodeAssoc"; private static final String DELETE_NODE_ASSOC = "alfresco.node.delete_NodeAssoc"; @@ -629,7 +629,10 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl } @Override - protected void selectNodesWithAspect(Long qnameId, Long minNodeId, final NodeRefQueryCallback resultsCallback) + protected void selectNodesWithAspects( + List qnameIds, + Long minNodeId, Long maxNodeId, + final NodeRefQueryCallback resultsCallback) { ResultHandler resultHandler = new ResultHandler() { @@ -641,10 +644,11 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl } }; - Map parameters = new HashMap(5); - parameters.put("minNodeId", minNodeId); - parameters.put("qnameId", qnameId); - template.select(SELECT_NODES_WITH_ASPECT_ID, parameters,resultHandler); + IdsEntity parameters = new IdsEntity(); + parameters.setIdOne(minNodeId); + parameters.setIdTwo(maxNodeId); + parameters.setIds(qnameIds); + template.select(SELECT_NODES_WITH_ASPECT_IDS, parameters, resultHandler); } @Override diff --git a/source/java/org/alfresco/repo/domain/patch/PatchDAO.java b/source/java/org/alfresco/repo/domain/patch/PatchDAO.java index bd0a7c973c..392d66f8df 100644 --- a/source/java/org/alfresco/repo/domain/patch/PatchDAO.java +++ b/source/java/org/alfresco/repo/domain/patch/PatchDAO.java @@ -20,6 +20,7 @@ package org.alfresco.repo.domain.patch; import java.util.List; import java.util.Map; +import java.util.Set; import org.alfresco.repo.domain.avm.AVMNodeEntity; import org.alfresco.repo.domain.contentdata.ContentDataDAO; @@ -215,33 +216,32 @@ public interface PatchDAO /** * Get shared acls with inheritance issues - * @return */ public List> getSharedAclsThatDoNotInheritCorrectlyFromThePrimaryParent(); /** * Get defining acls with inheritance issues - * @return */ public List> getDefiningAclsThatDoNotInheritCorrectlyFromThePrimaryParent(); /** * Get acls that do not inherit from the primary parent. - * @return */ public List> getAclsThatInheritFromNonPrimaryParent(); /** * Get acls that inherit with inheritance unset - * @return */ public List> getAclsThatInheritWithInheritanceUnset(); /** * Get shared acls that do not inherit correctly from the defining acl - * @return */ public List> getSharedAclsThatDoNotInheritCorrectlyFromTheirDefiningAcl(); - + /** + * @param qnames the qnames to search for + * @return Returns a count of the number of nodes that have either of the aspects + */ + public long getCountNodesWithAspects(Set qnames); } diff --git a/source/java/org/alfresco/repo/domain/patch/ibatis/PatchDAOImpl.java b/source/java/org/alfresco/repo/domain/patch/ibatis/PatchDAOImpl.java index e113085e6b..4f95f18723 100644 --- a/source/java/org/alfresco/repo/domain/patch/ibatis/PatchDAOImpl.java +++ b/source/java/org/alfresco/repo/domain/patch/ibatis/PatchDAOImpl.java @@ -23,6 +23,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import org.alfresco.ibatis.IdsEntity; import org.alfresco.model.ContentModel; @@ -101,6 +102,8 @@ public class PatchDAOImpl extends AbstractPatchDAOImpl private static final String SELECT_SHARED_ACLS_THAT_DO_NOT_INHERIT_CORRECTLY_FROM_THE_PRIMARY_PARENT = "alfresco.patch.select_sharedAclsThatDoNotInheritCorrectlyFromThePrimaryParent"; private static final String SELECT_SHARED_ACLS_THAT_DO_NOT_INHERIT_CORRECTLY_FROM_THEIR_DEFINING_ACL = "alfresco.patch.select_sharedAclsThatDoNotInheritCorrectlyFromTheirDefiningAcl"; + private static final String SELECT_COUNT_NODES_WITH_ASPECTS = "alfresco.patch.select_CountNodesWithAspectIds"; + private LocaleDAO localeDAO; protected SqlSessionTemplate template; @@ -628,4 +631,26 @@ public class PatchDAOImpl extends AbstractPatchDAOImpl Boolean.TRUE); return rows; } + + @Override + public long getCountNodesWithAspects(Set qnames) + { + // Resolve QNames + Set qnameIds = qnameDAO.convertQNamesToIds(qnames, false); + if (qnameIds.size() == 0) + { + return 0L; + } + IdsEntity params = new IdsEntity(); + params.setIds(new ArrayList(qnameIds)); + Long count = (Long) template.selectOne(SELECT_COUNT_NODES_WITH_ASPECTS, params); + if (count == null) + { + return 0L; + } + else + { + return count; + } + } } diff --git a/source/java/org/alfresco/repo/lock/LockServiceImpl.java b/source/java/org/alfresco/repo/lock/LockServiceImpl.java index 5e75a67b20..50c9146c5d 100644 --- a/source/java/org/alfresco/repo/lock/LockServiceImpl.java +++ b/source/java/org/alfresco/repo/lock/LockServiceImpl.java @@ -56,8 +56,6 @@ 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.AuthenticationService; -import org.alfresco.service.cmr.security.OwnableService; -import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; /** @@ -73,100 +71,36 @@ public class LockServiceImpl implements LockService, VersionServicePolicies.BeforeCreateVersionPolicy, VersionServicePolicies.OnCreateVersionPolicy { - /** - * The node service - */ - private NodeService nodeService; - - /** - * The tenant service - */ - private TenantService tenantService; - - /** - * The policy component - */ - private PolicyComponent policyComponent; - /** Key to the nodes ref's to ignore when checking for locks */ private static final String KEY_IGNORE_NODES = "lockService.ignoreNodes"; - /** - * The authentication service - */ + private NodeService nodeService; + private TenantService tenantService; private AuthenticationService authenticationService; - - /** - * The ownable service - * - */ - private OwnableService ownableService; - - /** - * The search service - */ private SearchService searchService; - /** - * Set the node service - * - * @param nodeService - * the node service - */ + private PolicyComponent policyComponent; + public void setNodeService(NodeService nodeService) { this.nodeService = nodeService; } - /** - * Set the tenant service - * - * @param tenantService - * the tenant service - */ public void setTenantService(TenantService tenantService) { this.tenantService = tenantService; } - /** - * Sets the policy component - * - * @param policyComponent - * the policy componentO - */ public void setPolicyComponent(PolicyComponent policyComponent) { this.policyComponent = policyComponent; } - /** - * Sets the authentication service - * - * @param authenticationService - * the authentication service - */ public void setAuthenticationService(AuthenticationService authenticationService) { this.authenticationService = authenticationService; } - /** - * Sets the ownable service - * - * @param ownableService - * the ownable service - */ - public void setOwnableService(OwnableService ownableService) - { - this.ownableService = ownableService; - } - - /** - * Set the search service - * - * @param searchService the search service - */ public void setSearchService(SearchService searchService) { this.searchService = searchService; @@ -353,19 +287,25 @@ public class LockServiceImpl implements LockService, public void unlock(NodeRef nodeRef) throws UnableToReleaseLockException { nodeRef = tenantService.getName(nodeRef); - - // Check for lock aspect - checkForLockApsect(nodeRef); - - addToIgnoreSet(nodeRef); - try + if (!nodeService.hasAspect(nodeRef, ContentModel.ASPECT_LOCKABLE)) { - // Clear the lock - this.nodeService.removeAspect(nodeRef, ContentModel.ASPECT_LOCKABLE); + // Nothing to unlock } - finally + else { - removeFromIgnoreSet(nodeRef); + // Check for lock aspect + checkForLockApsect(nodeRef); + + addToIgnoreSet(nodeRef); + try + { + // Clear the lock + this.nodeService.removeAspect(nodeRef, ContentModel.ASPECT_LOCKABLE); + } + finally + { + removeFromIgnoreSet(nodeRef); + } } } diff --git a/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java b/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java index 8eb4ec7644..80bfa7789a 100644 --- a/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java +++ b/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java @@ -41,6 +41,7 @@ import org.alfresco.query.PagingResults; import org.alfresco.repo.node.getchildren.GetChildrenCannedQuery; import org.alfresco.repo.node.getchildren.GetChildrenCannedQueryFactory; import org.alfresco.repo.search.QueryParameterDefImpl; +import org.alfresco.repo.security.permissions.PermissionCheckedCollection.PermissionCheckedCollectionMixin; import org.alfresco.repo.security.permissions.PermissionCheckedValue.PermissionCheckedValueMixin; import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.dictionary.DictionaryService; @@ -386,6 +387,7 @@ public class FileFolderServiceImpl implements FileFolderService { nodeInfos.add(toFileInfo(nodeRef, true)); } + PermissionCheckedCollectionMixin.create(nodeInfos, nodeRefs); return new PagingResults() { diff --git a/source/java/org/alfresco/repo/rule/RuleServiceCoverageTest.java b/source/java/org/alfresco/repo/rule/RuleServiceCoverageTest.java index fcbb51ebd9..f2d4b5cb2b 100644 --- a/source/java/org/alfresco/repo/rule/RuleServiceCoverageTest.java +++ b/source/java/org/alfresco/repo/rule/RuleServiceCoverageTest.java @@ -902,6 +902,8 @@ public class RuleServiceCoverageTest extends TestCase */ public void testCopyAction() { + String localName = getName() + System.currentTimeMillis(); + Map params = new HashMap(1); params.put(MoveActionExecuter.PARAM_DESTINATION_FOLDER, this.rootNodeRef); @@ -917,7 +919,7 @@ public class RuleServiceCoverageTest extends TestCase NodeRef newNodeRef = this.nodeService.createNode( this.nodeRef, ContentModel.ASSOC_CHILDREN, - QName.createQName(TEST_NAMESPACE, "origional"), + QName.createQName(TEST_NAMESPACE, localName), ContentModel.TYPE_CONTENT, getContentProperties()).getChildRef(); addContentToNode(newNodeRef); @@ -928,7 +930,7 @@ public class RuleServiceCoverageTest extends TestCase List origRefs = this.nodeService.getChildAssocs( this.nodeRef, RegexQNamePattern.MATCH_ALL, - QName.createQName(TEST_NAMESPACE, "origional")); + QName.createQName(TEST_NAMESPACE, localName)); assertNotNull(origRefs); assertEquals(1, origRefs.size()); NodeRef origNodeRef = origRefs.get(0).getChildRef(); @@ -937,7 +939,7 @@ public class RuleServiceCoverageTest extends TestCase // Check that the created node has been copied List copyChildAssocRefs = this.nodeService.getChildAssocs( this.rootNodeRef, - RegexQNamePattern.MATCH_ALL, QName.createQName(TEST_NAMESPACE, "origional")); + RegexQNamePattern.MATCH_ALL, QName.createQName(TEST_NAMESPACE, localName)); assertNotNull(copyChildAssocRefs); // ********************************** @@ -947,7 +949,7 @@ public class RuleServiceCoverageTest extends TestCase NodeRef copyNodeRef = copyChildAssocRefs.get(0).getChildRef(); assertTrue(this.nodeService.hasAspect(copyNodeRef, ContentModel.ASPECT_COPIEDFROM)); - NodeRef source = (NodeRef)this.nodeService.getProperty(copyNodeRef, ContentModel.PROP_COPY_REFERENCE); + NodeRef source = copyService.getOriginal(copyNodeRef); assertEquals(newNodeRef, source); // TODO test deep copy !! @@ -1028,7 +1030,7 @@ public class RuleServiceCoverageTest extends TestCase assertEquals(1, copyChildAssocRefs.size()); NodeRef copyNodeRef = copyChildAssocRefs.get(0).getChildRef(); assertTrue(this.nodeService.hasAspect(copyNodeRef, ContentModel.ASPECT_COPIEDFROM)); - NodeRef source = (NodeRef)this.nodeService.getProperty(copyNodeRef, ContentModel.PROP_COPY_REFERENCE); + NodeRef source = copyService.getOriginal(copyNodeRef); assertEquals(newNodeRef, source); // Check the transformed content @@ -1107,7 +1109,7 @@ public class RuleServiceCoverageTest extends TestCase assertEquals(1, copyChildAssocRefs.size()); NodeRef copyNodeRef = copyChildAssocRefs.get(0).getChildRef(); assertTrue(this.nodeService.hasAspect(copyNodeRef, ContentModel.ASPECT_COPIEDFROM)); - NodeRef source = (NodeRef)this.nodeService.getProperty(copyNodeRef, ContentModel.PROP_COPY_REFERENCE); + NodeRef source = copyService.getOriginal(copyNodeRef); assertEquals(newNodeRef, source); } @@ -1215,7 +1217,7 @@ public class RuleServiceCoverageTest extends TestCase else if (this.nodeService.hasAspect(childNodeRef, ContentModel.ASPECT_WORKING_COPY) == true) { // assert that it is the working copy that relates to the origional node - NodeRef copiedFromNodeRef = (NodeRef)this.nodeService.getProperty(childNodeRef, ContentModel.PROP_COPY_REFERENCE); + NodeRef copiedFromNodeRef = copyService.getOriginal(childNodeRef); assertEquals(newNodeRef, copiedFromNodeRef); } } diff --git a/source/java/org/alfresco/repo/rule/RuleServiceImpl.java b/source/java/org/alfresco/repo/rule/RuleServiceImpl.java index 9ecdd1780e..505885659b 100644 --- a/source/java/org/alfresco/repo/rule/RuleServiceImpl.java +++ b/source/java/org/alfresco/repo/rule/RuleServiceImpl.java @@ -43,6 +43,7 @@ import org.alfresco.service.cmr.action.ActionService; import org.alfresco.service.cmr.action.ActionServiceException; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.CopyService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; @@ -89,6 +90,7 @@ public class RuleServiceImpl private NodeService nodeService; private NodeService runtimeNodeService; + private CopyService copyService; private ActionService actionService; private DictionaryService dictionaryService; private PolicyComponent policyComponent; @@ -156,7 +158,15 @@ public class RuleServiceImpl { this.runtimeNodeService = runtimeNodeService; } - + + /** + * Set the service for locating copied nodes' originals + */ + public void setCopyService(CopyService copyService) + { + this.copyService = copyService; + } + /** * Set the action service */ @@ -335,107 +345,81 @@ public class RuleServiceImpl return result; } - /** - * @see org.alfresco.repo.rule.RuleService#getRuleTypes() - */ + @Override public List getRuleTypes() { return new ArrayList(this.ruleTypes.values()); } - /** - * @see org.alfresco.repo.rule.RuleService#getRuleType(java.lang.String) - */ + @Override public RuleType getRuleType(String name) { return this.ruleTypes.get(name); } - /** - * @see org.alfresco.service.cmr.rule.RuleService#enableRules() - */ + @Override public void enableRules() { this.rulesDisabled.remove(); } - /** - * @see org.alfresco.service.cmr.rule.RuleService#disableRules() - */ + @Override public void disableRules() { this.rulesDisabled.set(Boolean.TRUE); } - /** - * @see org.alfresco.service.cmr.rule.RuleService#isEnabled() - */ + @Override public boolean isEnabled() { return (this.globalRulesDisabled == false && this.rulesDisabled.get() == null); } - /** - * @see org.alfresco.service.cmr.rule.RuleService#rulesEnabled(NodeRef) - */ + @Override public boolean rulesEnabled(NodeRef nodeRef) { return (this.disabledNodeRefs.contains(nodeRef) == false); } - /** - * @see org.alfresco.service.cmr.rule.RuleService#disableRules(NodeRef) - */ + @Override public void disableRules(NodeRef nodeRef) { // Add the node to the set of disabled nodes this.disabledNodeRefs.add(nodeRef); } - /** - * @see org.alfresco.service.cmr.rule.RuleService#enableRules(NodeRef) - */ + @Override public void enableRules(NodeRef nodeRef) { // Remove the node from the set of disabled nodes this.disabledNodeRefs.remove(nodeRef); } - /** - * @see org.alfresco.service.cmr.rule.RuleService#disableRule(org.alfresco.service.cmr.rule.Rule) - */ + @Override public void disableRule(Rule rule) { this.disabledRules.add(rule); } - /** - * @see org.alfresco.service.cmr.rule.RuleService#enableRule(org.alfresco.service.cmr.rule.Rule) - */ + @Override public void enableRule(Rule rule) { this.disabledRules.remove(rule); } - /** - * @see org.alfresco.service.cmr.rule.RuleService#disableRuleType(org.alfresco.service.cmr.rule.RuleType) - */ + @Override public void disableRuleType(String ruleType) { disabledRuleTypes.add(ruleType); } - /** - * @see org.alfresco.service.cmr.rule.RuleService#enableRuleType(org.alfresco.service.cmr.rule.RuleType) - */ + @Override public void enableRuleType(String ruleType) { disabledRuleTypes.remove(ruleType); } - /** - * @see org.alfresco.service.cmr.rule.RuleService#isRuleTypeEnabled(org.alfresco.service.cmr.rule.RuleType) - */ + @Override public boolean isRuleTypeEnabled(String ruleType) { boolean result = true; @@ -446,33 +430,25 @@ public class RuleServiceImpl return result; } - /** - * @see org.alfresco.service.cmr.rule.RuleService#hasRules(org.alfresco.repo.ref.NodeRef) - */ + @Override public boolean hasRules(NodeRef nodeRef) { return getRules(nodeRef).size() != 0; } - /** - * @see org.alfresco.repo.rule.RuleService#getRules(org.alfresco.repo.ref.NodeRef) - */ + @Override public List getRules(NodeRef nodeRef) { return getRules(nodeRef, true, null); } - /** - * @see org.alfresco.repo.rule.RuleService#getRules(org.alfresco.repo.ref.NodeRef, boolean) - */ + @Override public List getRules(NodeRef nodeRef, boolean includeInherited) { return getRules(nodeRef, includeInherited, null); } - /** - * @see org.alfresco.repo.rule.RuleService#getRulesByRuleType(org.alfresco.repo.ref.NodeRef, org.alfresco.repo.rule.RuleType) - */ + @Override public List getRules(final NodeRef nodeRef, final boolean includeInherited, final String ruleTypeName) { //Run from system user: https://issues.alfresco.com/jira/browse/ALF-607 @@ -556,9 +532,7 @@ public class RuleServiceImpl return nodeRules; } - /** - * @see org.alfresco.service.cmr.rule.RuleService#countRules(org.alfresco.service.cmr.repository.NodeRef) - */ + @Override public int countRules(NodeRef nodeRef) { int ruleCount = 0; @@ -749,9 +723,7 @@ public class RuleServiceImpl return rule; } - /** - * @see org.alfresco.repo.rule.RuleService#saveRule(org.alfresco.repo.ref.NodeRef, org.alfresco.repo.rule.Rule) - */ + @Override public void saveRule(NodeRef nodeRef, Rule rule) { checkForLinkedRules(nodeRef); @@ -810,18 +782,14 @@ public class RuleServiceImpl } } - /** - * @see org.alfresco.service.cmr.rule.RuleService#saveRule(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.rule.Rule, int) - */ + @Override public void saveRule(NodeRef nodeRef, Rule rule, int index) { saveRule(nodeRef, rule); setRulePosition(nodeRef, rule.getNodeRef(), index); } - /** - * @see org.alfresco.service.cmr.rule.RuleService#setRulePosition(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef, int) - */ + @Override public void setRulePosition(NodeRef nodeRef, NodeRef ruleNodeRef, int index) { NodeRef ruleFolder = getSavedRuleFolderRef(nodeRef); @@ -856,9 +824,7 @@ public class RuleServiceImpl } } - /** - * @see org.alfresco.service.cmr.rule.RuleService#setRulePosition(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.rule.Rule, int) - */ + @Override public void setRulePosition(NodeRef nodeRef, Rule rule, int index) { setRulePosition(nodeRef, rule.getNodeRef(), index); @@ -909,9 +875,7 @@ public class RuleServiceImpl } - /** - * @see org.alfresco.repo.rule.RuleService#removeRule(org.alfresco.repo.ref.NodeRef, org.alfresco.service.cmr.rule.Rule) - */ + @Override public void removeRule(NodeRef nodeRef, Rule rule) { checkForLinkedRules(nodeRef); @@ -962,10 +926,8 @@ public class RuleServiceImpl throw new RuleServiceException("Can not edit rules as they are linked to another rule set."); } } - - /** - * @see org.alfresco.repo.rule.RuleService#removeAllRules(NodeRef) - */ + + @Override public void removeAllRules(NodeRef nodeRef) { checkForLinkedRules(nodeRef); @@ -999,14 +961,13 @@ public class RuleServiceImpl } } - /** - * @see org.alfresco.repo.rule.RuntimeRuleService#addRulePendingExecution(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.rule.Rule) - */ + @Override public void addRulePendingExecution(NodeRef actionableNodeRef, NodeRef actionedUponNodeRef, Rule rule) { addRulePendingExecution(actionableNodeRef, actionedUponNodeRef, rule, false); } + @Override @SuppressWarnings("unchecked") public void removeRulePendingExecution(NodeRef actionedUponNodeRef) { @@ -1035,9 +996,7 @@ public class RuleServiceImpl } } - /** - * @see org.alfresco.repo.rule.RuntimeRuleService#addRulePendingExecution(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.rule.Rule, boolean) - */ + @Override @SuppressWarnings("unchecked") public void addRulePendingExecution(NodeRef actionableNodeRef, NodeRef actionedUponNodeRef, Rule rule, boolean executeAtEnd) { @@ -1082,11 +1041,7 @@ public class RuleServiceImpl } } - - - /** - * @see org.alfresco.repo.rule.RuleService#executePendingRules() - */ + @Override public void executePendingRules() { if (AlfrescoTransactionSupport.getResource(KEY_RULES_EXECUTED) == null) @@ -1186,9 +1141,7 @@ public class RuleServiceImpl } } - /** - * @see org.alfresco.repo.rule.RuntimeRuleService#executeRule(org.alfresco.service.cmr.rule.Rule, org.alfresco.service.cmr.repository.NodeRef, java.util.Set) - */ + @Override public void executeRule(Rule rule, NodeRef actionedUponNodeRef, Set executedRules) { // Get the action associated with the rule @@ -1220,11 +1173,6 @@ public class RuleServiceImpl /** * Determines whether the rule can be executed - * - * @param executedRules - * @param actionedUponNodeRef - * @param rule - * @return */ private boolean canExecuteRule(Set executedRules, NodeRef actionedUponNodeRef, Rule rule) { @@ -1263,26 +1211,18 @@ public class RuleServiceImpl /** * Checks to see if a copy exists in the executed rules list - * - * @param executedRules - * @param actionedUponNodeRef - * @param rule - * @return */ private boolean checkForCopy(Set executedRules, NodeRef actionedUponNodeRef, Rule rule) { boolean result = true; if (this.nodeService.exists(actionedUponNodeRef) - && this.permissionService.hasPermission(actionedUponNodeRef, PermissionService.READ).equals(AccessStatus.ALLOWED) - && this.nodeService.hasAspect(actionedUponNodeRef, ContentModel.ASPECT_COPIEDFROM) == true) + && this.permissionService.hasPermission(actionedUponNodeRef, PermissionService.READ).equals(AccessStatus.ALLOWED)) { + NodeRef copiedFrom = copyService.getOriginal(actionedUponNodeRef); if (logger.isDebugEnabled() == true) { - logger.debug(" >> Has the copied from aspect (" + actionedUponNodeRef.getId() + ")"); + logger.debug(" >> Got the copiedFrom nodeRef (" + copiedFrom + ")"); } - NodeRef copiedFrom = (NodeRef)this.nodeService.getProperty(actionedUponNodeRef, ContentModel.PROP_COPY_REFERENCE); - - if (logger.isDebugEnabled() == true && copiedFrom != null) {logger.debug(" >> Got the copedFrom nodeRef (" + copiedFrom.getId() + ")");}; if (copiedFrom != null) { @@ -1436,18 +1376,14 @@ public class RuleServiceImpl } } - /** - * @see org.alfresco.service.cmr.rule.RuleService#getOwningNodeRef(org.alfresco.service.cmr.rule.Rule) - */ + @Override public NodeRef getOwningNodeRef(final Rule rule) { // Run from system user: https://issues.alfresco.com/jira/browse/ALF-607 return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() { - public NodeRef doWork() throws Exception { - NodeRef result = null; NodeRef ruleNodeRef = rule.getNodeRef(); @@ -1461,10 +1397,6 @@ public class RuleServiceImpl }, AuthenticationUtil.getSystemUserName()); } - /** - * @param ruleNodeRef - * @return - */ private NodeRef getOwningNodeRefRuleImpl(NodeRef ruleNodeRef) { // Get the system folder parent @@ -1474,9 +1406,7 @@ public class RuleServiceImpl return this.nodeService.getPrimaryParent(systemFolder).getParentRef(); } - /** - * @see org.alfresco.service.cmr.rule.RuleService#getOwningNodeRef(org.alfresco.service.cmr.action.Action) - */ + @Override public NodeRef getOwningNodeRef(final Action action) { // Run from system user: https://issues.alfresco.com/jira/browse/ALF-607 @@ -1498,9 +1428,6 @@ public class RuleServiceImpl }, AuthenticationUtil.getSystemUserName()); } - /** - * @param actionNodeRef - */ private NodeRef getOwningNodeRefActionImpl(NodeRef actionNodeRef) { NodeRef result = null; @@ -1520,17 +1447,13 @@ public class RuleServiceImpl return result; } - /** - * @see org.alfresco.service.cmr.rule.RuleService#isLinkedToRuleNode(org.alfresco.service.cmr.repository.NodeRef) - */ + @Override public boolean isLinkedToRuleNode(NodeRef nodeRef) { return (getLinkedToRuleNode(nodeRef) != null); } - /** - * @see org.alfresco.service.cmr.rule.RuleService#getLinkedToRuleNode(org.alfresco.service.cmr.repository.NodeRef) - */ + @Override public NodeRef getLinkedToRuleNode(NodeRef nodeRef) { NodeRef result = null; @@ -1548,9 +1471,7 @@ public class RuleServiceImpl return result; } - /** - * @see org.alfresco.service.cmr.rule.RuleService#getLinkedFromRuleNodes(org.alfresco.service.cmr.repository.NodeRef) - */ + @Override public List getLinkedFromRuleNodes(NodeRef nodeRef) { List result = new ArrayList(); @@ -1570,8 +1491,6 @@ public class RuleServiceImpl } } } - return result; } - } diff --git a/source/java/org/alfresco/repo/rule/ruletrigger/OnContentUpdateRuleTrigger.java b/source/java/org/alfresco/repo/rule/ruletrigger/OnContentUpdateRuleTrigger.java index 74dbac355a..9f5e576df3 100644 --- a/source/java/org/alfresco/repo/rule/ruletrigger/OnContentUpdateRuleTrigger.java +++ b/source/java/org/alfresco/repo/rule/ruletrigger/OnContentUpdateRuleTrigger.java @@ -27,7 +27,7 @@ import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.policy.JavaBehaviour; import org.alfresco.repo.transaction.TransactionalResourceHelper; import org.alfresco.service.cmr.repository.ChildAssociationRef; -import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; @@ -88,12 +88,12 @@ public class OnContentUpdateRuleTrigger extends RuleTriggerAbstractBase */ public void onContentUpdate(NodeRef nodeRef, boolean newContent) { - - // Check the new content and make sure that we do indeed want to trigger the rule - boolean fail = false; - if (newContent == true) - { - Boolean value = (Boolean)nodeService.getProperty(nodeRef, QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "editInline")); + + // Check the new content and make sure that we do indeed want to trigger the rule + boolean fail = false; + if (newContent == true) + { + Boolean value = (Boolean)nodeService.getProperty(nodeRef, QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "editInline")); if (value != null) { boolean editInline = value.booleanValue(); @@ -102,18 +102,17 @@ public class OnContentUpdateRuleTrigger extends RuleTriggerAbstractBase fail = true; } } - + if (fail == false) { - ContentReader contentReader = this.contentService.getReader(nodeRef, ContentModel.PROP_CONTENT); - if (contentReader == null || - contentReader.exists() == false || - isZeroLengthOfficeDoc(contentReader) == true) - { - fail = true; - } + // Note: Don't use the ContentService.getReader() because we don't need access to the content + ContentData contentData = (ContentData) nodeService.getProperty(nodeRef, ContentModel.PROP_CONTENT); + if (contentData == null || isZeroLengthOfficeDoc(contentData)) + { + fail = true; + } } - } + } // Double check for content created in this transaction if (fail == false && !newContent) @@ -126,11 +125,11 @@ public class OnContentUpdateRuleTrigger extends RuleTriggerAbstractBase } fail = wasCreatedInTxn; } - - // Trigger the rules in the appropriate way + + // Trigger the rules in the appropriate way if (fail == false && newContent == this.onNewContent) { - if (triggerParentRules == true) + if (triggerParentRules == true) { if (logger.isDebugEnabled() == true) { @@ -153,20 +152,20 @@ public class OnContentUpdateRuleTrigger extends RuleTriggerAbstractBase /** * Indicates whether we are dealing with a zero length office document or not * - * @param contentReader the content reader - * @return boolean true if zero length office document, false otherwise + * @param contentData the content details + * @return boolean true if zero length office document, false otherwise */ - private boolean isZeroLengthOfficeDoc(ContentReader contentReader) + private boolean isZeroLengthOfficeDoc(ContentData contentData) { - boolean result = false; - if (contentReader.getSize() == 0 && - (MimetypeMap.MIMETYPE_WORD.equals(contentReader.getMimetype()) == true || - MimetypeMap.MIMETYPE_EXCEL.equals(contentReader.getMimetype()) == true || - MimetypeMap.MIMETYPE_PPT.equals(contentReader.getMimetype()) == true)) - { - result = true; - } - return result; + boolean result = false; + if (contentData.getSize() == 0 && + (MimetypeMap.MIMETYPE_WORD.equals(contentData.getMimetype()) == true || + MimetypeMap.MIMETYPE_EXCEL.equals(contentData.getMimetype()) == true || + MimetypeMap.MIMETYPE_PPT.equals(contentData.getMimetype()) == true)) + { + result = true; + } + return result; } } diff --git a/source/java/org/alfresco/repo/security/permissions/PermissionCheckedCollection.java b/source/java/org/alfresco/repo/security/permissions/PermissionCheckedCollection.java index 8c9aaf734c..834f811b44 100644 --- a/source/java/org/alfresco/repo/security/permissions/PermissionCheckedCollection.java +++ b/source/java/org/alfresco/repo/security/permissions/PermissionCheckedCollection.java @@ -95,6 +95,31 @@ public interface PermissionCheckedCollection { return sizeOriginal; } + /** + * Helper method to create a {@link PermissionCheckedCollection} from an existing Collection + * by applying the same values as present on a potentially permission-checked source. If the + * existing checked source is NOT permission-checked, then the collection will not be + * decorated. + * + * @param the type of the Collection + * @param collection the Collection to proxy + * @param checkedSource a collection that might implement {@link PermissionCheckedCollection} + * @return a Collection of the same type but including the + * {@link PermissionCheckedCollection} interface + */ + public static final Collection create( + Collection collection, Collection checkedSource) + { + if (checkedSource instanceof PermissionCheckedCollection) + { + PermissionCheckedCollection source = (PermissionCheckedCollection) checkedSource; + return create(collection, source.isCutOff(), source.sizeUnchecked(), source.sizeOriginal()); + } + else + { + return collection; + } + } /** * Helper method to create a {@link PermissionCheckedCollection} from an existing Collection * diff --git a/source/java/org/alfresco/repo/security/permissions/dynamic/LockOwnerDynamicAuthority.java b/source/java/org/alfresco/repo/security/permissions/dynamic/LockOwnerDynamicAuthority.java index eb495d0591..88f1fc0294 100644 --- a/source/java/org/alfresco/repo/security/permissions/dynamic/LockOwnerDynamicAuthority.java +++ b/source/java/org/alfresco/repo/security/permissions/dynamic/LockOwnerDynamicAuthority.java @@ -18,26 +18,24 @@ */ package org.alfresco.repo.security.permissions.dynamic; -import java.io.Serializable; import java.util.HashSet; import java.util.List; import java.util.Set; -import org.alfresco.model.ContentModel; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.security.permissions.DynamicAuthority; import org.alfresco.repo.security.permissions.PermissionReference; import org.alfresco.repo.security.permissions.impl.ModelDAO; +import org.alfresco.service.cmr.coci.CheckOutCheckInService; import org.alfresco.service.cmr.lock.LockService; import org.alfresco.service.cmr.lock.LockStatus; import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.util.PropertyCheck; +import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationEvent; import org.springframework.extensions.surf.util.AbstractLifecycleBean; -import org.alfresco.util.PropertyCheck; /** * LockOwnerDynamicAuthority @@ -46,7 +44,7 @@ public class LockOwnerDynamicAuthority extends AbstractLifecycleBean implements { private LockService lockService; - private NodeService nodeService; + private CheckOutCheckInService checkOutCheckInService; private ModelDAO modelDAO; @@ -64,22 +62,10 @@ public class LockOwnerDynamicAuthority extends AbstractLifecycleBean implements { return true; } - if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_WORKING_COPY)) + NodeRef original = checkOutCheckInService.getCheckedOut(nodeRef); + if (original != null) { - NodeRef original = null; - Serializable reference = nodeService.getProperty(nodeRef, ContentModel.PROP_COPY_REFERENCE); - if (reference != null) - { - original = DefaultTypeConverter.INSTANCE.convert(NodeRef.class, reference); - } - if (original != null && nodeService.exists(original)) - { - return (lockService.getLockStatus(original, userName) == LockStatus.LOCK_OWNER); - } - else - { - return false; - } + return (lockService.getLockStatus(original, userName) == LockStatus.LOCK_OWNER); } else { @@ -99,8 +85,11 @@ public class LockOwnerDynamicAuthority extends AbstractLifecycleBean implements @Override protected void onBootstrap(ApplicationEvent event) { + ApplicationContext ctx = super.getApplicationContext(); + checkOutCheckInService = (CheckOutCheckInService) ctx.getBean("checkOutCheckInService"); + PropertyCheck.mandatory(this, "lockService", lockService); - PropertyCheck.mandatory(this, "nodeService", nodeService); + PropertyCheck.mandatory(this, "checkOutCheckInService", checkOutCheckInService); PropertyCheck.mandatory(this, "modelDAO", modelDAO); // Build the permission set @@ -126,7 +115,6 @@ public class LockOwnerDynamicAuthority extends AbstractLifecycleBean implements /** * Set the lock service - * @param lockService */ public void setLockService(LockService lockService) { @@ -134,17 +122,17 @@ public class LockOwnerDynamicAuthority extends AbstractLifecycleBean implements } /** - * Set the node service - * @param nodeService + * Service to get the check-in details. This is not used for Spring configuration + * because it requires a permission-wrapped public service that in turn depends on + * this component. */ - public void setNodeService(NodeService nodeService) + public void setCheckOutCheckInService(CheckOutCheckInService checkOutCheckInService) { - this.nodeService = nodeService; + this.checkOutCheckInService = checkOutCheckInService; } - + /** * Set the permissions model dao - * @param modelDAO */ public void setModelDAO(ModelDAO modelDAO) { @@ -153,14 +141,12 @@ public class LockOwnerDynamicAuthority extends AbstractLifecycleBean implements /** * Set the permissions for which this dynamic authority is required - * @param requiredFor */ public void setRequiredFor(List requiredFor) { this.requiredFor = requiredFor; } - public Set requiredFor() { return whenRequired; diff --git a/source/java/org/alfresco/repo/security/permissions/dynamic/LockOwnerDynamicAuthorityTest.java b/source/java/org/alfresco/repo/security/permissions/dynamic/LockOwnerDynamicAuthorityTest.java index 4fa7bebea6..f08aba3297 100644 --- a/source/java/org/alfresco/repo/security/permissions/dynamic/LockOwnerDynamicAuthorityTest.java +++ b/source/java/org/alfresco/repo/security/permissions/dynamic/LockOwnerDynamicAuthorityTest.java @@ -54,33 +54,23 @@ import org.springframework.context.ApplicationContext; * Test the lock owner dynamic authority * * @author andyh - * */ public class LockOwnerDynamicAuthorityTest extends TestCase { private static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext(); private NodeService nodeService; - private MutableAuthenticationService authenticationService; - private AuthenticationComponent authenticationComponent; - private MutableAuthenticationDao authenticationDAO; - private LockService lockService; - - private NodeRef rootNodeRef; - - private UserTransaction userTransaction; - private PermissionService permissionService; - private LockOwnerDynamicAuthority dynamicAuthority; - private CheckOutCheckInService checkOutCheckInService; - private OwnableService ownableService; + + private NodeRef rootNodeRef; + private UserTransaction userTransaction; /** * diff --git a/source/java/org/alfresco/repo/version/VersionMigratorTest.java b/source/java/org/alfresco/repo/version/VersionMigratorTest.java index 45cae9d054..e0053b4795 100644 --- a/source/java/org/alfresco/repo/version/VersionMigratorTest.java +++ b/source/java/org/alfresco/repo/version/VersionMigratorTest.java @@ -431,7 +431,7 @@ public class VersionMigratorTest extends BaseVersionStoreTest NodeRef nodeRef = nodeService.createNode( rootNodeRef, ContentModel.ASSOC_CHILDREN, - QName.createQName("{test}MyVersionableNode"), + QName.createQName(TEST_NAMESPACE, "MyVersionableNode"), TEST_TYPE_QNAME, this.nodeProperties).getChildRef(); diff --git a/source/java/org/alfresco/service/cmr/coci/CheckOutCheckInService.java b/source/java/org/alfresco/service/cmr/coci/CheckOutCheckInService.java index 44b08fcc55..09ac31db82 100644 --- a/source/java/org/alfresco/service/cmr/coci/CheckOutCheckInService.java +++ b/source/java/org/alfresco/service/cmr/coci/CheckOutCheckInService.java @@ -22,7 +22,6 @@ import java.io.Serializable; import java.util.Map; import org.alfresco.service.Auditable; -import org.alfresco.service.PublicService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.QName; @@ -176,4 +175,33 @@ public interface CheckOutCheckInService */ @Auditable(parameters = {"nodeRef"}) public NodeRef getWorkingCopy(NodeRef nodeRef); + + /** + * Helper method to retrieve the original node (check-out source) for a working copy. + *

+ * A null node reference is returned if the node is not a working copy. + * + * @param nodeRef the (potential) working copy + * @return the original (source) node or null if it is not a working copy + */ + @Auditable(parameters = {"nodeRef"}) + public NodeRef getCheckedOut(NodeRef nodeRef); + + /** + * Determine if a node is a working copy or not + * + * @param nodeRef the (potential) working copy + * @return true if the node is a working copy otherwise false + */ + @Auditable(parameters = {"nodeRef"}) + public boolean isWorkingCopy(NodeRef nodeRef); + + /** + * Determine if a node has been checked out or not + * + * @param nodeRef the (potentially) checked out node + * @return true if the node has been checked out otherwise false + */ + @Auditable(parameters = {"nodeRef"}) + public boolean isCheckedOut(NodeRef nodeRef); } diff --git a/source/java/org/alfresco/service/cmr/lock/LockService.java b/source/java/org/alfresco/service/cmr/lock/LockService.java index d200d06c5b..c47652a4a7 100644 --- a/source/java/org/alfresco/service/cmr/lock/LockService.java +++ b/source/java/org/alfresco/service/cmr/lock/LockService.java @@ -22,7 +22,6 @@ import java.util.Collection; import java.util.List; import org.alfresco.service.Auditable; -import org.alfresco.service.PublicService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.StoreRef; @@ -137,7 +136,7 @@ public interface LockService throws UnableToAquireLockException; /** - * Removes the lock on a node. + * Removes the lock on a node; if there is no lock then nothing is done. *

* The user must have sufficient permissions to remove the lock (ie: be the * owner of the lock or have admin rights) otherwise an exception will be raised. diff --git a/source/java/org/alfresco/service/cmr/repository/CopyService.java b/source/java/org/alfresco/service/cmr/repository/CopyService.java index 27ad0dedd7..92cd2f5a5e 100644 --- a/source/java/org/alfresco/service/cmr/repository/CopyService.java +++ b/source/java/org/alfresco/service/cmr/repository/CopyService.java @@ -19,10 +19,10 @@ package org.alfresco.service.cmr.repository; import java.util.List; -import java.util.Set; import org.alfresco.query.PagingRequest; import org.alfresco.query.PagingResults; +import org.alfresco.repo.security.permissions.PermissionCheckValue; import org.alfresco.service.Auditable; import org.alfresco.service.namespace.QName; @@ -160,6 +160,16 @@ public interface CopyService @Auditable(parameters = {"sourceNodeRef", "destinationNodeRef"}) public void copy(NodeRef sourceNodeRef, NodeRef destinationNodeRef); + /** + * Get the original for a given copied node + * + * @param copiedNodeRef the copied node reference + * @return the original node reference or null if it isn't a + * copy or the original has been deleted + */ + @Auditable(parameters = {"nodeRef"}) + public NodeRef getOriginal(NodeRef copiedNodeRef); + /** * Gets all the copies of a given node that have been made using this service. * @@ -176,19 +186,15 @@ public interface CopyService * @author Derek Hulley * @since 4.0 */ - public class CopyInfo + public class CopyInfo implements PermissionCheckValue { private final NodeRef nodeRef; private final String name; - private final NodeRef parentNodeRef; - private final String parentName; - public CopyInfo(NodeRef nodeRef, String name, NodeRef parentNodeRef, String parentName) + public CopyInfo(NodeRef nodeRef, String name) { this.nodeRef = nodeRef; this.name = name; - this.parentNodeRef = parentNodeRef; - this.parentName = parentName; } /** @@ -206,22 +212,6 @@ public interface CopyService { return name; } - - /** - * @return the parent of the node copy - */ - public NodeRef getParentNodeRef() - { - return parentNodeRef; - } - - /** - * @return the name of the parent of the node copy - */ - public String getParentName() - { - return parentName; - } } /** @@ -231,7 +221,7 @@ public interface CopyService * @param pagingRequest page request details * @return the page(s) of nodes that were copied from the given node */ - @Auditable(parameters = {"nodeRef"}) + @Auditable(parameters = {"originalNodeRef"}) public PagingResults getCopies(NodeRef originalNodeRef, PagingRequest pagingRequest); /** @@ -243,10 +233,9 @@ public interface CopyService * @param pagingRequest page request details * @return the page(s) of nodes that were copied from the given node */ - @Auditable(parameters = {"nodeRef"}) + @Auditable(parameters = {"originalNodeRef", "copyParentNodeRef"}) public PagingResults getCopies( NodeRef originalNodeRef, NodeRef copyParentNodeRef, - Set copyNodeAspectsToIgnore, PagingRequest pagingRequest); }