diff --git a/config/alfresco/web-publishing-context.xml b/config/alfresco/web-publishing-context.xml index d5f7365587..afba82e006 100644 --- a/config/alfresco/web-publishing-context.xml +++ b/config/alfresco/web-publishing-context.xml @@ -55,6 +55,7 @@ + diff --git a/source/java/org/alfresco/repo/publishing/ChannelHelper.java b/source/java/org/alfresco/repo/publishing/ChannelHelper.java index 36f80b4e6c..ceb49b4c19 100644 --- a/source/java/org/alfresco/repo/publishing/ChannelHelper.java +++ b/source/java/org/alfresco/repo/publishing/ChannelHelper.java @@ -20,7 +20,10 @@ package org.alfresco.repo.publishing; import static org.alfresco.model.ContentModel.ASSOC_CONTAINS; +import static org.alfresco.model.ContentModel.PROP_CONTENT; +import static org.alfresco.model.ContentModel.PROP_CONTENT_PROPERTY_NAME; import static org.alfresco.repo.publishing.PublishingModel.ASPECT_CONTENT_ROOT; +import static org.alfresco.repo.publishing.PublishingModel.ASPECT_PUBLISHED; import static org.alfresco.repo.publishing.PublishingModel.ASSOC_SOURCE; import static org.alfresco.repo.publishing.PublishingModel.NAMESPACE; import static org.alfresco.repo.publishing.PublishingModel.PROP_CHANNEL; @@ -31,6 +34,7 @@ import static org.alfresco.repo.publishing.PublishingModel.TYPE_DELIVERY_CHANNEL import java.io.Serializable; import java.util.List; import java.util.Map; +import java.util.Set; import org.alfresco.model.ContentModel; import org.alfresco.service.cmr.dictionary.DictionaryService; @@ -40,6 +44,8 @@ import org.alfresco.service.cmr.publishing.channels.Channel; import org.alfresco.service.cmr.publishing.channels.ChannelService; import org.alfresco.service.cmr.publishing.channels.ChannelType; import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.namespace.NamespaceService; @@ -59,6 +65,7 @@ public class ChannelHelper private NodeService nodeService; private DictionaryService dictionaryService; + private ContentService contentService; public ChannelHelper() { @@ -92,7 +99,7 @@ public class ChannelHelper String channelTypeId = (String) props.get(PublishingModel.PROP_CHANNEL_TYPE_ID); ChannelType channelType = channelService.getChannelType(channelTypeId); String name = (String) props.get(ContentModel.PROP_NAME); - return new ChannelImpl(channelType, nodeRef, name, nodeService); + return new ChannelImpl(channelType, nodeRef, name, this); } public NodeRef addChannelToEnvironment(NodeRef environment, Channel channel, Map properties) @@ -196,10 +203,69 @@ public class ChannelHelper return result; } + public Map getChannelProperties(NodeRef channel) + { + return nodeService.getProperties(channel); + } + public ChildAssociationRef createMapping(NodeRef source, NodeRef publishedNode) { QName qName = QName.createQName(NAMESPACE, GUID.generate()); - return nodeService.addChild(publishedNode, source, ASSOC_SOURCE, qName); + ChildAssociationRef assoc = nodeService.addChild(publishedNode, source, ASSOC_SOURCE, qName); + nodeService.addAspect(source, ASPECT_PUBLISHED, null); + return assoc; + } + + /** + * @param nodeToPublish + * @param channelType + * @return + */ + public boolean canPublish(NodeRef nodeToPublish, ChannelType type) + { + if(type.canPublish() == false) + { + return false; + } + boolean isContentTypeSupported = isContentTypeSupported(nodeToPublish, type); + boolean isMimetypeSupported = isMimetypeSupported(nodeToPublish, type); + return isContentTypeSupported && isMimetypeSupported; + } + + private boolean isMimetypeSupported(NodeRef nodeToPublish, ChannelType type) + { + Set supportedMimetypes = type.getSupportedMimetypes(); + if (supportedMimetypes == null || supportedMimetypes.isEmpty()) + { + return true; + } + QName contentProp = (QName) nodeService.getProperty(nodeToPublish, PROP_CONTENT_PROPERTY_NAME); + if (contentProp == null) + { + String defaultValue = dictionaryService.getProperty(PROP_CONTENT_PROPERTY_NAME).getDefaultValue(); + contentProp = defaultValue == null ? PROP_CONTENT : QName.createQName(defaultValue); + } + ContentReader reader = contentService.getReader(nodeToPublish, contentProp); + return supportedMimetypes.contains(reader.getMimetype()); + } + + private boolean isContentTypeSupported(NodeRef nodeToPublish, ChannelType type) + { + Set supportedContentTypes = type.getSupportedContentTypes(); + if(supportedContentTypes == null || supportedContentTypes.isEmpty()) + { + return true; + } + QName contentType = nodeService.getType(nodeToPublish); + for (QName supportedType : supportedContentTypes) + { + if(contentType.equals(supportedType) + || dictionaryService.isSubClass(contentType, supportedType)) + { + return true; + } + } + return false; } private QName getChannelQName(String channelName) @@ -248,6 +314,12 @@ public class ChannelHelper return null; } + public void sendStatusUpdates(NodeRef nodeToPublish, NodeRef channelNode, ChannelType channelType) + { + //TODO + + } + /** * @param nodeService the nodeService to set */ @@ -263,4 +335,13 @@ public class ChannelHelper { this.dictionaryService = dictionaryService; } + + /** + * @param contentService the contentService to set + */ + public void setContentService(ContentService contentService) + { + this.contentService = contentService; + } + } diff --git a/source/java/org/alfresco/repo/publishing/ChannelHelperTest.java b/source/java/org/alfresco/repo/publishing/ChannelHelperTest.java index bd798db016..b146bae782 100644 --- a/source/java/org/alfresco/repo/publishing/ChannelHelperTest.java +++ b/source/java/org/alfresco/repo/publishing/ChannelHelperTest.java @@ -19,8 +19,14 @@ package org.alfresco.repo.publishing; +import static org.mockito.Mockito.*; +import static junit.framework.Assert.*; + +import org.alfresco.service.cmr.publishing.channels.ChannelType; +import org.alfresco.service.cmr.repository.NodeRef; import org.junit.Test; import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -33,6 +39,9 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @ContextConfiguration(locations = { "classpath:test/alfresco/test-web-publishing-context.xml"}) public class ChannelHelperTest { + @Autowired + private ChannelHelper helper; + @Test public void testMapNodeRef() throws Exception { @@ -44,5 +53,4 @@ public class ChannelHelperTest // NodeRef unmappedNodeRef = environmentHelper.mapEnvironmentToEditorial(liveEnvironmentNode, mappedNodeRef); // assertEquals(testNodeRef, unmappedNodeRef); } - } diff --git a/source/java/org/alfresco/repo/publishing/ChannelImpl.java b/source/java/org/alfresco/repo/publishing/ChannelImpl.java index 50e51f44a6..4e3e8d94cb 100644 --- a/source/java/org/alfresco/repo/publishing/ChannelImpl.java +++ b/source/java/org/alfresco/repo/publishing/ChannelImpl.java @@ -25,7 +25,6 @@ import java.util.Map; import org.alfresco.service.cmr.publishing.channels.Channel; import org.alfresco.service.cmr.publishing.channels.ChannelType; import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.namespace.QName; /** @@ -37,19 +36,19 @@ public class ChannelImpl implements Channel private final NodeRef nodeRef; private final ChannelType channelType; private final String name; - private final NodeService nodeService; + private final ChannelHelper channelHelper; /** * @param channelType * @param name * @param channelService */ - public ChannelImpl(ChannelType channelType, NodeRef nodeRef, String name, NodeService nodeService) + public ChannelImpl(ChannelType channelType, NodeRef nodeRef, String name, ChannelHelper channelHelper) { this.nodeRef = nodeRef; this.channelType = channelType; this.name = name; - this.nodeService = nodeService; + this.channelHelper = channelHelper; } /** @@ -81,7 +80,39 @@ public class ChannelImpl implements Channel */ public Map getProperties() { - return nodeService.getProperties(nodeRef); + return channelHelper.getChannelProperties(nodeRef); + } + + /** + * {@inheritDoc} + */ + @Override + public void publish(NodeRef nodeToPublish) + { + if(channelHelper.canPublish(nodeToPublish, channelType)) + { + channelType.publish(nodeToPublish, getProperties()); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void unPublish(NodeRef nodeToUnpublish) + { + // TODO Auto-generated method stub + + } + + /** + * {@inheritDoc} + */ + @Override + public void updateStatus(String status) + { + // TODO Auto-generated method stub + } } diff --git a/source/java/org/alfresco/repo/publishing/MockChannelType.java b/source/java/org/alfresco/repo/publishing/MockChannelType.java index 368c23984c..3f3bc5d754 100644 --- a/source/java/org/alfresco/repo/publishing/MockChannelType.java +++ b/source/java/org/alfresco/repo/publishing/MockChannelType.java @@ -19,9 +19,12 @@ package org.alfresco.repo.publishing; +import java.io.Serializable; import java.util.Map; +import java.util.Set; import org.alfresco.model.ContentModel; +import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.QName; /** @@ -65,4 +68,77 @@ public class MockChannelType extends AbstractChannelType { return ContentModel.TYPE_FOLDER; } + + /** + * {@inheritDoc} + */ + @Override + public void publish(NodeRef nodeToPublish, Map properties) + { + // NOOP + } + + /** + * {@inheritDoc} + */ + @Override + public void unpublish(NodeRef nodeToUnpublish, Map properties) + { + //NOOP + } + + /** + * {@inheritDoc} + */ + @Override + public void updateStatus(String status, Map properties) + { + //NOOP + } + + /** + * {@inheritDoc} + */ + @Override + public boolean canPublish() + { + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean canUnpublish() + { + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public Set getSupportedMimetypes() + { + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public Set getSupportedContentTypes() + { + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean canPublishStatusUpdates() + { + // TODO Auto-generated method stub + return false; + } } diff --git a/source/java/org/alfresco/repo/publishing/PublishEventActionTest.java b/source/java/org/alfresco/repo/publishing/PublishEventActionTest.java index 01a0be69be..64b3974185 100644 --- a/source/java/org/alfresco/repo/publishing/PublishEventActionTest.java +++ b/source/java/org/alfresco/repo/publishing/PublishEventActionTest.java @@ -32,9 +32,11 @@ import static org.alfresco.model.ContentModel.PROP_LONGITUDE; import static org.alfresco.model.ContentModel.PROP_NAME; import static org.alfresco.model.ContentModel.TYPE_CONTENT; import static org.alfresco.repo.publishing.PublishingModel.ASSOC_LAST_PUBLISHING_EVENT; +import static org.mockito.Mockito.*; import java.io.Serializable; import java.util.Calendar; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -42,6 +44,7 @@ import java.util.Set; import javax.annotation.Resource; +import org.alfresco.model.ContentModel; import org.alfresco.repo.content.MimetypeMap; import org.alfresco.service.cmr.publishing.MutablePublishingPackage; import org.alfresco.service.cmr.publishing.PublishingPackage; @@ -56,6 +59,7 @@ import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.RegexQNamePattern; import org.junit.Test; +import org.mockito.exceptions.verification.NeverWantedButInvoked; import org.springframework.beans.factory.annotation.Autowired; /** @@ -87,6 +91,7 @@ public class PublishEventActionTest extends AbstractPublishingIntegrationTest private NodeRef root; private NodeRef channel; private String eventId; + private ChannelType channelType; @Test public void testPublishNodes() throws Exception @@ -166,6 +171,8 @@ public class PublishEventActionTest extends AbstractPublishingIntegrationTest // Update published node. publishEventNode = publishNode(source); + NodeRef newPublishNode = channelHelper.mapSourceToEnvironment(source, channel); + assertEquals(publishedNode, newPublishNode); // Published node shoudl still exist. assertNotNull(publishedNode); @@ -193,7 +200,9 @@ public class PublishEventActionTest extends AbstractPublishingIntegrationTest // Update publish node publishNode(source); - + newPublishNode = channelHelper.mapSourceToEnvironment(source, channel); + assertEquals(publishedNode, newPublishNode); + aspects = nodeService.getAspects(source); assertFalse(aspects.contains(ASPECT_GEOGRAPHIC)); @@ -202,6 +211,114 @@ public class PublishEventActionTest extends AbstractPublishingIntegrationTest assertFalse(publishedProps.containsKey(PROP_LONGITUDE)); } + @SuppressWarnings("unchecked") + @Test + public void testChannelTypePublishIsCalledOnPublish() throws Exception + { + // Create content node with appropriate aspects added. + NodeRef source = createContentNode(contentNodeName, content); + + // Enable publishing on ChannelType. + when(channelType.canPublish()).thenReturn(true); + + publishNode(source); + NodeRef publishedNode = channelHelper.mapSourceToEnvironment(source, channel); + + // Check publish was called + verify(channelType, times(1)).publish(eq(publishedNode), anyMap()); + } + + public void testChannelTypePublishIsCalledOnUpdate() throws Exception + { + // Create content node with appropriate aspects added. + NodeRef source = createContentNode(contentNodeName, content); + + // Publish source node but dont' call ChannelType.publish(). + publishNode(source); + NodeRef publishedNode = channelHelper.mapSourceToEnvironment(source, channel); + + // Check publish was not called. + verify(channelType, never()).publish(eq(publishedNode), anyMap()); + + // Enable publishing on ChannelType. + when(channelType.canPublish()).thenReturn(true); + + // Update publish node + publishNode(source); + + // Check publish was called on update + verify(channelType, times(1)).publish(eq(publishedNode), anyMap()); + } + + @Test + @SuppressWarnings("unchecked") + public void testSupportedContentTypes() throws Exception + { + // Create content node with appropriate aspects added. + NodeRef source = createContentNode(contentNodeName, content); + + // Enable publishing on ChannelType. + when(channelType.canPublish()).thenReturn(true); + + // Set supported type to cm:folder + Set contentTypes = Collections.singleton(ContentModel.TYPE_FOLDER); + when(channelType.getSupportedContentTypes()).thenReturn(contentTypes); + + // Publish source node but don't call ChannelType.publish(). + publishNode(source); + NodeRef publishedNode = channelHelper.mapSourceToEnvironment(source, channel); + + verify(channelType, never()).publish(eq(publishedNode), anyMap()); + + // Change supported type to cm:content + contentTypes = Collections.singleton(ContentModel.TYPE_CONTENT); + when(channelType.getSupportedContentTypes()).thenReturn(contentTypes); + + // Publish source node + publishNode(source); + + verify(channelType, times(1)).publish(eq(publishedNode), anyMap()); + + // Change supported type to cm:cmobject + contentTypes = Collections.singleton(ContentModel.TYPE_CMOBJECT); + when(channelType.getSupportedContentTypes()).thenReturn(contentTypes); + + // Publish source node + publishNode(source); + + verify(channelType, times(2)).publish(eq(publishedNode), anyMap()); + } + + @Test + @SuppressWarnings("unchecked") + public void testSupportedMimeTypes() throws Exception + { + // Create content node with appropriate aspects added. + NodeRef source = createContentNode(contentNodeName, content); + + // Enable publishing on ChannelType. + when(channelType.canPublish()).thenReturn(true); + + // Set supported type to XML + Set mimeTypes = Collections.singleton(MimetypeMap.MIMETYPE_XML); + when(channelType.getSupportedMimetypes()).thenReturn(mimeTypes); + + // Publish source node but don't call ChannelType.publish(). + publishNode(source); + NodeRef publishedNode = channelHelper.mapSourceToEnvironment(source, channel); + + verify(channelType, never()).publish(eq(publishedNode), anyMap()); + + // Change supported type to plain text. + mimeTypes = Collections.singleton(MimetypeMap.MIMETYPE_TEXT_PLAIN); + when(channelType.getSupportedMimetypes()).thenReturn(mimeTypes); + + // Publish source node + publishNode(source); + + verify(channelType, times(1)).publish(eq(publishedNode), anyMap()); + } + private NodeRef publishNode(NodeRef source) { MutablePublishingPackage pckg = queue.createPublishingPackage(); @@ -238,10 +355,6 @@ public class PublishEventActionTest extends AbstractPublishingIntegrationTest return source; } - /** - * @param source - * @param theContent - */ private void writeContent(NodeRef source, String theContent) { ContentWriter writer = contentService.getWriter(source, PROP_CONTENT, true); @@ -260,8 +373,12 @@ public class PublishEventActionTest extends AbstractPublishingIntegrationTest public void setUp() throws Exception { super.setUp(); - ChannelType channelType = mockChannelType(); + this.channelType = channelService.getChannelType(channelTypeId); + if(channelType == null) + { + this.channelType = mockChannelType(); channelService.register(channelType); + } channelService.createChannel(siteId, channelTypeId, channelName, null); this.channel = channelHelper.getChannelNodeForEnvironment(environment.getNodeRef(), channelName); diff --git a/source/java/org/alfresco/repo/publishing/PublishingEventProcessor.java b/source/java/org/alfresco/repo/publishing/PublishingEventProcessor.java index 3565430606..f4786d0f41 100644 --- a/source/java/org/alfresco/repo/publishing/PublishingEventProcessor.java +++ b/source/java/org/alfresco/repo/publishing/PublishingEventProcessor.java @@ -20,6 +20,7 @@ package org.alfresco.repo.publishing; import static org.alfresco.model.ContentModel.ASSOC_CONTAINS; +import static org.alfresco.repo.publishing.PublishingModel.ASPECT_PUBLISHED; import static org.alfresco.repo.publishing.PublishingModel.ASSOC_LAST_PUBLISHING_EVENT; import static org.alfresco.repo.publishing.PublishingModel.NAMESPACE; @@ -113,18 +114,19 @@ public class PublishingEventProcessor public NodeRef publishEntry(Channel channel, PublishingPackageEntry entry, NodeRef eventNode) { - NodeRef mappedNode = channelHelper.mapSourceToEnvironment(entry.getNodeRef(), channel.getNodeRef()); - if(mappedNode == null) + NodeRef publishedNode = channelHelper.mapSourceToEnvironment(entry.getNodeRef(), channel.getNodeRef()); + if(publishedNode == null) { - mappedNode = publishNewNode(channel.getNodeRef(), entry.getSnapshot()); + publishedNode = publishNewNode(channel.getNodeRef(), entry.getSnapshot()); } else { - updatePublishedNode(mappedNode, entry); + updatePublishedNode(publishedNode, entry); } QName qName = QName.createQName(NAMESPACE, eventNode.getId()); - nodeService.addChild(mappedNode, eventNode, ASSOC_LAST_PUBLISHING_EVENT, qName); - return mappedNode; + nodeService.addChild(publishedNode, eventNode, ASSOC_LAST_PUBLISHING_EVENT, qName); + channel.publish(publishedNode); + return publishedNode; } /** @@ -190,6 +192,7 @@ public class PublishingEventProcessor { Set aspectsToRemove = nodeService.getAspects(publishedNode); aspectsToRemove.removeAll(newAspects); + aspectsToRemove.remove(ASPECT_PUBLISHED); for (QName aspectToRemove : aspectsToRemove) { nodeService.removeAspect(publishedNode, aspectToRemove); diff --git a/source/java/org/alfresco/repo/publishing/WebPublishingTestSuite.java b/source/java/org/alfresco/repo/publishing/WebPublishingTestSuite.java index f057fc8bca..3989919882 100644 --- a/source/java/org/alfresco/repo/publishing/WebPublishingTestSuite.java +++ b/source/java/org/alfresco/repo/publishing/WebPublishingTestSuite.java @@ -35,7 +35,8 @@ import org.junit.runners.Suite; EnvironmentImplTest.class, PublishingQueueImplTest.class, PublishingPackageSerializerTest.class, - PublishingIntegratedTest.class + PublishingIntegratedTest.class, + PublishEventActionTest.class }) public class WebPublishingTestSuite { diff --git a/source/java/org/alfresco/service/cmr/publishing/channels/Channel.java b/source/java/org/alfresco/service/cmr/publishing/channels/Channel.java index 899aa46f19..91a0dfb15b 100644 --- a/source/java/org/alfresco/service/cmr/publishing/channels/Channel.java +++ b/source/java/org/alfresco/service/cmr/publishing/channels/Channel.java @@ -46,4 +46,9 @@ public interface Channel String getName(); Map getProperties(); -} + + void publish(NodeRef nodeToPublish); + void unPublish(NodeRef nodeToUnpublish); + void updateStatus(String status); + +} \ No newline at end of file diff --git a/source/java/org/alfresco/service/cmr/publishing/channels/ChannelType.java b/source/java/org/alfresco/service/cmr/publishing/channels/ChannelType.java index d3fb06117a..88029f413e 100644 --- a/source/java/org/alfresco/service/cmr/publishing/channels/ChannelType.java +++ b/source/java/org/alfresco/service/cmr/publishing/channels/ChannelType.java @@ -19,8 +19,11 @@ package org.alfresco.service.cmr.publishing.channels; +import java.io.Serializable; import java.util.Map; +import java.util.Set; +import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.transfer.NodeFilter; import org.alfresco.service.cmr.transfer.NodeFinder; import org.alfresco.service.namespace.QName; @@ -32,9 +35,18 @@ import org.alfresco.service.namespace.QName; public interface ChannelType { String getId(); - Map getCapabilities(); QName getChannelNodeType(); QName getContentRootNodeType(); NodeFinder getNodeFinder(); NodeFilter getNodeFilter(); + void publish(NodeRef nodeToPublish, Map properties); + void unpublish(NodeRef nodeToUnpublish, Map properties); + void updateStatus(String status, Map properties); + + boolean canPublish(); + boolean canUnpublish(); + boolean canPublishStatusUpdates(); + + Set getSupportedMimetypes(); + Set getSupportedContentTypes(); }