From 7ceaf0a9b09a2285cfdb34af1600b0eb2c9c6aba Mon Sep 17 00:00:00 2001 From: Brian Remmington Date: Thu, 4 Aug 2011 08:26:41 +0000 Subject: [PATCH] Merged BRANCHES/DEV/BRIAN/PUBLISHING to HEAD: 29482: Publishing: - Added support for LinkedIn status updates 29486: Social Publishing UI Updates, including: - Dialogue: Complete UI rework - Publishing History: display of unpublish event support - Created Alfresco.util.toggleClass function 29491: Publishing: - Added correct list of supported MIME types the the YouTube channel type 29493: Publishing: - Added video/mp4 to YouTube supported MIME types 29496: ChannelsGet now filters out channels that are not authorised. Added a space before the node URL on status updates. Extended unit tests to check behaviour for non-Admin users. 29513: Adds specific http client libraries to prevent the mac falling back to it's buggy default implementation (which, e.g. doesn't send Content-Length headers if the content length is zero). Required for Flickr support (their publish API requires both a content length header and zero length content. It returns a 411 error using the default Mac libs). 29534: Fixed PublishingEventsGet REST method. ChannelService getChannels() methods now support filtering by publish permissions. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@29542 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../alfresco/linkedin-publishing-context.xml | 2 +- .../model/linkedInPublishingModel.xml | 11 +- config/alfresco/model/publishingModel.xml | 8 +- .../invitation/site/InviteSenderTest.java | 2 - .../repo/publishing/AbstractChannelType.java | 2 +- .../AbstractPublishingIntegrationTest.java | 78 ++--- .../repo/publishing/ChannelHelper.java | 49 +++- .../alfresco/repo/publishing/ChannelImpl.java | 42 ++- .../repo/publishing/ChannelServiceImpl.java | 47 ++- .../ChannelServiceImplIntegratedTest.java | 185 ++++++------ .../publishing/JaxbHttpMessageConverter.java | 6 + .../publishing/PublishEventActionTest.java | 94 ++---- .../publishing/PublishingEventProcessor.java | 9 +- .../publishing/PublishingQueueImplTest.java | 89 ++---- .../publishing/PublishingRootObjectTest.java | 16 +- .../repo/publishing/PublishingTestHelper.java | 276 ++++++++++++++++++ .../facebook/FacebookChannelType.java | 2 +- .../publishing/flickr/FlickrChannelType.java | 7 +- .../flickr/FlickrUnpublishAction.java | 3 +- .../linkedin/LinkedInChannelType.java | 35 +-- .../linkedin/LinkedInPublishingModel.java | 2 - .../linkedin/springsocial/api/Activity.java | 16 + .../springsocial/api/AlfrescoLinkedIn.java | 28 ++ .../linkedin/springsocial/api/Share.java | 13 + .../springsocial/api/ShareVisibility.java | 7 + .../springsocial/api/ShareVisibilityCode.java | 40 +++ .../api/impl/AlfrescoLinkedInTemplate.java | 91 ++++++ .../api/impl/xml/JaxbActivityImpl.java | 55 ++++ .../api/impl/xml/JaxbShareImpl.java | 44 +++ .../api/impl/xml/JaxbShareVisibilityImpl.java | 41 +++ .../springsocial/api/impl/xml/jaxb.index | 3 + .../linkedin/springsocial/api/jaxb.index | 1 + .../springsocial/connect/LinkedInAdapter.java | 58 ++++ .../connect/LinkedInConnectionFactory.java | 30 ++ .../connect/LinkedInServiceProvider.java | 41 +++ .../twitter/TwitterChannelType.java | 13 + .../youtube/YouTubeChannelType.java | 22 +- .../cmr/publishing/channels/Channel.java | 22 ++ .../publishing/channels/ChannelService.java | 8 +- 39 files changed, 1114 insertions(+), 384 deletions(-) create mode 100644 source/java/org/alfresco/repo/publishing/PublishingTestHelper.java create mode 100644 source/java/org/alfresco/repo/publishing/linkedin/springsocial/api/Activity.java create mode 100644 source/java/org/alfresco/repo/publishing/linkedin/springsocial/api/AlfrescoLinkedIn.java create mode 100644 source/java/org/alfresco/repo/publishing/linkedin/springsocial/api/Share.java create mode 100644 source/java/org/alfresco/repo/publishing/linkedin/springsocial/api/ShareVisibility.java create mode 100644 source/java/org/alfresco/repo/publishing/linkedin/springsocial/api/ShareVisibilityCode.java create mode 100644 source/java/org/alfresco/repo/publishing/linkedin/springsocial/api/impl/AlfrescoLinkedInTemplate.java create mode 100644 source/java/org/alfresco/repo/publishing/linkedin/springsocial/api/impl/xml/JaxbActivityImpl.java create mode 100644 source/java/org/alfresco/repo/publishing/linkedin/springsocial/api/impl/xml/JaxbShareImpl.java create mode 100644 source/java/org/alfresco/repo/publishing/linkedin/springsocial/api/impl/xml/JaxbShareVisibilityImpl.java create mode 100644 source/java/org/alfresco/repo/publishing/linkedin/springsocial/api/impl/xml/jaxb.index create mode 100644 source/java/org/alfresco/repo/publishing/linkedin/springsocial/api/jaxb.index create mode 100644 source/java/org/alfresco/repo/publishing/linkedin/springsocial/connect/LinkedInAdapter.java create mode 100644 source/java/org/alfresco/repo/publishing/linkedin/springsocial/connect/LinkedInConnectionFactory.java create mode 100644 source/java/org/alfresco/repo/publishing/linkedin/springsocial/connect/LinkedInServiceProvider.java diff --git a/config/alfresco/linkedin-publishing-context.xml b/config/alfresco/linkedin-publishing-context.xml index 8fa252cda0..c007df534d 100644 --- a/config/alfresco/linkedin-publishing-context.xml +++ b/config/alfresco/linkedin-publishing-context.xml @@ -13,7 +13,7 @@ - + diff --git a/config/alfresco/model/linkedInPublishingModel.xml b/config/alfresco/model/linkedInPublishingModel.xml index 1f0983ec4c..8d0f875b80 100644 --- a/config/alfresco/model/linkedInPublishingModel.xml +++ b/config/alfresco/model/linkedInPublishingModel.xml @@ -32,16 +32,7 @@ LinkedIn Asset Applied to a node that has been published to LinkedIn - - - LinkedIn Status Id - d:text - - - LinkedIn Status URL - d:text - - + pub:AssetAspect diff --git a/config/alfresco/model/publishingModel.xml b/config/alfresco/model/publishingModel.xml index 4a97c05fda..203ee8d22d 100644 --- a/config/alfresco/model/publishingModel.xml +++ b/config/alfresco/model/publishingModel.xml @@ -282,23 +282,23 @@ false - false + true pub:PublishingEvent false - true + false false - false + true pub:DeliveryChannel false - true + false diff --git a/source/java/org/alfresco/repo/invitation/site/InviteSenderTest.java b/source/java/org/alfresco/repo/invitation/site/InviteSenderTest.java index cc9283b589..cf3d76adf6 100644 --- a/source/java/org/alfresco/repo/invitation/site/InviteSenderTest.java +++ b/source/java/org/alfresco/repo/invitation/site/InviteSenderTest.java @@ -30,7 +30,6 @@ import static org.alfresco.repo.invitation.WorkflowModelNominatedInvitation.wfVa import static org.alfresco.repo.invitation.WorkflowModelNominatedInvitation.wfVarServerPath; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; @@ -66,7 +65,6 @@ import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.cmr.site.SiteInfo; import org.alfresco.service.cmr.site.SiteService; -import org.alfresco.util.ModelUtil; import org.mockito.ArgumentCaptor; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; diff --git a/source/java/org/alfresco/repo/publishing/AbstractChannelType.java b/source/java/org/alfresco/repo/publishing/AbstractChannelType.java index 525545aef5..e46382c45e 100644 --- a/source/java/org/alfresco/repo/publishing/AbstractChannelType.java +++ b/source/java/org/alfresco/repo/publishing/AbstractChannelType.java @@ -139,7 +139,7 @@ public abstract class AbstractChannelType implements ChannelType, InitializingBe @Override public int getMaximumStatusLength() { - return -1; + return 0; } @Override diff --git a/source/java/org/alfresco/repo/publishing/AbstractPublishingIntegrationTest.java b/source/java/org/alfresco/repo/publishing/AbstractPublishingIntegrationTest.java index 83387544f5..94f21ebfd0 100644 --- a/source/java/org/alfresco/repo/publishing/AbstractPublishingIntegrationTest.java +++ b/source/java/org/alfresco/repo/publishing/AbstractPublishingIntegrationTest.java @@ -19,22 +19,17 @@ package org.alfresco.repo.publishing; -import static org.alfresco.repo.publishing.PublishingModel.TYPE_DELIVERY_CHANNEL; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import javax.transaction.Status; -import javax.transaction.UserTransaction; - -import org.alfresco.model.ContentModel; import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.person.TestPersonManager; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.model.FileFolderService; -import org.alfresco.service.cmr.publishing.channels.ChannelType; -import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.publishing.PublishingService; +import org.alfresco.service.cmr.publishing.channels.ChannelService; import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.security.MutableAuthenticationService; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.cmr.site.SiteService; -import org.alfresco.service.cmr.site.SiteVisibility; import org.alfresco.util.BaseSpringTest; import org.alfresco.util.GUID; import org.junit.After; @@ -49,50 +44,42 @@ public abstract class AbstractPublishingIntegrationTest extends BaseSpringTest { protected static final String channelTypeId = "MockChannelType"; - protected PublishingRootObject rootObject; - protected ServiceRegistry serviceRegistry; - - protected SiteService siteService; - protected FileFolderService fileFolderService; protected NodeService nodeService; + protected PublishingTestHelper testHelper; + protected TestPersonManager personManager; + + protected String username; - protected String siteId; - protected PublishingQueueImpl queue; - protected Environment environment; - protected NodeRef docLib; - @Override @Before public void onSetUp() throws Exception { super.onSetUp(); - this.rootObject = (PublishingRootObject) getApplicationContext().getBean("publishingRootObject"); - serviceRegistry = (ServiceRegistry) getApplicationContext().getBean("ServiceRegistry"); - AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); - this.siteService = serviceRegistry.getSiteService(); - this.fileFolderService = serviceRegistry.getFileFolderService(); - this.nodeService = serviceRegistry.getNodeService(); - - this.siteId = GUID.generate(); - siteService.createSite("test", siteId, - "Site created by publishing test", - "Site created by publishing test", - SiteVisibility.PUBLIC); - this.docLib = siteService.createContainer(siteId, SiteService.DOCUMENT_LIBRARY, ContentModel.TYPE_FOLDER, null); - - this.environment = rootObject.getEnvironment(); - this.queue = rootObject.getPublishingQueue(); + serviceRegistry = (ServiceRegistry) getApplicationContext().getBean(ServiceRegistry.SERVICE_REGISTRY); AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); + SiteService siteService = serviceRegistry.getSiteService(); + FileFolderService fileFolderService = serviceRegistry.getFileFolderService(); + PermissionService permissionService = serviceRegistry.getPermissionService(); + this.nodeService = serviceRegistry.getNodeService(); + ChannelService channelService = (ChannelService) getApplicationContext().getBean(ChannelServiceImpl.NAME); + PublishingService publishingService = (PublishingService) getApplicationContext().getBean(PublishServiceImpl.NAME); + MutableAuthenticationService authenticationService= (MutableAuthenticationService) getApplicationContext().getBean(ServiceRegistry.AUTHENTICATION_SERVICE.getLocalName()); + PersonService personService= (PersonService) getApplicationContext().getBean(ServiceRegistry.PERSON_SERVICE.getLocalName()); + + this.personManager = new TestPersonManager(authenticationService, personService, nodeService); + this.testHelper = new PublishingTestHelper(channelService, publishingService, siteService, fileFolderService, permissionService); + + this.username = GUID.generate(); + personManager.createPerson(username); } @After public void onTearDown() throws Exception { - AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); try { - siteService.deleteSite(siteId); + testHelper.tearDown(); } finally { @@ -100,17 +87,4 @@ public abstract class AbstractPublishingIntegrationTest extends BaseSpringTest } } - protected ChannelType mockChannelType() - { - ChannelType channelType = mock(ChannelType.class); - mockChannelTypeBehaviour(channelType); - return channelType; - } - - protected void mockChannelTypeBehaviour(ChannelType channelType) - { - when(channelType.getId()).thenReturn(channelTypeId); - when(channelType.getChannelNodeType()).thenReturn(TYPE_DELIVERY_CHANNEL); - } - } diff --git a/source/java/org/alfresco/repo/publishing/ChannelHelper.java b/source/java/org/alfresco/repo/publishing/ChannelHelper.java index 5f73b554b0..034472313f 100644 --- a/source/java/org/alfresco/repo/publishing/ChannelHelper.java +++ b/source/java/org/alfresco/repo/publishing/ChannelHelper.java @@ -93,15 +93,14 @@ public class ChannelHelper nodeService.createNode(parent, ASSOC_CONTAINS, channelQName, channelNodeType, props); NodeRef channelNode = channelAssoc.getChildRef(); // Allow any user to read Channel permissions. - permissionService.setPermission(channelNode, PermissionService.ALL_AUTHORITIES, PermissionService.READ_PERMISSIONS, true); + permissionService.setPermission(channelNode, PermissionService.ALL_AUTHORITIES, PermissionService.READ_ASSOCIATIONS, true); return channelNode; } public Channel buildChannelObject(NodeRef nodeRef, ChannelService channelService) { if(nodeRef == null || - nodeService.exists(nodeRef)==false || - permissionService.hasPermission(nodeRef, PermissionService.ADD_CHILDREN)!= AccessStatus.ALLOWED) + nodeService.exists(nodeRef)==false) { return null; } @@ -259,16 +258,40 @@ public class ChannelHelper return null; } - public List getChannels(NodeRef channelContainer, final ChannelService channelService) + public List getAllChannels(NodeRef channelContainer, final ChannelService channelService) { List channelAssocs = getChannelAssocs(channelContainer); - return CollectionUtils.transform(channelAssocs, getChannelTransformer(channelService)); + return CollectionUtils.transform(channelAssocs, getChannelTransformer(channelService, false)); } - public List getChannelsByType(NodeRef containerNode, String channelTypeId, ChannelService channelService) + + public List getChannelsForTypes(final NodeRef containerNode, List types, final ChannelService channelService, final boolean checkPermissions) + { + return CollectionUtils.transformFlat(types, new Function>() + { + public List apply(ChannelType channelType) + { + return getChannelsByType(containerNode, channelType.getId(), channelService, checkPermissions); + } + }); + } + + public List getChannelsByType(NodeRef containerNode, String channelTypeId, ChannelService channelService, boolean checkPermissions) { List channelAssocs = getChannelAssocsByType(containerNode, channelTypeId); - return CollectionUtils.transform(channelAssocs, getChannelTransformer(channelService)); + return CollectionUtils.transform(channelAssocs, getChannelTransformer(channelService, checkPermissions)); + } + + public List filterAuthorisedChannels(Collection channels) + { + return CollectionUtils.filter(channels, new Filter() + { + @Override + public Boolean apply(Channel value) + { + return value.isAuthorised(); + } + }); } public List getReleventChannelTypes(final NodeRef nodeToPublish, Collection channelTypes) @@ -334,18 +357,28 @@ public class ChannelHelper return null; } - private Function getChannelTransformer(final ChannelService channelService) + private Function getChannelTransformer(final ChannelService channelService, final boolean checkPermissions) { return new Function() { public Channel apply(ChildAssociationRef value) { NodeRef channelNode = value.getChildRef(); + if(checkPermissions && hasPublishPermissions(channelNode)==false) + { + return null; + } return buildChannelObject(channelNode, channelService); } }; } + public boolean hasPublishPermissions(NodeRef channelNode) + { + AccessStatus access = permissionService.hasPermission(channelNode, PermissionService.ADD_CHILDREN); + return AccessStatus.ALLOWED == access; + } + public boolean isChannelAuthorised(NodeRef channelNode) { Boolean isAuthorised = Boolean.FALSE; diff --git a/source/java/org/alfresco/repo/publishing/ChannelImpl.java b/source/java/org/alfresco/repo/publishing/ChannelImpl.java index afbee6516d..1dd0f56b82 100644 --- a/source/java/org/alfresco/repo/publishing/ChannelImpl.java +++ b/source/java/org/alfresco/repo/publishing/ChannelImpl.java @@ -29,7 +29,8 @@ import org.alfresco.service.namespace.QName; /** * @author Brian - * + * @author Nick Smith + * @since 4.0 */ public class ChannelImpl implements Channel { @@ -38,11 +39,6 @@ public class ChannelImpl implements Channel private final String name; private final ChannelHelper channelHelper; - /** - * @param channelType - * @param name - * @param channelService - */ public ChannelImpl(ChannelType channelType, NodeRef nodeRef, String name, ChannelHelper channelHelper) { this.nodeRef = nodeRef; @@ -57,7 +53,6 @@ public class ChannelImpl implements Channel @Override public String getId() { - // TODO Auto-generated method stub return nodeRef.toString(); } @@ -134,8 +129,41 @@ public class ChannelImpl implements Channel return channelType.getNodeUrl(mappedNode); } + /** + * {@inheritDoc} + */ public boolean isAuthorised() { return channelHelper.isChannelAuthorised(nodeRef); } + + /** + * {@inheritDoc} + */ + public boolean canPublish() + { + return channelType.canPublish() && + isAuthorised() && + channelHelper.hasPublishPermissions(nodeRef); + } + + /** + * {@inheritDoc} + */ + public boolean canUnpublish() + { + return channelType.canPublish() && + isAuthorised() && + channelHelper.hasPublishPermissions(nodeRef); + } + + /** + * {@inheritDoc} + */ + public boolean canPublishStatusUpdates() + { + return channelType.canPublish() && + isAuthorised() && + channelHelper.hasPublishPermissions(nodeRef); + } } diff --git a/source/java/org/alfresco/repo/publishing/ChannelServiceImpl.java b/source/java/org/alfresco/repo/publishing/ChannelServiceImpl.java index 729e89266e..a04a0499c8 100644 --- a/source/java/org/alfresco/repo/publishing/ChannelServiceImpl.java +++ b/source/java/org/alfresco/repo/publishing/ChannelServiceImpl.java @@ -46,7 +46,6 @@ import org.alfresco.service.namespace.QName; import org.alfresco.util.ParameterCheck; import org.alfresco.util.collections.CollectionUtils; import org.alfresco.util.collections.Filter; -import org.alfresco.util.collections.Function; /** * @author Nick Smith @@ -159,7 +158,7 @@ public class ChannelServiceImpl implements ChannelService public List getChannels() { NodeRef channelContainer = getChannelContainer(); - return channelHelper.getChannels(channelContainer, this); + return channelHelper.getAllChannels(channelContainer, this); } /** @@ -201,13 +200,14 @@ public class ChannelServiceImpl implements ChannelService { NodeRef containerNode = getChannelContainer(); List types = channelHelper.getReleventChannelTypes(nodeToPublish, channelTypes.values()); - return getChannelsForTypes(containerNode, types); + List channels = channelHelper.getChannelsForTypes(containerNode, types, this, true); + return channelHelper.filterAuthorisedChannels(channels); } /** * {@inheritDoc} */ - public List getPublishingChannels() + public List getPublishingChannels(boolean filterByPublishPermission) { final NodeRef containerNode = getChannelContainer(); if(containerNode != null) @@ -219,7 +219,7 @@ public class ChannelServiceImpl implements ChannelService return type.canPublish(); } }); - return getChannelsForTypes(containerNode, types); + return channelHelper.getChannelsForTypes(containerNode, types, this, filterByPublishPermission); } return Collections.emptyList(); } @@ -227,16 +227,24 @@ public class ChannelServiceImpl implements ChannelService /** * {@inheritDoc} */ - public List getStatusUpdateChannels() + public List getStatusUpdateChannels(boolean filterByPublishPermission) { final NodeRef containerNode = getChannelContainer(); if (containerNode != null) { List types = channelHelper.getStatusUpdateChannelTypes(channelTypes.values()); - return getChannelsForTypes(containerNode, types); + return channelHelper.getChannelsForTypes(containerNode, types, this, filterByPublishPermission); } return Collections.emptyList(); } + + /** + * {@inheritDoc} + */ + public List getAuthorisedStatusUpdateChannels() + { + return channelHelper.filterAuthorisedChannels(getStatusUpdateChannels(false)); + } /** * {@inheritDoc} @@ -246,21 +254,10 @@ public class ChannelServiceImpl implements ChannelService SiteInfo site = siteService.getSite(nodeToPublish); if(site!=null) { - return getStatusUpdateChannels(); + return getStatusUpdateChannels(false); } return Collections.emptyList(); } - - private List getChannelsForTypes(final NodeRef containerNode, List types) - { - return CollectionUtils.transformFlat(types, new Function>() - { - public List apply(ChannelType channelType) - { - return channelHelper.getChannelsByType(containerNode, channelType.getId(), ChannelServiceImpl.this); - } - }); - } private NodeRef getChannelContainer() { @@ -306,14 +303,9 @@ public class ChannelServiceImpl implements ChannelService } } - /* - * (non-Javadoc) - * - * @see - * org.alfresco.service.cmr.publishing.channels.ChannelService#updateChannel - * (java.lang.String, java.lang.String, java.util.Map) + /** + * {@inheritDoc} */ - @Override public void updateChannel(Channel channel, Map properties) { HashMap actualProps = new HashMap(properties); @@ -328,7 +320,6 @@ public class ChannelServiceImpl implements ChannelService /** * {@inheritDoc} */ - @Override public Channel getChannelById(String id) { if(id!=null&& NodeRef.isNodeRef(id)) @@ -338,5 +329,5 @@ public class ChannelServiceImpl implements ChannelService } return null; } - + } diff --git a/source/java/org/alfresco/repo/publishing/ChannelServiceImplIntegratedTest.java b/source/java/org/alfresco/repo/publishing/ChannelServiceImplIntegratedTest.java index e3e16b0847..61ac8f3756 100644 --- a/source/java/org/alfresco/repo/publishing/ChannelServiceImplIntegratedTest.java +++ b/source/java/org/alfresco/repo/publishing/ChannelServiceImplIntegratedTest.java @@ -19,9 +19,6 @@ package org.alfresco.repo.publishing; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - import java.io.Serializable; import java.util.HashSet; import java.util.List; @@ -32,14 +29,10 @@ import javax.annotation.Resource; import org.alfresco.model.ContentModel; import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.repo.security.person.TestPersonManager; -import org.alfresco.service.ServiceRegistry; +import org.alfresco.repo.security.permissions.AccessDeniedException; 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.security.MutableAuthenticationService; import org.alfresco.service.cmr.security.PermissionService; -import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.namespace.QName; import org.alfresco.util.GUID; import org.alfresco.util.collections.CollectionUtils; @@ -54,29 +47,51 @@ import org.junit.Test; public class ChannelServiceImplIntegratedTest extends AbstractPublishingIntegrationTest { private static final String channelName = GUID.generate(); - private static final String channelTypeName = "MockedChannelType"; - private static boolean channelTypeRegistered = false; @Resource(name="channelService") private ChannelServiceImpl channelService; - private PermissionService permissionService; - private TestPersonManager personManager; - private ChannelType mockedChannelType = mock(ChannelType.class); - @Test public void testCreateChannel() throws Exception { + personManager.setUser(username); + try + { + createChannel(); + fail("Only Admin user can create channels!"); + } + catch(AccessDeniedException e) + { + // NOOP + } + + AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); Channel channel = createChannel(); - assertEquals(channelTypeName, channel.getChannelType().getId()); - assertEquals(channelName, channel.getName()); - assertTrue(nodeService.exists(channel.getNodeRef())); + assertEquals(channelTypeId, channel.getChannelType().getId()); + assertNotNull(channelName, channel.getName()); + NodeRef channelNode = new NodeRef(channel.getId()); + assertTrue(nodeService.exists(channelNode)); } @Test public void testDeleteChannel() throws Exception { Channel channel = createChannel(); + assertNotNull("The channel should exist! Id: "+channel.getId(), channelService.getChannelById(channel.getId())); + assertNotNull("The channel should exist! Name: "+channelName, channelService.getChannelByName(channelName)); + + personManager.setUser(username); + try + { + channelService.deleteChannel(channel); + fail("Only Admin users should be able to delete channels."); + } + catch(AccessDeniedException e) + { + //NOOP + } + + AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); channelService.deleteChannel(channel); assertNull("The channel should have been deleed! Id: "+channel.getId(), channelService.getChannelById(channel.getId())); @@ -88,11 +103,26 @@ public class ChannelServiceImplIntegratedTest extends AbstractPublishingIntegrat { String newChannelName = "New Channel Name"; Channel channel = createChannel(); - channelService.renameChannel(channel, newChannelName); + personManager.setUser(username); + try + { + channelService.renameChannel(channel, newChannelName); + fail("Only Admin user can rename Channel."); + } + catch(AccessDeniedException e) + { + //NOOP + } + + AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); + channelService.renameChannel(channel, newChannelName); Channel renamedChannel = channelService.getChannelById(channel.getId()); assertNotNull(renamedChannel); assertEquals(newChannelName, renamedChannel.getName()); + assertNotNull(channelService.getChannelByName(newChannelName)); + assertNotNull(channelService.getChannelById(channel.getId())); + assertNull(channelService.getChannelByName(channelName)); } @Test @@ -105,8 +135,20 @@ public class ChannelServiceImplIntegratedTest extends AbstractPublishingIntegrat assertNull(props.get(ContentModel.PROP_TITLE)); props.put(ContentModel.PROP_TITLE, newTitle); - channelService.updateChannel(channel, props); + + personManager.setUser(username); + try + { + channelService.updateChannel(channel, props); + fail("Only Admin user can rename Channel."); + } + catch(AccessDeniedException e) + { + //NOOP + } + AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); + channelService.updateChannel(channel, props); Channel updatedChannel = channelService.getChannelById(channel.getId()); Serializable title = updatedChannel.getProperties().get(ContentModel.PROP_TITLE); assertNotNull(title); @@ -119,21 +161,20 @@ public class ChannelServiceImplIntegratedTest extends AbstractPublishingIntegrat int startingSize = channelService.getChannels().size(); int channelCount = 7; - Set channelNames = new HashSet(); + Set ids = new HashSet(); for (int i = 0; i < channelCount; ++i) { - String name = GUID.generate(); - channelNames.add(name); - channelService.createChannel(channelTypeName, name, null); - + Channel newChannel = testHelper.createChannel(channelTypeId); + ids.add(newChannel.getId()); + List channels = channelService.getChannels(); assertEquals(i + 1 + startingSize, channels.size()); - Set names = new HashSet(channelNames); + Set idsToCheck = new HashSet(ids); for (Channel channel : channels) { - names.remove(channel.getName()); + idsToCheck.remove(channel.getId()); } - assertTrue(names.isEmpty()); + assertTrue(idsToCheck.isEmpty()); } } @@ -142,7 +183,6 @@ public class ChannelServiceImplIntegratedTest extends AbstractPublishingIntegrat { // Create Channel as Admin user. Channel channel = createChannel(); - NodeRef channelNode = new NodeRef(channel.getId()); // Create User1 and set as FullyAuthenticatedUser. String user1 = GUID.generate(); @@ -155,12 +195,8 @@ public class ChannelServiceImplIntegratedTest extends AbstractPublishingIntegrat List channels = channelService.getChannels(); assertFalse("Result of getChannels() should not contain the channel!", checkContainsChannel(channel.getId(), channels)); - // Set authentication to Admin - AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); //Add Read permissions to User1. - permissionService.setPermission(channelNode, user1, PermissionService.READ, true); - // Set authentication to User1 - personManager.setUser(user1); + testHelper.setChannelPermission(user1, channel.getId(), PermissionService.READ); // Read permissions should not allow access to the Channel. channelById = channelService.getChannelById(channel.getId()); @@ -168,12 +204,8 @@ public class ChannelServiceImplIntegratedTest extends AbstractPublishingIntegrat channels = channelService.getChannels(); assertFalse("Result of getChannels() should not contain the channel!", checkContainsChannel(channel.getId(), channels)); - // Set authentication to Admin - AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); //Add ADD_CHILD permissions to User1. - permissionService.setPermission(channelNode, user1, PermissionService.ADD_CHILDREN, true); - // Set authentication to User1 - personManager.setUser(user1); + testHelper.setChannelPermission(user1, channel.getId(), PermissionService.ADD_CHILDREN); // Add Child permissions should allow access to the Channel. channelById = channelService.getChannelById(channel.getId()); @@ -183,26 +215,39 @@ public class ChannelServiceImplIntegratedTest extends AbstractPublishingIntegrat } @Test - public void testGetChannel() throws Exception + public void testGetChannelByName() throws Exception { - try - { - channelService.getChannelByName(null); - fail("Should throw an Exception if channelName is null!"); - } - catch (IllegalArgumentException e) { - // NOOP - } - Channel channel = channelService.getChannelByName(channelName); + Channel channel = channelService.getChannelById(null); assertNull("Should return null if unknown channelName", channel); + channel = channelService.getChannelByName(channelName); + assertNull("Should return null if null channelName", channel); + Channel createdChannel = createChannel(); channel = channelService.getChannelByName(channelName); assertNotNull("Should return created channel!", channel); assertEquals(channelName, channel.getName()); assertEquals(createdChannel.getChannelType().getId(), channel.getChannelType().getId()); - assertEquals(createdChannel.getNodeRef(), channel.getNodeRef()); + assertEquals(createdChannel.getId(), channel.getId()); + } + + @Test + public void testGetChannelById() throws Exception + { + Channel channel = channelService.getChannelById(null); + assertNull("Should return null if null channelId", channel); + + channel = channelService.getChannelById("test://channel/id"); + assertNull("Should return null if unknown channelId", channel); + + Channel createdChannel = createChannel(); + + channel = channelService.getChannelById(createdChannel.getId()); + assertNotNull("Should return created channel!", channel); + assertEquals(createdChannel.getId(), channel.getId()); + assertEquals(channelName, channel.getName()); + assertEquals(createdChannel.getChannelType().getId(), channel.getChannelType().getId()); } private boolean checkContainsChannel(final String id, List channels) @@ -217,54 +262,18 @@ public class ChannelServiceImplIntegratedTest extends AbstractPublishingIntegrat Channel result = CollectionUtils.findFirst(channels, acceptor); return result != null; } - + private Channel createChannel() { - return channelService.createChannel(channelTypeName, channelName, null); + return testHelper.createChannel(channelTypeId, channelName); } - @Before @Override public void onSetUp() throws Exception { super.onSetUp(); this.channelService = (ChannelServiceImpl) getApplicationContext().getBean("channelService"); - this.permissionService = (PermissionService) getApplicationContext().getBean(ServiceRegistry.PERMISSIONS_SERVICE.getLocalName()); - MutableAuthenticationService authenticationService= (MutableAuthenticationService) getApplicationContext().getBean(ServiceRegistry.AUTHENTICATION_SERVICE.getLocalName()); - PersonService personService= (PersonService) getApplicationContext().getBean(ServiceRegistry.PERSON_SERVICE.getLocalName()); - - this.personManager = new TestPersonManager(authenticationService, personService, nodeService); - - when(mockedChannelType.getId()).thenReturn(channelTypeName); - when(mockedChannelType.getChannelNodeType()).thenReturn(PublishingModel.TYPE_DELIVERY_CHANNEL); - - if (!channelTypeRegistered) - { - channelService.register(mockedChannelType); - channelTypeRegistered = true; - } - - } - - /** - * {@inheritDoc} - */ - @Override - public void onTearDown() throws Exception - { - AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); - try - { - Channel channel = channelService.getChannelByName(channelName); - if (channel != null) - { - channelService.deleteChannel(channel); - } - } - finally - { - super.onTearDown(); - } + testHelper.mockChannelType(channelTypeId); } } diff --git a/source/java/org/alfresco/repo/publishing/JaxbHttpMessageConverter.java b/source/java/org/alfresco/repo/publishing/JaxbHttpMessageConverter.java index 8ba8975427..ea81b811e1 100644 --- a/source/java/org/alfresco/repo/publishing/JaxbHttpMessageConverter.java +++ b/source/java/org/alfresco/repo/publishing/JaxbHttpMessageConverter.java @@ -96,6 +96,12 @@ public class JaxbHttpMessageConverter extends AbstractXmlHttpMessageConverter clazz, HttpHeaders headers, Source source) throws IOException { diff --git a/source/java/org/alfresco/repo/publishing/PublishEventActionTest.java b/source/java/org/alfresco/repo/publishing/PublishEventActionTest.java index 51357ed334..b0caeff328 100644 --- a/source/java/org/alfresco/repo/publishing/PublishEventActionTest.java +++ b/source/java/org/alfresco/repo/publishing/PublishEventActionTest.java @@ -36,7 +36,6 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.io.Serializable; -import java.util.Calendar; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -48,22 +47,17 @@ 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; import org.alfresco.service.cmr.publishing.PublishingService; import org.alfresco.service.cmr.publishing.Status; import org.alfresco.service.cmr.publishing.StatusUpdate; 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.AssociationRef; 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.NodeRef; import org.alfresco.service.namespace.QName; -import org.alfresco.util.GUID; import org.junit.Test; -import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; /** @@ -79,9 +73,6 @@ public class PublishEventActionTest extends AbstractPublishingIntegrationTest @Resource(name="publishingService") private PublishingService publishingService; - @Resource(name="channelService") - private ChannelService channelService; - @Resource(name="contentService") private ContentService contentService; @@ -93,14 +84,13 @@ public class PublishEventActionTest extends AbstractPublishingIntegrationTest private Channel channel; private NodeRef channelNode; - private String eventId; private ChannelType channelType; - + @Test public void testPublishNodes() throws Exception { // Create content node with appropriate aspects added. - NodeRef source = createContentNode(contentNodeName, content); + NodeRef source = testHelper.createContentNode(contentNodeName, content, MimetypeMap.MIMETYPE_TEXT_PLAIN); double lattitude = 0.25; double longtitude = 0.75; @@ -139,9 +129,9 @@ public class PublishEventActionTest extends AbstractPublishingIntegrationTest public void testUpdatePublishedNode() throws Exception { // Create content node without aspects - NodeRef source = createContentNode(contentNodeName, content); + NodeRef source = testHelper.createContentNode(contentNodeName, content, MimetypeMap.MIMETYPE_TEXT_PLAIN); NodeRef publishEventNode = publishNode(source); - + // Check published node exists NodeRef publishedNode = channelHelper.mapSourceToEnvironment(source, channelNode); assertNotNull(publishedNode); @@ -170,7 +160,7 @@ public class PublishEventActionTest extends AbstractPublishingIntegrationTest String newName = "NewName"; nodeService.setProperty(source, PROP_NAME, newName); String newContent = "The new content"; - writeContent(source, newContent); + testHelper.writeContent(source, newContent, MimetypeMap.MIMETYPE_TEXT_PLAIN); // Update published node. publishEventNode = publishNode(source); @@ -219,7 +209,7 @@ public class PublishEventActionTest extends AbstractPublishingIntegrationTest public void testChannelTypePublishIsCalledOnPublish() throws Exception { // Create content node with appropriate aspects added. - NodeRef source = createContentNode(contentNodeName, content); + NodeRef source = testHelper.createContentNode(contentNodeName, content, MimetypeMap.MIMETYPE_TEXT_PLAIN); // Enable publishing on ChannelType. when(channelType.canPublish()).thenReturn(true); @@ -235,7 +225,7 @@ public class PublishEventActionTest extends AbstractPublishingIntegrationTest public void testChannelTypePublishIsCalledOnUpdate() throws Exception { // Create content node with appropriate aspects added. - NodeRef source = createContentNode(contentNodeName, content); + NodeRef source = testHelper.createContentNode(contentNodeName, content, MimetypeMap.MIMETYPE_TEXT_PLAIN); // Publish source node but dont' call ChannelType.publish(). publishNode(source); @@ -259,7 +249,7 @@ public class PublishEventActionTest extends AbstractPublishingIntegrationTest public void testSupportedContentTypes() throws Exception { // Create content node with appropriate aspects added. - NodeRef source = createContentNode(contentNodeName, content); + NodeRef source = testHelper.createContentNode(contentNodeName, content, MimetypeMap.MIMETYPE_TEXT_PLAIN); // Enable publishing on ChannelType. when(channelType.canPublish()).thenReturn(true); @@ -298,7 +288,7 @@ public class PublishEventActionTest extends AbstractPublishingIntegrationTest public void testSupportedMimeTypes() throws Exception { // Create content node with appropriate aspects added. - NodeRef source = createContentNode(contentNodeName, content); + NodeRef source = testHelper.createContentNode(contentNodeName, content, MimetypeMap.MIMETYPE_TEXT_PLAIN); // Enable publishing on ChannelType. when(channelType.canPublish()).thenReturn(true); @@ -326,11 +316,11 @@ public class PublishEventActionTest extends AbstractPublishingIntegrationTest @SuppressWarnings("unchecked") public void testStatusUpdate() throws Exception { - NodeRef source = createContentNode(contentNodeName, content); + NodeRef source = testHelper.createContentNode(contentNodeName, content, MimetypeMap.MIMETYPE_TEXT_PLAIN); // Create Status Update String message = "Here is the message "; - StatusUpdate status = queue.createStatusUpdate(message, source, channel.getId()); + StatusUpdate status = publishingService.getPublishingQueue().createStatusUpdate(message, source, channel.getId()); String url = "http://test/url"; when(channelType.getNodeUrl(any(NodeRef.class))).thenReturn(url); @@ -349,9 +339,9 @@ public class PublishEventActionTest extends AbstractPublishingIntegrationTest private NodeRef publishNode(NodeRef source, StatusUpdate statusUpdate) { - MutablePublishingPackage pckg = queue.createPublishingPackage(); + MutablePublishingPackage pckg = publishingService.getPublishingQueue().createPublishingPackage(); pckg.addNodesToPublish(source); - scheduleEvent(pckg, statusUpdate); + String eventId = testHelper.scheduleEvent1Year(pckg, channel.getId(), null, statusUpdate); assertNotNull(eventId); NodeRef eventNode = new NodeRef(eventId); @@ -368,36 +358,14 @@ public class PublishEventActionTest extends AbstractPublishingIntegrationTest return eventNode; } - private void scheduleEvent(PublishingPackage publishPckg, StatusUpdate statusUpdate) - { - Calendar schedule = Calendar.getInstance(); - schedule.add(Calendar.YEAR, 1); - this.eventId = queue.scheduleNewEvent(publishPckg, channel.getId(), schedule, null, statusUpdate); - } - private void addGeographicAspect(NodeRef source, double lattitude, double longtitude) { Map props = new HashMap(); props.put(PROP_LATITUDE, lattitude); props.put(PROP_LONGITUDE, longtitude); - nodeService.addAspect(source, ASPECT_GEOGRAPHIC, props); + serviceRegistry.getNodeService().addAspect(source, ASPECT_GEOGRAPHIC, props); } - private NodeRef createContentNode(String name, String theContent) - { - NodeRef source = fileFolderService.create(docLib, name, TYPE_CONTENT).getNodeRef(); - writeContent(source, theContent); - return source; - } - - private void writeContent(NodeRef source, String theContent) - { - ContentWriter writer = contentService.getWriter(source, PROP_CONTENT, true); - writer.setEncoding("UTF-8"); - writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); - writer.putContent(theContent); - } - private String readContent(NodeRef source) { ContentReader reader = contentService.getReader(source, PROP_CONTENT); @@ -409,33 +377,13 @@ public class PublishEventActionTest extends AbstractPublishingIntegrationTest { super.onSetUp(); this.publishingService = (PublishingService) getApplicationContext().getBean("publishingService"); - channelService = (ChannelServiceImpl) getApplicationContext().getBean("channelService"); - contentService = (ContentService) getApplicationContext().getBean("ContentService"); - channelHelper = (ChannelHelper) getApplicationContext().getBean("channelHelper"); - action = (PublishEventAction) getApplicationContext().getBean("pub_publishEvent"); - - this.channelType = channelService.getChannelType(channelTypeId); - if(channelType == null) - { - this.channelType = mockChannelType(); - channelService.register(channelType); - } - else - { - Mockito.reset(channelType); - mockChannelTypeBehaviour(channelType); - } - this.channel = channelService.createChannel(channelTypeId, GUID.generate(), null); - this.channelNode = channel.getNodeRef(); + this.contentService = (ContentService) getApplicationContext().getBean("ContentService"); + this.channelHelper = (ChannelHelper) getApplicationContext().getBean("channelHelper"); + this.action = (PublishEventAction) getApplicationContext().getBean("pub_publishEvent"); + + this.channelType = testHelper.mockChannelType(channelTypeId); + this.channel = testHelper.createChannel(channelTypeId); + this.channelNode = new NodeRef(channel.getId()); } - @Override - public void onTearDown() throws Exception - { - if(eventId !=null) - { - publishingService.cancelPublishingEvent(eventId); - } - super.onTearDown(); - } } diff --git a/source/java/org/alfresco/repo/publishing/PublishingEventProcessor.java b/source/java/org/alfresco/repo/publishing/PublishingEventProcessor.java index eccfb1611b..a65c7456d8 100644 --- a/source/java/org/alfresco/repo/publishing/PublishingEventProcessor.java +++ b/source/java/org/alfresco/repo/publishing/PublishingEventProcessor.java @@ -50,6 +50,8 @@ import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.RegexQNamePattern; import org.alfresco.util.GUID; import org.alfresco.util.ParameterCheck; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; /** * @author Nick Smith @@ -58,6 +60,8 @@ import org.alfresco.util.ParameterCheck; */ public class PublishingEventProcessor { + private static final Log log = LogFactory.getLog(PublishingEventProcessor.class); + private PublishingEventHelper eventHelper; private ChannelHelper channelHelper; private ChannelService channelService; @@ -91,6 +95,7 @@ public class PublishingEventProcessor } catch(Exception e) { + log.error("Caught exception while processing publishing event " + eventNode, e); fail(eventNode, e.getMessage()); } finally @@ -112,7 +117,7 @@ public class PublishingEventProcessor String nodeUrl = publishChannel.getUrl(node); if(nodeUrl != null) { - message += urlShortener.shortenUrl(nodeUrl); + message += " " + urlShortener.shortenUrl(nodeUrl); } } Set channels = update.getChannelIds(); @@ -148,9 +153,9 @@ public class PublishingEventProcessor } - public void fail(NodeRef eventNode, String msg) { + log.error("Failed to process publishing event " + eventNode + ": " + msg); String completedStatus = Status.FAILED.name(); nodeService.setProperty(eventNode, PublishingModel.PROP_PUBLISHING_EVENT_STATUS, completedStatus); } diff --git a/source/java/org/alfresco/repo/publishing/PublishingQueueImplTest.java b/source/java/org/alfresco/repo/publishing/PublishingQueueImplTest.java index f8d7299139..bc31e5f3cd 100644 --- a/source/java/org/alfresco/repo/publishing/PublishingQueueImplTest.java +++ b/source/java/org/alfresco/repo/publishing/PublishingQueueImplTest.java @@ -33,11 +33,7 @@ import java.util.Map; import java.util.Set; import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.model.ContentModel; import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.repo.security.permissions.AccessDeniedException; -import org.alfresco.repo.security.person.TestPersonManager; -import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.publishing.MutablePublishingPackage; import org.alfresco.service.cmr.publishing.PublishingEvent; import org.alfresco.service.cmr.publishing.PublishingPackage; @@ -48,9 +44,7 @@ import org.alfresco.service.cmr.publishing.StatusUpdate; 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.security.MutableAuthenticationService; import org.alfresco.service.cmr.security.PermissionService; -import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.cmr.workflow.WorkflowInstance; import org.alfresco.service.cmr.workflow.WorkflowPath; import org.alfresco.service.cmr.workflow.WorkflowService; @@ -70,11 +64,6 @@ public class PublishingQueueImplTest extends AbstractPublishingIntegrationTest private PublishingService publishingService; private WorkflowService workflowService; - private PermissionService permissionService; - private TestPersonManager personManager; - private ChannelServiceImpl channelService; - - private String eventId; @Test public void testScheduleNewPublishingEvent() throws Exception @@ -84,7 +73,7 @@ public class PublishingQueueImplTest extends AbstractPublishingIntegrationTest assertNull(nodeService.getProperty(firstNode, PROP_VERSION_LABEL)); assertNull(nodeService.getProperty(firstNode, PROP_VERSION_LABEL)); - MutablePublishingPackage publishingPackage = queue.createPublishingPackage(); + MutablePublishingPackage publishingPackage = publishingService.getPublishingQueue().createPublishingPackage(); publishingPackage.addNodesToPublish(firstNode, secondNode); //TODO Implement Unpublish @@ -94,7 +83,7 @@ public class PublishingQueueImplTest extends AbstractPublishingIntegrationTest Calendar schedule = Calendar.getInstance(); schedule.add(Calendar.HOUR, 2); - this.eventId = queue.scheduleNewEvent(publishingPackage, channelId, schedule, comment, null); + String eventId = publishingService.getPublishingQueue().scheduleNewEvent(publishingPackage, channelId, schedule, comment, null); //Check schedule triggered versioning. Serializable version = nodeService.getProperty(firstNode, PROP_VERSION_LABEL); @@ -156,14 +145,14 @@ public class PublishingQueueImplTest extends AbstractPublishingIntegrationTest List channelNames = Arrays.asList("test://channel/Channel1", "test://channel/Channel2", "test://channel/Channel3" ); String message = "The message"; - StatusUpdate update = queue.createStatusUpdate(message, secondNode, channelNames); + StatusUpdate update = publishingService.getPublishingQueue().createStatusUpdate(message, secondNode, channelNames); // Publish an event with the StatusUpdate - MutablePublishingPackage publishingPackage = queue.createPublishingPackage(); + MutablePublishingPackage publishingPackage = publishingService.getPublishingQueue().createPublishingPackage(); publishingPackage.addNodesToPublish(firstNode, secondNode); Calendar schedule = Calendar.getInstance(); schedule.add(Calendar.HOUR, 2); - this.eventId = queue.scheduleNewEvent(publishingPackage, channelId, schedule, comment, update); + String eventId = testHelper.scheduleEvent1Year(publishingPackage, channelId, comment, update); PublishingEvent event = publishingService.getPublishingEvent(eventId); StatusUpdate actualUpdate = event.getStatusUpdate(); @@ -178,12 +167,9 @@ public class PublishingQueueImplTest extends AbstractPublishingIntegrationTest public void testScheduleNewEventPermissions() throws Exception { // Create Channels as Admin - ChannelType channelType = mockChannelType(); - channelService.register(channelType); - Channel publishChannel = channelService.createChannel(channelType.getId(), "Channel1", null); - NodeRef publishChannelNode = new NodeRef(publishChannel.getId()); - Channel statusChannel = channelService.createChannel(channelType.getId(), "Channel2", null); - NodeRef statusChannelNode = new NodeRef(statusChannel.getId()); + ChannelType channelType = testHelper.mockChannelType(channelTypeId); + Channel publishChannel = testHelper.createChannel(channelTypeId); + Channel statusChannel = testHelper.createChannel(channelTypeId); NodeRef firstNode = createContent("First"); NodeRef secondNode = createContent("Second"); @@ -191,18 +177,16 @@ public class PublishingQueueImplTest extends AbstractPublishingIntegrationTest // Create User1, add read permissions and set as current user. String user1 = GUID.generate(); personManager.createPerson(user1); - permissionService.setPermission(publishChannelNode, user1, PermissionService.READ, true); - permissionService.setPermission(statusChannelNode, user1, PermissionService.READ, true); + testHelper.setChannelPermission(user1, publishChannel.getId(), PermissionService.READ); + testHelper.setChannelPermission(user1, statusChannel.getId(), PermissionService.READ); personManager.setUser(user1); // Publish an event - MutablePublishingPackage publishingPackage = queue.createPublishingPackage(); + MutablePublishingPackage publishingPackage = publishingService.getPublishingQueue().createPublishingPackage(); publishingPackage.addNodesToPublish(firstNode, secondNode); - Calendar schedule = Calendar.getInstance(); - schedule.add(Calendar.HOUR, 2); try { - this.eventId = queue.scheduleNewEvent(publishingPackage, publishChannel.getId(), schedule, comment, null); + testHelper.scheduleEvent1Year(publishingPackage, publishChannel.getId(), null, null); fail("shceduleNewEvent should have thrown an AccessDeniedException!"); } catch(AlfrescoRuntimeException e) @@ -211,20 +195,17 @@ public class PublishingQueueImplTest extends AbstractPublishingIntegrationTest } // Set Add Child permission on publish channel. - AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); - permissionService.setPermission(publishChannelNode, user1, PermissionService.ADD_CHILDREN, true); - personManager.setUser(user1); + testHelper.allowChannelAccess(user1, publishChannel.getId()); // Check publish works now. - this.eventId = queue.scheduleNewEvent(publishingPackage, publishChannel.getId(), schedule, comment, null); + String eventId = testHelper.scheduleEvent1Year(publishingPackage, publishChannel.getId(), null, null); assertNotNull(eventId); - publishingService.cancelPublishingEvent(eventId); String message = "The message"; - StatusUpdate update = queue.createStatusUpdate(message, secondNode, statusChannel.getId()); + StatusUpdate update = publishingService.getPublishingQueue().createStatusUpdate(message, secondNode, statusChannel.getId()); try { - this.eventId = queue.scheduleNewEvent(publishingPackage, publishChannel.getId(), schedule, comment, update); + eventId = testHelper.scheduleEvent1Year(publishingPackage, publishChannel.getId(), null, update); fail("shceduleNewEvent with status update should have thrown an AccessDeniedException!"); } catch(AlfrescoRuntimeException e) @@ -233,18 +214,16 @@ public class PublishingQueueImplTest extends AbstractPublishingIntegrationTest } // Set Add Child permission on status channel. - AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); - permissionService.setPermission(statusChannelNode, user1, PermissionService.ADD_CHILDREN, true); - personManager.setUser(user1); - + testHelper.allowChannelAccess(user1, statusChannel.getId()); + // Check publish works now. - this.eventId = queue.scheduleNewEvent(publishingPackage, publishChannel.getId(), schedule, comment, null); + eventId = testHelper.scheduleEvent1Year(publishingPackage, publishChannel.getId(), null, update); assertNotNull(eventId); } private NodeRef createContent(String name) { - return fileFolderService.create(docLib, name, ContentModel.TYPE_CONTENT).getNodeRef(); + return testHelper.createContentNode(name); } /** @@ -256,34 +235,6 @@ public class PublishingQueueImplTest extends AbstractPublishingIntegrationTest super.onSetUp(); this.workflowService = serviceRegistry.getWorkflowService(); this.publishingService = (PublishingService) getApplicationContext().getBean("publishingService"); - this.channelService = (ChannelServiceImpl) getApplicationContext().getBean("channelService"); - - this.permissionService = (PermissionService) getApplicationContext().getBean(ServiceRegistry.PERMISSIONS_SERVICE.getLocalName()); - MutableAuthenticationService authenticationService= (MutableAuthenticationService) getApplicationContext().getBean(ServiceRegistry.AUTHENTICATION_SERVICE.getLocalName()); - PersonService personService= (PersonService) getApplicationContext().getBean(ServiceRegistry.PERSON_SERVICE.getLocalName()); - - this.personManager = new TestPersonManager(authenticationService, personService, nodeService); } - /** - * {@inheritDoc} - * @throws Exception - */ - @Override - public void onTearDown() throws Exception - { - AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); - if(eventId!=null) - { - try - { - publishingService.cancelPublishingEvent(eventId); - } - catch(Exception e) - { - //NOOP - } - } - super.onTearDown(); - } } diff --git a/source/java/org/alfresco/repo/publishing/PublishingRootObjectTest.java b/source/java/org/alfresco/repo/publishing/PublishingRootObjectTest.java index 0578ab3ff5..3a5258b2f9 100644 --- a/source/java/org/alfresco/repo/publishing/PublishingRootObjectTest.java +++ b/source/java/org/alfresco/repo/publishing/PublishingRootObjectTest.java @@ -28,7 +28,7 @@ import org.alfresco.service.cmr.repository.NodeRef; import org.junit.Test; /** - * @author Brian + * @author Nick Smith * */ public class PublishingRootObjectTest extends AbstractPublishingIntegrationTest @@ -36,13 +36,6 @@ public class PublishingRootObjectTest extends AbstractPublishingIntegrationTest @Resource(name="publishingRootObject") private PublishingRootObject rootObject; - @Override - public void onSetUp() throws Exception - { - super.onSetUp(); - this.rootObject = (PublishingRootObject) getApplicationContext().getBean(PublishingRootObject.NAME); - } - @Test public void testGetEnvironment() throws Exception { @@ -77,4 +70,11 @@ public class PublishingRootObjectTest extends AbstractPublishingIntegrationTest assertEquals(rootObject.getEnvironment().getNodeRef(), parentAssoc.getParentRef()); } + @Override + public void onSetUp() throws Exception + { + super.onSetUp(); + this.rootObject = (PublishingRootObject) getApplicationContext().getBean(PublishingRootObject.NAME); + } + } diff --git a/source/java/org/alfresco/repo/publishing/PublishingTestHelper.java b/source/java/org/alfresco/repo/publishing/PublishingTestHelper.java new file mode 100644 index 0000000000..b3dc009057 --- /dev/null +++ b/source/java/org/alfresco/repo/publishing/PublishingTestHelper.java @@ -0,0 +1,276 @@ +/* + * 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.publishing; + +import static org.alfresco.model.ContentModel.TYPE_CONTENT; +import static org.alfresco.repo.publishing.PublishingModel.TYPE_DELIVERY_CHANNEL; +import static org.alfresco.repo.publishing.PublishingModel.PROP_AUTHORISATION_COMPLETE; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.when; + +import java.io.File; +import java.io.Serializable; +import java.util.Calendar; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.service.cmr.model.FileFolderService; +import org.alfresco.service.cmr.publishing.PublishingPackage; +import org.alfresco.service.cmr.publishing.PublishingQueue; +import org.alfresco.service.cmr.publishing.PublishingService; +import org.alfresco.service.cmr.publishing.StatusUpdate; +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.ContentWriter; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.cmr.site.SiteService; +import org.alfresco.service.cmr.site.SiteVisibility; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.GUID; +import org.alfresco.util.collections.CollectionUtils; + +/** + * @author Nick Smith + * @since 4.0 + * + */ +public class PublishingTestHelper +{ + private final ChannelService channelService; + private final PublishingService publishingService; + private final SiteService siteService; + private final FileFolderService fileFolderService; + private final PermissionService permissionService; + + private final NodeRef docLib; + private final String siteId = GUID.generate(); + + private final List channels = new LinkedList(); + private final List events = new LinkedList(); + + public PublishingTestHelper(ChannelService channelService, + PublishingService publishingService, + SiteService siteService, + FileFolderService fileFolderService, + PermissionService permissionService) + { + this.channelService = channelService; + this.publishingService = publishingService; + this.siteService = siteService; + this.fileFolderService = fileFolderService; + this.permissionService = permissionService; + + AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); + siteService.createSite("test", siteId, + "Test site created by ChannelServiceImplIntegratedTest", + "Test site created by ChannelServiceImplIntegratedTest", + SiteVisibility.PUBLIC); + this.docLib = siteService.createContainer(siteId, SiteService.DOCUMENT_LIBRARY, ContentModel.TYPE_FOLDER, null); + } + + + public void tearDown() throws Exception + { + AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); + try + { + cancelAllEvents(); + } + finally + { + try + { + deleteAllChannels(); + } + finally + { + deleteSite(); + } + } + } + + private void deleteSite() + { + try + { + siteService.deleteSite(siteId); + } + catch(Throwable t) + { + //NOOP + } + } + + private void deleteAllChannels() + { + for (Channel channel : channels) + { + try + { + channelService.deleteChannel(channel); + } + catch(Throwable t) + { + //NOOP + } + } + } + + private void cancelAllEvents() + { + for (String event : events) + { + try + { + publishingService.cancelPublishingEvent(event); + } + catch(Throwable t) + { + //NOOP + } + } + } + + public ChannelType mockChannelType(String channelTypeId) + { + ChannelType channelType = channelService.getChannelType(channelTypeId); + if(channelType != null) + { + reset(channelType); + when(channelType.getId()).thenReturn(channelTypeId); + } + else + { + channelType = mock(ChannelType.class); + when(channelType.getId()).thenReturn(channelTypeId); + channelService.register(channelType); + } + when(channelType.getChannelNodeType()).thenReturn(TYPE_DELIVERY_CHANNEL); + return channelType; + } + + public Channel createChannel(String typeId) + { + String channelName = GUID.generate(); + return createChannel(typeId, channelName); + } + + public Channel createChannel(String typeId, String channelName) + { + return createChannel(typeId, channelName, true); + } + + public Channel createChannel(String typeId, String channelName, boolean isAuthorised) + { + Channel channel = channelService.createChannel(typeId, channelName, null); + channels.add(channel); + Map properties = Collections.singletonMap(PROP_AUTHORISATION_COMPLETE, (Serializable)isAuthorised); + channelService.updateChannel(channel, properties); + return channel; + } + + public void allowChannelAccess(String username, String channelId) + { + setChannelPermission(username, channelId, PermissionService.ADD_CHILDREN); + } + + public void setChannelPermission(final String username, String channelId, final String permission) + { + final NodeRef channel = new NodeRef(channelId); + AuthenticationUtil.runAs(new RunAsWork() + { + public Void doWork() throws Exception + { + permissionService.setPermission(channel, username, permission, true); + return null; + } + }, AuthenticationUtil.getSystemUserName()); + } + + public String scheduleEvent1Year(PublishingPackage pckg, String channelId, String comment, StatusUpdate statusUpdate) + { + Calendar schedule = Calendar.getInstance(); + schedule.add(Calendar.YEAR, 1); + return scheduleEvent(pckg, channelId, schedule, comment, statusUpdate); + } + + public String scheduleEvent(PublishingPackage pckg, String channelId, Calendar schedule, String comment, StatusUpdate statusUpdate) + { + PublishingQueue queue = publishingService.getPublishingQueue(); + String eventId = queue.scheduleNewEvent(pckg, channelId, schedule, comment, statusUpdate); + events.add(eventId); + return eventId; + } + + public void addEvent(String eventId) + { + events.add(eventId); + } + + public void addEvents(Collection eventIds) + { + events.addAll(eventIds); + } + + public NodeRef createContentNode(String name) + { + return fileFolderService.create(docLib, name, TYPE_CONTENT).getNodeRef(); + } + + public NodeRef createContentNode(String name, File theContent, String mimetype) + { + NodeRef source = createContentNode(name); + writeContent(source, theContent, mimetype); + return source; + } + + public NodeRef createContentNode(String name, String theContent, String mimetype) + { + NodeRef source = fileFolderService.create(docLib, name, TYPE_CONTENT).getNodeRef(); + writeContent(source, theContent, mimetype); + return source; + } + + public void writeContent(NodeRef source, String theContent, String mimetype) + { + ContentWriter writer = fileFolderService.getWriter(source); + writer.setEncoding("UTF-8"); + writer.putContent(theContent); + writer.setMimetype(mimetype); + } + + public void writeContent(NodeRef source, File theContent, String mimetype) + { + ContentWriter writer = fileFolderService.getWriter(source); + writer.setMimetype(mimetype); + writer.setEncoding("UTF-8"); + writer.putContent(theContent); + } + +} diff --git a/source/java/org/alfresco/repo/publishing/facebook/FacebookChannelType.java b/source/java/org/alfresco/repo/publishing/facebook/FacebookChannelType.java index 8c671d4b89..bac6f3d453 100644 --- a/source/java/org/alfresco/repo/publishing/facebook/FacebookChannelType.java +++ b/source/java/org/alfresco/repo/publishing/facebook/FacebookChannelType.java @@ -40,7 +40,7 @@ import org.springframework.social.oauth2.OAuth2Parameters; public class FacebookChannelType extends AbstractChannelType { public final static String ID = "facebook"; - public final static String DEFAULT_REDIRECT_URI = "http://cognite.net"; + public final static String DEFAULT_REDIRECT_URI = "http://cognite.net/alfresco/stand-alone-auth-return.html"; private FacebookPublishingHelper publishingHelper; private String redirectUri = DEFAULT_REDIRECT_URI; diff --git a/source/java/org/alfresco/repo/publishing/flickr/FlickrChannelType.java b/source/java/org/alfresco/repo/publishing/flickr/FlickrChannelType.java index 7bb224953e..6407571b19 100644 --- a/source/java/org/alfresco/repo/publishing/flickr/FlickrChannelType.java +++ b/source/java/org/alfresco/repo/publishing/flickr/FlickrChannelType.java @@ -105,10 +105,6 @@ public class FlickrChannelType extends AbstractOAuth1ChannelType @Override public void publish(NodeRef nodeToPublish, Map properties) { - // TODO Nick S: Not sure it is very useful to use an Action hee. - // The Action assumes the nodeToPublish is under a properly configured DeliveryChannel. - // Ie. the action assumes the node was generated via the Publishing Service. - // The Action only really has value if it can be called independant of the Publishing Service IMO. Action publishAction = actionService.createAction(FlickrPublishAction.NAME); actionService.executeAction(publishAction, nodeToPublish); } @@ -116,7 +112,8 @@ public class FlickrChannelType extends AbstractOAuth1ChannelType @Override public void unpublish(NodeRef nodeToUnpublish, Map properties) { - //NOOP + Action action = actionService.createAction(FlickrUnpublishAction.NAME); + actionService.executeAction(action, nodeToUnpublish); } @Override diff --git a/source/java/org/alfresco/repo/publishing/flickr/FlickrUnpublishAction.java b/source/java/org/alfresco/repo/publishing/flickr/FlickrUnpublishAction.java index 3032bc1364..f2d0d59909 100644 --- a/source/java/org/alfresco/repo/publishing/flickr/FlickrUnpublishAction.java +++ b/source/java/org/alfresco/repo/publishing/flickr/FlickrUnpublishAction.java @@ -73,7 +73,6 @@ public class FlickrUnpublishAction extends ActionExecuterAbstractBase @Override protected void addParameterDefinitions(List paramList) { - // TODO Auto-generated method stub - + //Deliberately empty } } diff --git a/source/java/org/alfresco/repo/publishing/linkedin/LinkedInChannelType.java b/source/java/org/alfresco/repo/publishing/linkedin/LinkedInChannelType.java index 4417302f59..4ec04edaff 100644 --- a/source/java/org/alfresco/repo/publishing/linkedin/LinkedInChannelType.java +++ b/source/java/org/alfresco/repo/publishing/linkedin/LinkedInChannelType.java @@ -26,22 +26,22 @@ import java.util.Map; import java.util.Set; import org.alfresco.repo.publishing.AbstractOAuth1ChannelType; +import org.alfresco.repo.publishing.linkedin.springsocial.api.AlfrescoLinkedIn; import org.alfresco.service.cmr.publishing.channels.Channel; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.QName; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.social.connect.Connection; -import org.springframework.social.linkedin.api.LinkedIn; -import org.springframework.social.oauth1.OAuth1Parameters; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; /** * @author Nick Smith * @since 4.0 */ -public class LinkedInChannelType extends AbstractOAuth1ChannelType +public class LinkedInChannelType extends AbstractOAuth1ChannelType { - public final static String ID = "linkedIn"; + private final static Log log = LogFactory.getLog(LinkedInChannelType.class); + public final static String ID = "linkedin"; @Override public boolean canPublish() @@ -97,12 +97,22 @@ public class LinkedInChannelType extends AbstractOAuth1ChannelType //NO-OP } + @Override + public int getMaximumStatusLength() + { + return 700; + } + @Override public void updateStatus(Channel channel, String status, Map properties) { NodeRef channelNode = new NodeRef(channel.getId()); - Connection connection = getConnectionForChannel(channelNode); - // TODO update status + Connection connection = getConnectionForChannel(channelNode); + if (log.isInfoEnabled()) + { + log.info("Posting update to LinkedIn channel " + channel.getName() + ": " + status); + } + connection.getApi().shareComment(status); } @Override @@ -110,13 +120,4 @@ public class LinkedInChannelType extends AbstractOAuth1ChannelType { throw new UnsupportedOperationException(); } - - @Override - protected OAuth1Parameters getOAuth1Parameters(String callbackUrl) - { - MultiValueMap params = new LinkedMultiValueMap(); - params.add("perms", "delete"); - return new OAuth1Parameters(callbackUrl, params); - } - } diff --git a/source/java/org/alfresco/repo/publishing/linkedin/LinkedInPublishingModel.java b/source/java/org/alfresco/repo/publishing/linkedin/LinkedInPublishingModel.java index 90bef1cbe4..cc63dba13a 100644 --- a/source/java/org/alfresco/repo/publishing/linkedin/LinkedInPublishingModel.java +++ b/source/java/org/alfresco/repo/publishing/linkedin/LinkedInPublishingModel.java @@ -33,6 +33,4 @@ public interface LinkedInPublishingModel public static final QName TYPE_DELIVERY_CHANNEL = QName.createQName(NAMESPACE, "DeliveryChannel"); public static final QName ASPECT_ASSET = QName.createQName(NAMESPACE, "AssetAspect"); - public static final QName PROP_ASSET_ID = QName.createQName(NAMESPACE, "assetId"); - public static final QName PROP_ASSET_URL = QName.createQName(NAMESPACE, "assetUrl"); } diff --git a/source/java/org/alfresco/repo/publishing/linkedin/springsocial/api/Activity.java b/source/java/org/alfresco/repo/publishing/linkedin/springsocial/api/Activity.java new file mode 100644 index 0000000000..f3eac116e3 --- /dev/null +++ b/source/java/org/alfresco/repo/publishing/linkedin/springsocial/api/Activity.java @@ -0,0 +1,16 @@ +package org.alfresco.repo.publishing.linkedin.springsocial.api; + +public interface Activity +{ + public String getContentType(); + + public void setContentType(String value); + + public String getBody(); + + public void setBody(String value); + + public String getLocale(); + + public void setLocale(String value); +} diff --git a/source/java/org/alfresco/repo/publishing/linkedin/springsocial/api/AlfrescoLinkedIn.java b/source/java/org/alfresco/repo/publishing/linkedin/springsocial/api/AlfrescoLinkedIn.java new file mode 100644 index 0000000000..bb6c77b165 --- /dev/null +++ b/source/java/org/alfresco/repo/publishing/linkedin/springsocial/api/AlfrescoLinkedIn.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.publishing.linkedin.springsocial.api; + +import org.springframework.social.linkedin.api.LinkedIn; + +public interface AlfrescoLinkedIn extends LinkedIn +{ + void postNetworkUpdate(String update); + + void shareComment(String comment); +} diff --git a/source/java/org/alfresco/repo/publishing/linkedin/springsocial/api/Share.java b/source/java/org/alfresco/repo/publishing/linkedin/springsocial/api/Share.java new file mode 100644 index 0000000000..1f786a8b10 --- /dev/null +++ b/source/java/org/alfresco/repo/publishing/linkedin/springsocial/api/Share.java @@ -0,0 +1,13 @@ +package org.alfresco.repo.publishing.linkedin.springsocial.api; + +public interface Share +{ + public String getComment(); + + public void setComment(String comment); + + public ShareVisibility getVisibility(); + + public void setVisibility(ShareVisibility visibility); + +} diff --git a/source/java/org/alfresco/repo/publishing/linkedin/springsocial/api/ShareVisibility.java b/source/java/org/alfresco/repo/publishing/linkedin/springsocial/api/ShareVisibility.java new file mode 100644 index 0000000000..fd30ad567a --- /dev/null +++ b/source/java/org/alfresco/repo/publishing/linkedin/springsocial/api/ShareVisibility.java @@ -0,0 +1,7 @@ +package org.alfresco.repo.publishing.linkedin.springsocial.api; + +public interface ShareVisibility +{ + ShareVisibilityCode getCode(); + void setCode(ShareVisibilityCode code); +} diff --git a/source/java/org/alfresco/repo/publishing/linkedin/springsocial/api/ShareVisibilityCode.java b/source/java/org/alfresco/repo/publishing/linkedin/springsocial/api/ShareVisibilityCode.java new file mode 100644 index 0000000000..b6f1901ad8 --- /dev/null +++ b/source/java/org/alfresco/repo/publishing/linkedin/springsocial/api/ShareVisibilityCode.java @@ -0,0 +1,40 @@ +package org.alfresco.repo.publishing.linkedin.springsocial.api; + +import javax.xml.bind.annotation.XmlEnum; +import javax.xml.bind.annotation.XmlEnumValue; + +@XmlEnum +public enum ShareVisibilityCode +{ + + @XmlEnumValue("anyone") + ANYONE("anyone"), + + @XmlEnumValue("connections-only") + CONNECTIONS_ONLY("connections-only"); + + private final String value; + + ShareVisibilityCode(String value) + { + this.value = value; + } + + public String value() + { + return value; + } + + public static ShareVisibilityCode fromValue(String value) + { + for (ShareVisibilityCode validCode : ShareVisibilityCode.values()) + { + if (validCode.value.equals(value)) + { + return validCode; + } + } + throw new IllegalArgumentException(value); + } + +} diff --git a/source/java/org/alfresco/repo/publishing/linkedin/springsocial/api/impl/AlfrescoLinkedInTemplate.java b/source/java/org/alfresco/repo/publishing/linkedin/springsocial/api/impl/AlfrescoLinkedInTemplate.java new file mode 100644 index 0000000000..2e7fdb3b21 --- /dev/null +++ b/source/java/org/alfresco/repo/publishing/linkedin/springsocial/api/impl/AlfrescoLinkedInTemplate.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.publishing.linkedin.springsocial.api.impl; + +import java.util.List; + +import org.alfresco.repo.publishing.JaxbHttpMessageConverter; +import org.alfresco.repo.publishing.linkedin.springsocial.api.Activity; +import org.alfresco.repo.publishing.linkedin.springsocial.api.AlfrescoLinkedIn; +import org.alfresco.repo.publishing.linkedin.springsocial.api.Share; +import org.alfresco.repo.publishing.linkedin.springsocial.api.impl.xml.JaxbActivityImpl; +import org.alfresco.repo.publishing.linkedin.springsocial.api.impl.xml.JaxbShareImpl; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.social.linkedin.api.impl.LinkedInTemplate; +import org.springframework.social.support.URIBuilder; + +public class AlfrescoLinkedInTemplate extends LinkedInTemplate implements AlfrescoLinkedIn +{ + private static String JAXB_CONTEXT_PATH = "org.alfresco.repo.publishing.linkedin.springsocial.api.impl.xml:" + + "org.alfresco.repo.publishing.linkedin.springsocial.api"; + + public AlfrescoLinkedInTemplate(String consumerKey, String consumerSecret, String accessToken, + String accessTokenSecret) + { + super(consumerKey, consumerSecret, accessToken, accessTokenSecret); + } + + protected List> getMessageConverters() + { + List> messageConverters = super.getMessageConverters(); + messageConverters.add(new JaxbHttpMessageConverter(JAXB_CONTEXT_PATH)); + return messageConverters; + } + + @Override + public void postNetworkUpdate(String update) + { + if (update == null || update.trim().length() == 0) + return; + + URIBuilder uriBuilder = URIBuilder.fromUri("http://api.linkedin.com/v1/people/~/person-activities"); + + Activity activity = new JaxbActivityImpl(); + activity.setBody(update); + + HttpEntity entity = buildEntity(activity); + getRestTemplate().postForObject(uriBuilder.build(), entity, String.class); + } + + @Override + public void shareComment(String comment) + { + if (comment == null || comment.trim().length() == 0) + return; + + URIBuilder uriBuilder = URIBuilder.fromUri("http://api.linkedin.com/v1/people/~/shares"); + + Share share = new JaxbShareImpl(); + share.setComment(comment); + + HttpEntity entity = buildEntity(share); + getRestTemplate().postForLocation(uriBuilder.build(), entity); + } + + private HttpEntity buildEntity(Object body) + { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.TEXT_XML); + + return new HttpEntity(body, headers); + } +} diff --git a/source/java/org/alfresco/repo/publishing/linkedin/springsocial/api/impl/xml/JaxbActivityImpl.java b/source/java/org/alfresco/repo/publishing/linkedin/springsocial/api/impl/xml/JaxbActivityImpl.java new file mode 100644 index 0000000000..7ea921181e --- /dev/null +++ b/source/java/org/alfresco/repo/publishing/linkedin/springsocial/api/impl/xml/JaxbActivityImpl.java @@ -0,0 +1,55 @@ +package org.alfresco.repo.publishing.linkedin.springsocial.api.impl.xml; + +import java.util.Locale; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + +import org.alfresco.repo.publishing.linkedin.springsocial.api.Activity; + +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = {"contentType", "body"}) +@XmlRootElement(name = "activity") +public class JaxbActivityImpl implements Activity +{ + @XmlElement(name = "content-type") + protected String contentType = "linkedin-html"; + @XmlElement(required = true) + protected String body; + @XmlAttribute(required = true) + protected String locale = Locale.getDefault().toString(); + + public String getContentType() + { + return contentType; + } + + public void setContentType(String value) + { + this.contentType = value; + } + + public String getBody() + { + return body; + } + + public void setBody(String value) + { + this.body = value; + } + + public String getLocale() + { + return locale; + } + + public void setLocale(String value) + { + this.locale = value; + } +} diff --git a/source/java/org/alfresco/repo/publishing/linkedin/springsocial/api/impl/xml/JaxbShareImpl.java b/source/java/org/alfresco/repo/publishing/linkedin/springsocial/api/impl/xml/JaxbShareImpl.java new file mode 100644 index 0000000000..9577ac8c85 --- /dev/null +++ b/source/java/org/alfresco/repo/publishing/linkedin/springsocial/api/impl/xml/JaxbShareImpl.java @@ -0,0 +1,44 @@ +package org.alfresco.repo.publishing.linkedin.springsocial.api.impl.xml; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + +import org.alfresco.repo.publishing.linkedin.springsocial.api.Share; +import org.alfresco.repo.publishing.linkedin.springsocial.api.ShareVisibility; +import org.alfresco.repo.publishing.linkedin.springsocial.api.ShareVisibilityCode; + +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { "comment", "visibility" }) +@XmlRootElement(name = "share") +public class JaxbShareImpl implements Share +{ + @XmlElement(required = true, name = "comment") + protected String comment; + + @XmlElement(required = true, name = "visibility") + protected JaxbShareVisibilityImpl visibility = new JaxbShareVisibilityImpl(ShareVisibilityCode.ANYONE); + + public String getComment() + { + return comment; + } + + public void setComment(String comment) + { + this.comment = comment; + } + + public ShareVisibility getVisibility() + { + return visibility; + } + + public void setVisibility(ShareVisibility visibility) + { + this.visibility = new JaxbShareVisibilityImpl(visibility.getCode()); + } + +} diff --git a/source/java/org/alfresco/repo/publishing/linkedin/springsocial/api/impl/xml/JaxbShareVisibilityImpl.java b/source/java/org/alfresco/repo/publishing/linkedin/springsocial/api/impl/xml/JaxbShareVisibilityImpl.java new file mode 100644 index 0000000000..2fe81ee711 --- /dev/null +++ b/source/java/org/alfresco/repo/publishing/linkedin/springsocial/api/impl/xml/JaxbShareVisibilityImpl.java @@ -0,0 +1,41 @@ +package org.alfresco.repo.publishing.linkedin.springsocial.api.impl.xml; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + +import org.alfresco.repo.publishing.linkedin.springsocial.api.ShareVisibility; +import org.alfresco.repo.publishing.linkedin.springsocial.api.ShareVisibilityCode; + +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { "code" }) +@XmlRootElement(name = "visibility") +public class JaxbShareVisibilityImpl implements ShareVisibility +{ + @XmlElement(required = true) + protected ShareVisibilityCode code; + + public JaxbShareVisibilityImpl() + { + super(); + } + + public JaxbShareVisibilityImpl(ShareVisibilityCode code) + { + super(); + this.code = code; + } + + public ShareVisibilityCode getCode() + { + return code; + } + + public void setCode(ShareVisibilityCode value) + { + this.code = value; + } + +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/publishing/linkedin/springsocial/api/impl/xml/jaxb.index b/source/java/org/alfresco/repo/publishing/linkedin/springsocial/api/impl/xml/jaxb.index new file mode 100644 index 0000000000..4da699670d --- /dev/null +++ b/source/java/org/alfresco/repo/publishing/linkedin/springsocial/api/impl/xml/jaxb.index @@ -0,0 +1,3 @@ +JaxbShareImpl +JaxbShareVisibilityImpl +JaxbActivityImpl diff --git a/source/java/org/alfresco/repo/publishing/linkedin/springsocial/api/jaxb.index b/source/java/org/alfresco/repo/publishing/linkedin/springsocial/api/jaxb.index new file mode 100644 index 0000000000..c090449d8f --- /dev/null +++ b/source/java/org/alfresco/repo/publishing/linkedin/springsocial/api/jaxb.index @@ -0,0 +1 @@ +ShareVisibilityCode \ No newline at end of file diff --git a/source/java/org/alfresco/repo/publishing/linkedin/springsocial/connect/LinkedInAdapter.java b/source/java/org/alfresco/repo/publishing/linkedin/springsocial/connect/LinkedInAdapter.java new file mode 100644 index 0000000000..e151141bef --- /dev/null +++ b/source/java/org/alfresco/repo/publishing/linkedin/springsocial/connect/LinkedInAdapter.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.publishing.linkedin.springsocial.connect; + +import org.alfresco.repo.publishing.linkedin.springsocial.api.AlfrescoLinkedIn; +import org.springframework.social.connect.ApiAdapter; +import org.springframework.social.connect.ConnectionValues; +import org.springframework.social.connect.UserProfile; +import org.springframework.social.connect.UserProfileBuilder; +import org.springframework.social.linkedin.api.LinkedInProfile; +import org.springframework.web.client.HttpClientErrorException; + +public class LinkedInAdapter implements ApiAdapter +{ + public boolean test(AlfrescoLinkedIn linkedin) { + try { + linkedin.getUserProfile(); + return true; + } catch (HttpClientErrorException e) { + // TODO: Have api throw more specific exception and trigger off of that. + return false; + } + } + + public void setConnectionValues(AlfrescoLinkedIn linkedin, ConnectionValues values) { + LinkedInProfile profile = linkedin.getUserProfile(); + values.setProviderUserId(profile.getId()); + values.setDisplayName(profile.getFirstName() + " " + profile.getLastName()); + values.setProfileUrl(profile.getPublicProfileUrl()); + values.setImageUrl(profile.getProfilePictureUrl()); + } + + public UserProfile fetchUserProfile(AlfrescoLinkedIn linkedin) { + LinkedInProfile profile = linkedin.getUserProfile(); + return new UserProfileBuilder().setName(profile.getFirstName() + " " + profile.getLastName()).build(); + } + + public void updateStatus(AlfrescoLinkedIn linkedin, String message) { + // not supported yet + } + +} diff --git a/source/java/org/alfresco/repo/publishing/linkedin/springsocial/connect/LinkedInConnectionFactory.java b/source/java/org/alfresco/repo/publishing/linkedin/springsocial/connect/LinkedInConnectionFactory.java new file mode 100644 index 0000000000..c50dcc5743 --- /dev/null +++ b/source/java/org/alfresco/repo/publishing/linkedin/springsocial/connect/LinkedInConnectionFactory.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.publishing.linkedin.springsocial.connect; + +import org.alfresco.repo.publishing.linkedin.springsocial.api.AlfrescoLinkedIn; +import org.springframework.social.connect.support.OAuth1ConnectionFactory; + +public class LinkedInConnectionFactory extends OAuth1ConnectionFactory{ + + public LinkedInConnectionFactory(String consumerKey, String consumerSecret) { + super("linkedin", new LinkedInServiceProvider(consumerKey, consumerSecret), new LinkedInAdapter()); + } + +} diff --git a/source/java/org/alfresco/repo/publishing/linkedin/springsocial/connect/LinkedInServiceProvider.java b/source/java/org/alfresco/repo/publishing/linkedin/springsocial/connect/LinkedInServiceProvider.java new file mode 100644 index 0000000000..4fc0dff191 --- /dev/null +++ b/source/java/org/alfresco/repo/publishing/linkedin/springsocial/connect/LinkedInServiceProvider.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.publishing.linkedin.springsocial.connect; + +import org.alfresco.repo.publishing.linkedin.springsocial.api.AlfrescoLinkedIn; +import org.alfresco.repo.publishing.linkedin.springsocial.api.impl.AlfrescoLinkedInTemplate; +import org.springframework.social.oauth1.AbstractOAuth1ServiceProvider; +import org.springframework.social.oauth1.OAuth1Template; + +public class LinkedInServiceProvider extends AbstractOAuth1ServiceProvider { + + public LinkedInServiceProvider(String consumerKey, String consumerSecret) { + super(consumerKey, consumerSecret, new OAuth1Template(consumerKey, consumerSecret, + "https://api.linkedin.com/uas/oauth/requestToken", + "https://www.linkedin.com/uas/oauth/authorize", + "https://www.linkedin.com/uas/oauth/authenticate", + "https://api.linkedin.com/uas/oauth/accessToken")); + } + + public AlfrescoLinkedIn getApi(String accessToken, String secret) { + return new AlfrescoLinkedInTemplate(getConsumerKey(), getConsumerSecret(), accessToken, secret); + } + + +} diff --git a/source/java/org/alfresco/repo/publishing/twitter/TwitterChannelType.java b/source/java/org/alfresco/repo/publishing/twitter/TwitterChannelType.java index 3977053159..d601c9aa5c 100644 --- a/source/java/org/alfresco/repo/publishing/twitter/TwitterChannelType.java +++ b/source/java/org/alfresco/repo/publishing/twitter/TwitterChannelType.java @@ -27,11 +27,14 @@ import org.alfresco.repo.publishing.AbstractOAuth1ChannelType; import org.alfresco.service.cmr.publishing.channels.Channel; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.QName; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.social.connect.Connection; import org.springframework.social.twitter.api.Twitter; public class TwitterChannelType extends AbstractOAuth1ChannelType { + private final static Log log = LogFactory.getLog(TwitterChannelType.class); public final static String ID = "twitter"; @Override @@ -88,10 +91,20 @@ public class TwitterChannelType extends AbstractOAuth1ChannelType //NO-OP } + @Override + public int getMaximumStatusLength() + { + return 140; + } + @Override public void updateStatus(Channel channel, String status, Map properties) { Connection connection = getConnectionForChannel(channel.getNodeRef()); + if (log.isInfoEnabled()) + { + log.info("Posting update to Twitter channel " + channel.getName() + ": " + status); + } connection.getApi().timelineOperations().updateStatus(status); } diff --git a/source/java/org/alfresco/repo/publishing/youtube/YouTubeChannelType.java b/source/java/org/alfresco/repo/publishing/youtube/YouTubeChannelType.java index 8d02125cd3..7fd89325e4 100644 --- a/source/java/org/alfresco/repo/publishing/youtube/YouTubeChannelType.java +++ b/source/java/org/alfresco/repo/publishing/youtube/YouTubeChannelType.java @@ -22,7 +22,9 @@ import java.io.Serializable; import java.util.Collections; import java.util.Map; import java.util.Set; +import java.util.TreeSet; +import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.publishing.AbstractChannelType; import org.alfresco.repo.publishing.PublishingModel; import org.alfresco.service.cmr.action.Action; @@ -31,9 +33,22 @@ import org.alfresco.service.cmr.publishing.channels.Channel; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.namespace.QName; +import org.alfresco.util.collections.CollectionUtils; public class YouTubeChannelType extends AbstractChannelType { + private final static Set DEFAULT_SUPPORTED_MIME_TYPES = CollectionUtils.unmodifiableSet( + MimetypeMap.MIMETYPE_VIDEO_MPG, + MimetypeMap.MIMETYPE_VIDEO_MP4, + MimetypeMap.MIMETYPE_VIDEO_FLV, + MimetypeMap.MIMETYPE_VIDEO_3GP, + MimetypeMap.MIMETYPE_VIDEO_AVI, + MimetypeMap.MIMETYPE_VIDEO_QUICKTIME, + MimetypeMap.MIMETYPE_VIDEO_WMV + ); + + private Set supportedMimeTypes = DEFAULT_SUPPORTED_MIME_TYPES; + public final static String ID = "youtube"; private NodeService nodeService; private ActionService actionService; @@ -48,6 +63,11 @@ public class YouTubeChannelType extends AbstractChannelType this.actionService = actionService; } + public void setSupportedMimeTypes(Set supportedMimeTypes) + { + this.supportedMimeTypes = Collections.unmodifiableSet(new TreeSet(supportedMimeTypes)); + } + @Override public boolean canPublish() { @@ -87,7 +107,7 @@ public class YouTubeChannelType extends AbstractChannelType @Override public Set getSupportedMimeTypes() { - return Collections.emptySet(); + return supportedMimeTypes; } @Override 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 376914b572..64f421f7ec 100644 --- a/source/java/org/alfresco/service/cmr/publishing/channels/Channel.java +++ b/source/java/org/alfresco/service/cmr/publishing/channels/Channel.java @@ -76,4 +76,26 @@ public interface Channel * @return true if this channel has been authorised and is ready for use. */ boolean isAuthorised(); + + /** + * Returns true only if the currently authenticated user can publish content to this {@link Channel}. + * If the {@link ChannelType} does not support publishing, if the {@link Channel} is not authorised or if the currently authenticated user does not have permission to publish to this {@link Channel} then this method will return false. + * @return + */ + boolean canPublish(); + + /** + * Returns true only if the currently authenticated user can unpublish content from this {@link Channel}. + * If the {@link ChannelType} does not support unpublishing, if the {@link Channel} is not authorised or if the currently authenticated user does not have permission to publish to this {@link Channel} then this method will return false. + * @return + */ + boolean canUnpublish(); + + /** + * Returns true only if the currently authenticated user can unpublish status updates to this {@link Channel}. + * If the {@link ChannelType} does not support publishing of status updates, if the {@link Channel} is not authorised or if the currently authenticated user does not have permission to publish to this {@link Channel} then this method will return false. + * @return + */ + boolean canPublishStatusUpdates(); + } \ No newline at end of file diff --git a/source/java/org/alfresco/service/cmr/publishing/channels/ChannelService.java b/source/java/org/alfresco/service/cmr/publishing/channels/ChannelService.java index 77bde220cb..bb23673df7 100644 --- a/source/java/org/alfresco/service/cmr/publishing/channels/ChannelService.java +++ b/source/java/org/alfresco/service/cmr/publishing/channels/ChannelService.java @@ -113,14 +113,16 @@ public interface ChannelService /** * Returns a list of all the channels that are capable of publishing in the specified Share site. + * @param filterByPublishPermission TODO * @return */ - List getPublishingChannels(); + List getPublishingChannels(boolean filterByPublishPermission); /** * Returns all {@link Channel}s cpaable of performing a status update for the given Share Site. + * @param filterByPublishPermission TODO * @return */ - List getStatusUpdateChannels(); - + List getStatusUpdateChannels(boolean filterByPublishPermission); + }