From 0e0e1655865e244f34f5d64bf6fd34a496595cb8 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Fri, 1 Mar 2013 10:35:49 +0000 Subject: [PATCH] ALF-16274: Enable quick share for custom sub types of cm:content git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@47380 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../alfresco/quickshare-services-context.xml | 1 + .../quickshare/QuickShareServiceImpl.java | 24 ++- .../QuickShareServiceIntegrationTest.java | 129 ++++++++++++-- .../util/test/junitrules/TemporaryModels.java | 164 ++++++++++++++++++ 4 files changed, 304 insertions(+), 14 deletions(-) create mode 100644 source/java/org/alfresco/util/test/junitrules/TemporaryModels.java diff --git a/config/alfresco/quickshare-services-context.xml b/config/alfresco/quickshare-services-context.xml index 5a76085d29..0816b194f3 100644 --- a/config/alfresco/quickshare-services-context.xml +++ b/config/alfresco/quickshare-services-context.xml @@ -54,6 +54,7 @@ + diff --git a/source/java/org/alfresco/repo/quickshare/QuickShareServiceImpl.java b/source/java/org/alfresco/repo/quickshare/QuickShareServiceImpl.java index a050f0f429..4d6789e0bd 100644 --- a/source/java/org/alfresco/repo/quickshare/QuickShareServiceImpl.java +++ b/source/java/org/alfresco/repo/quickshare/QuickShareServiceImpl.java @@ -40,6 +40,7 @@ import org.alfresco.repo.tenant.TenantUtil; import org.alfresco.repo.tenant.TenantUtil.TenantRunAsWork; import org.alfresco.repo.thumbnail.ThumbnailDefinition; import org.alfresco.service.cmr.attributes.AttributeService; +import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.quickshare.InvalidSharedIdException; import org.alfresco.service.cmr.quickshare.QuickShareDTO; import org.alfresco.service.cmr.quickshare.QuickShareDisabledException; @@ -79,6 +80,7 @@ public class QuickShareServiceImpl implements QuickShareService, NodeServicePoli private boolean enabled; private AttributeService attributeService; + private DictionaryService dictionaryService; private NodeService nodeService; private PersonService personService; private PolicyComponent policyComponent; @@ -101,6 +103,14 @@ public class QuickShareServiceImpl implements QuickShareService, NodeServicePoli this.attributeService = attributeService; } + /** + * Set the dictionary service + */ + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + /** * Set the node service */ @@ -167,7 +177,7 @@ public class QuickShareServiceImpl implements QuickShareService, NodeServicePoli //Check the node is the correct type QName typeQName = nodeService.getType(nodeRef); - if (! typeQName.equals(ContentModel.TYPE_CONTENT)) + if (isSharable(typeQName) == false) { throw new InvalidNodeRefException(nodeRef); } @@ -312,6 +322,12 @@ public class QuickShareServiceImpl implements QuickShareService, NodeServicePoli { metadata.put("sharedId", nodeProps.get(QuickShareModel.PROP_QSHARE_SHAREDID)); } + else + { + QName type = nodeService.getType(nodeRef); + boolean sharable = isSharable(type); + metadata.put("sharable", sharable); + } Map model = new HashMap(1); model.put("item", metadata); @@ -425,7 +441,7 @@ public class QuickShareServiceImpl implements QuickShareService, NodeServicePoli public Void doWork() throws Exception { QName typeQName = nodeService.getType(nodeRef); - if (! typeQName.equals(ContentModel.TYPE_CONTENT)) + if (! isSharable(typeQName)) { throw new InvalidNodeRefException(nodeRef); } @@ -451,6 +467,10 @@ public class QuickShareServiceImpl implements QuickShareService, NodeServicePoli } } + private boolean isSharable(QName type) + { + return type.equals(ContentModel.TYPE_CONTENT) || dictionaryService.isSubClass(type, ContentModel.TYPE_CONTENT); + } // Prevent copying of Quick share properties on node copy. @Override public CopyBehaviourCallback getCopyCallback(QName classRef, CopyDetails copyDetails) diff --git a/source/java/org/alfresco/repo/quickshare/QuickShareServiceIntegrationTest.java b/source/java/org/alfresco/repo/quickshare/QuickShareServiceIntegrationTest.java index 933ea2223b..ee65bc04f3 100644 --- a/source/java/org/alfresco/repo/quickshare/QuickShareServiceIntegrationTest.java +++ b/source/java/org/alfresco/repo/quickshare/QuickShareServiceIntegrationTest.java @@ -24,6 +24,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import java.io.ByteArrayInputStream; import java.io.Serializable; import java.util.Map; @@ -33,16 +34,19 @@ import org.alfresco.repo.model.Repository; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.security.permissions.AccessDeniedException; +import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.quickshare.InvalidSharedIdException; import org.alfresco.service.cmr.quickshare.QuickShareDTO; import org.alfresco.service.cmr.quickshare.QuickShareService; import org.alfresco.service.cmr.repository.CopyService; +import org.alfresco.service.cmr.repository.InvalidNodeRefException; 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.test.junitrules.AlfrescoPerson; import org.alfresco.util.test.junitrules.ApplicationContextInit; +import org.alfresco.util.test.junitrules.TemporaryModels; import org.alfresco.util.test.junitrules.TemporaryNodes; import org.apache.commons.codec.binary.Base64; import org.junit.Assert; @@ -66,9 +70,40 @@ public class QuickShareServiceIntegrationTest { private static final ApplicationContextInit testContext = new ApplicationContextInit(); + private static final String MODEL = + "" + + "" + + "LX model" + + "Peter Löfgren" + + "1.0" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "LX dokument" + + "cm:content" + + "" + + "cm:generalclassifiable" + + "" + + "" + + "" + + "LX dokument 2" + + "cm:cmobject" + + "" + + "" + + ""; + private static CopyService copyService; private static NodeService nodeService; private static QuickShareService quickShareService; + private static DictionaryService dictionaryService; private static Repository repository; private static AlfrescoPerson user1 = new AlfrescoPerson(testContext, "UserOne"); @@ -76,6 +111,8 @@ public class QuickShareServiceIntegrationTest // A rule to manage test nodes reused across all the test methods @Rule public TemporaryNodes testNodes = new TemporaryNodes(testContext); + + @Rule public TemporaryModels temporaryModels = new TemporaryModels(testContext); @ClassRule public static RuleChain classChain = RuleChain.outerRule(testContext) .around(user1) @@ -96,6 +133,7 @@ public class QuickShareServiceIntegrationTest ApplicationContext ctx = testContext.getApplicationContext(); copyService = ctx.getBean("CopyService", CopyService.class); + dictionaryService = ctx.getBean("dictionaryService", DictionaryService.class); nodeService = ctx.getBean("NodeService", NodeService.class); quickShareService = ctx.getBean("QuickShareService", QuickShareService.class); repository = ctx.getBean("repositoryHelper", Repository.class); @@ -160,18 +198,8 @@ public class QuickShareServiceIntegrationTest } @Test public void unshare() { - final QuickShareDTO dto = share(testNode, user1.getUsername()); - - AuthenticationUtil.runAs(new RunAsWork() - { - - @Override - public Void doWork() throws Exception - { - quickShareService.unshareContent(dto.getId()); - return null; - } - }, user1.getUsername()); + final QuickShareDTO dto = share(testNode, user1.getUsername()); + unshare(dto.getId(), user1.getUsername()); AuthenticationUtil.runAsSystem(new RunAsWork(){ @Override @@ -186,6 +214,21 @@ public class QuickShareServiceIntegrationTest }); } + + private void unshare(final String sharedId, final String userName) { + + AuthenticationUtil.runAs(new RunAsWork() + { + + @Override + public Void doWork() throws Exception + { + quickShareService.unshareContent(sharedId); + return null; + } + }, userName); + } + private QuickShareDTO share(final NodeRef nodeRef, String username) { return AuthenticationUtil.runAs(new RunAsWork() @@ -255,4 +298,66 @@ public class QuickShareServiceIntegrationTest } }, user1.getUsername()); } + + /** + * Content types that extend cm:content should be shareable. + * + * See https://issues.alfresco.com/jira/browse/ALF-16274. + */ + @Test public void testWithCustomContentType() + { + ByteArrayInputStream modelStream = new ByteArrayInputStream(MODEL.getBytes()); + temporaryModels.loadModel(modelStream); + + QName sharableType = QName.createQName("{http://bugtestmodel}doc"); + QName unsharableType = QName.createQName("{http://bugtestmodel}doc2"); + + final NodeRef sharableNode = testNodes.createNodeWithTextContent(userHome, + "Quick Share Custom Type Sharable Test Node", + sharableType, + user1.getUsername(), + "Quick Share Test Node Content"); + + Map metadata = getMetadata(sharableNode, user1); + + assertTrue((Boolean)metadata.get("sharable")); + + QuickShareDTO dto = share(sharableNode, user1.getUsername()); + unshare(dto.getId(), user1.getUsername()); + + final NodeRef unsharableNode = testNodes.createNodeWithTextContent(userHome, + "Quick Share Custom Type Unsharable Test Node", + unsharableType, + user1.getUsername(), + "Quick Share Test Node Content"); + + metadata = getMetadata(unsharableNode, user1); + assertFalse((Boolean)metadata.get("sharable")); + + boolean exceptionThrown = false; + try { + // Prior to fixing ALF-16274, this would throw an InvalidNodeRefException. + share(unsharableNode, user1.getUsername()); + } + catch(InvalidNodeRefException ex) + { + exceptionThrown = true; + } + assertTrue("InvalidNodeRefException not thrown on trying to share an unsharable content type", exceptionThrown); + } + + + @SuppressWarnings("unchecked") + private Map getMetadata(final NodeRef nodeRef, AlfrescoPerson user) { + Map container = AuthenticationUtil.runAs(new RunAsWork>() + { + @Override + public Map doWork() throws Exception + { + return quickShareService.getMetaData(nodeRef); + } + }, user.getUsername()); + return (Map)container.get("item"); + } + } diff --git a/source/java/org/alfresco/util/test/junitrules/TemporaryModels.java b/source/java/org/alfresco/util/test/junitrules/TemporaryModels.java new file mode 100644 index 0000000000..4861452908 --- /dev/null +++ b/source/java/org/alfresco/util/test/junitrules/TemporaryModels.java @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2005-2012 + 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.util.test.junitrules; + +import java.io.IOException; +import java.io.InputStream; +import java.util.HashSet; +import java.util.Set; + +import org.alfresco.repo.dictionary.DictionaryDAO; +import org.alfresco.repo.dictionary.M2Model; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.cmr.dictionary.DictionaryException; +import org.alfresco.service.namespace.QName; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.junit.rules.ExternalResource; +import org.springframework.context.ApplicationContext; + +/** + * A JUnit rule designed to help with the automatic cleanup of temporary models and to make it easier to + * create common test models with JUnit code. + * + * @author Alex Miller + * @since 4.2 + */ +public class TemporaryModels extends ExternalResource +{ + private static final Log logger = LogFactory.getLog(TemporaryModels.class); + + private final ApplicationContextInit appContextRule; + + private final Set loadedModels = new HashSet(); + + /** + * Constructs the rule with a reference to a {@link ApplicationContextInit rule} which can be used to retrieve the ApplicationContext. + * + * @param appContextRule a rule which can be used to retrieve the spring app context. + */ + public TemporaryModels(ApplicationContextInit appContextRule) + { + this.appContextRule = appContextRule; + } + + + @Override protected void before() throws Throwable + { + // Intentionally empty + } + + @Override protected void after() + { + final RetryingTransactionHelper transactionHelper = getTransactionHelper(); + final DictionaryDAO dictionaryDAO = getDictionaryDAO(); + + // Run as system to ensure all non-system nodes can be deleted irrespective of which user created them. + AuthenticationUtil.runAs(new RunAsWork() + { + @Override public Void doWork() throws Exception + { + transactionHelper.doInTransaction(new RetryingTransactionCallback() + { + @Override public Void execute() throws Throwable + { + for (QName model : loadedModels) + { + dictionaryDAO.removeModel(model); + } + return null; + } + }); + return null; + } + }, AuthenticationUtil.getSystemUserName()); + } + + + private RetryingTransactionHelper getTransactionHelper() { + final ApplicationContext springContext = appContextRule.getApplicationContext(); + + final RetryingTransactionHelper transactionHelper = springContext.getBean("retryingTransactionHelper", RetryingTransactionHelper.class); + return transactionHelper; + } + + public QName loadModel(String modelPath, ClassLoader classLoader) + { + InputStream modelStream = classLoader.getResourceAsStream(modelPath); + if (modelStream == null) + { + throw new DictionaryException("Could not find bootstrap model " + modelPath); + } + try + { + return loadModel(modelStream); + } + finally + { + try + { + modelStream.close(); + } + catch (IOException ioe) + { + logger.warn("Failed to close model input stream for '"+modelPath+"': "+ioe); + } + } + } + + public QName loadModel(InputStream modelStream) + { + try + { + final M2Model model = M2Model.createModel(modelStream); + + return loadModel(model); + } + catch(DictionaryException e) + { + throw new DictionaryException("Could not import model", e); + } + + } + + + private QName loadModel(final M2Model model) { + if (logger.isDebugEnabled()) + { + logger.debug("Loading model: "+model.getName()); + } + + final DictionaryDAO dictionaryDAO = getDictionaryDAO(); + QName modelQName = dictionaryDAO.putModel(model); + loadedModels.add(modelQName); + return modelQName; + } + + + private DictionaryDAO getDictionaryDAO() { + final ApplicationContext springContext = appContextRule.getApplicationContext(); + + DictionaryDAO dictionaryDAO = springContext.getBean("dictionaryDAO", DictionaryDAO.class); + return dictionaryDAO; + } +}