From 869afa4673116d78db53fd46afe3e0f8423befc9 Mon Sep 17 00:00:00 2001 From: Brian Remmington Date: Mon, 11 Jul 2011 11:21:06 +0000 Subject: [PATCH] Publishing: added authorisation framework for channel types. Migrated YouTube channel type onto it, and added Twitter and SlideShare channel types. Facebook is also added, but there is an issue with the way it authorises apps, so it isn't wired in yet. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@28910 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../application-context-highlevel.xml | 3 +- .../model/facebookPublishingModel.xml | 53 ++ config/alfresco/model/publishingModel.xml | 591 ++++++++++-------- .../model/slidesharePublishingModel.xml | 53 ++ .../alfresco/model/twitterPublishingModel.xml | 47 ++ .../alfresco/model/youtubePublishingModel.xml | 13 +- .../slideshare-publishing-context.xml | 30 + .../alfresco/twitter-publishing-context.xml | 28 + .../alfresco/youtube-publishing-context.xml | 5 +- .../repo/publishing/AbstractChannelType.java | 86 ++- .../alfresco/repo/publishing/ChannelImpl.java | 2 +- .../repo/publishing/ChannelServiceImpl.java | 1 + .../repo/publishing/MockChannelType.java | 9 +- .../publishing/PublishEventActionTest.java | 4 +- .../repo/publishing/PublishingModel.java | 8 + .../facebook/FacebookChannelType.java | 168 +++++ .../facebook/FacebookPublishingHelper.java | 71 +++ .../facebook/FacebookPublishingModel.java | 40 ++ .../slideshare/SlideShareChannelType.java | 151 +++++ .../slideshare/SlideSharePublishAction.java | 138 ++++ .../SlideSharePublishingHelper.java | 64 ++ .../slideshare/SlideSharePublishingModel.java | 40 ++ .../springsocial/ConnectionSerializer.java | 45 -- .../springsocial/OAuth1ChannelType.java | 71 --- .../publishing/test/TestChannelType1.java | 10 +- .../publishing/test/TestChannelType2.java | 10 +- .../publishing/test/TestChannelType3.java | 10 +- .../twitter/TwitterChannelType.java | 169 +++++ .../twitter/TwitterPublishingHelper.java | 68 ++ .../twitter/TwitterPublishingModel.java | 38 ++ .../youtube/YouTubeChannelType.java | 3 +- .../youtube/YouTubePublishingHelper.java | 5 +- .../youtube/YouTubePublishingModel.java | 2 - .../repo/publishing/youtube/YouTubeTest.java | 5 +- .../cmr/publishing/channels/ChannelType.java | 6 +- 35 files changed, 1608 insertions(+), 439 deletions(-) create mode 100644 config/alfresco/model/facebookPublishingModel.xml create mode 100644 config/alfresco/model/slidesharePublishingModel.xml create mode 100644 config/alfresco/model/twitterPublishingModel.xml create mode 100644 config/alfresco/slideshare-publishing-context.xml create mode 100644 config/alfresco/twitter-publishing-context.xml create mode 100644 source/java/org/alfresco/repo/publishing/facebook/FacebookChannelType.java create mode 100644 source/java/org/alfresco/repo/publishing/facebook/FacebookPublishingHelper.java create mode 100644 source/java/org/alfresco/repo/publishing/facebook/FacebookPublishingModel.java create mode 100644 source/java/org/alfresco/repo/publishing/slideshare/SlideShareChannelType.java create mode 100644 source/java/org/alfresco/repo/publishing/slideshare/SlideSharePublishAction.java create mode 100644 source/java/org/alfresco/repo/publishing/slideshare/SlideSharePublishingHelper.java create mode 100644 source/java/org/alfresco/repo/publishing/slideshare/SlideSharePublishingModel.java delete mode 100644 source/java/org/alfresco/repo/publishing/springsocial/ConnectionSerializer.java delete mode 100644 source/java/org/alfresco/repo/publishing/springsocial/OAuth1ChannelType.java create mode 100644 source/java/org/alfresco/repo/publishing/twitter/TwitterChannelType.java create mode 100644 source/java/org/alfresco/repo/publishing/twitter/TwitterPublishingHelper.java create mode 100644 source/java/org/alfresco/repo/publishing/twitter/TwitterPublishingModel.java diff --git a/config/alfresco/application-context-highlevel.xml b/config/alfresco/application-context-highlevel.xml index 3641baae8e..0359d3d76e 100644 --- a/config/alfresco/application-context-highlevel.xml +++ b/config/alfresco/application-context-highlevel.xml @@ -26,6 +26,7 @@ - + + diff --git a/config/alfresco/model/facebookPublishingModel.xml b/config/alfresco/model/facebookPublishingModel.xml new file mode 100644 index 0000000000..3c83e65adf --- /dev/null +++ b/config/alfresco/model/facebookPublishingModel.xml @@ -0,0 +1,53 @@ + + + Alfresco Facebook Publishing Content Model + Alfresco + 2011-06-15 + 1.0 + + + + + + + + + + + + + + + Facebook Delivery Channel + Node type used to represent Facebook delivery channels + pub:DeliveryChannel + + facebook:DeliveryChannelAspect + + + + + + + + Facebook Delivery Channel Aspect + Applied to a node that represents a Facebook delivery channel + pub:UserPasswordDeliveryChannelAspect + + + + Facebook Asset + Applied to a node that has been published to Facebook + + + Facebook Asset Id + d:text + + + Facebook Asset URL + d:text + + + + + diff --git a/config/alfresco/model/publishingModel.xml b/config/alfresco/model/publishingModel.xml index 4c6f6df52e..61630cfdbd 100644 --- a/config/alfresco/model/publishingModel.xml +++ b/config/alfresco/model/publishingModel.xml @@ -1,292 +1,331 @@ - Alfresco Publishing Content Model - Alfresco - 2011-05-04 - 1.0 + Alfresco Publishing Content Model + Alfresco + 2011-05-04 + 1.0 - - - - - - + + + + + + - - - + + + - - - Delivery Channel - The base type for all delivery channels - cm:folder - - - Channel Type Id - d:text - false - - - - - - false - false - - - pub:DeliveryServer - false - true - - - - + + + Delivery Channel + The base type for all delivery channels + cm:folder + + + Channel Type Id + d:text + false + + + Has the channel authorisation process been completed? + d:boolean + false + + + + + + false + false + + + pub:DeliveryServer + false + true + + + + - - Delivery Channel Container - A container type that holds a set of delivery channels within a Share site - cm:folder - - st:siteContainer - - + + Delivery Channel Container + A container type that holds a set of delivery channels within a Share site + cm:folder + + st:siteContainer + + - - Delivery Server - The base type for all delivery servers - sys:base - + + Delivery Server + The base type for all delivery servers + sys:base + - - Publishing Environment - A container type that holds a set of delivery channels - cm:folder - - - - false - false - - - pub:PublishingQueue - false - false - - - - + + Publishing Environment + A container type that holds a set of delivery channels + cm:folder + + + + false + false + + + pub:PublishingQueue + false + false + + + + - - Publishing Queue - A container type that holds publishing events that are yet to be processed + + Publishing Queue + A container type that holds publishing events that are yet to be processed - sys:base - - - - false - false - - - pub:PublishingEvent - false - true - - - - + sys:base + + + + false + false + + + pub:PublishingEvent + false + true + + + + - - Publishing Event - Holds details of a publishing event - sys:base - - - Status - d:text - true - - - - - SCHEDULED - IN_PROGRESS - CANCEL_REQUESTED - COMPLETED - FAILED - - - - - - - Payload - d:content - false - - true - false - false - - - - Nodes to publish - d:text - true - - true - false - false - - - - Nodes to unpublish - d:text - true - - true - false - false - - - - Nodes depended on - d:text - true - - true - false - false - - - - Scheduled time for the event - d:datetime - - - Time zone of the scheduled time for the event - d:text - - - Comment for the event - d:text - - - Channel to publish to - d:text - - - The Id of the associated Publishing Event Workflow Instance - d:text - - - The status update message - d:text - - - The status update NodeRef used to generate a URL - d:text - - - The names of the channels to be notified of this status update. - d:text - true - - - - cm:auditable - - + + Publishing Event + Holds details of a publishing event + sys:base + + + Status + d:text + true + + + + + SCHEDULED + IN_PROGRESS + CANCEL_REQUESTED + COMPLETED + FAILED + + + + + + + Payload + d:content + false + + true + false + false + + + + Nodes to publish + d:text + true + + true + false + false + + + + Nodes to unpublish + d:text + true + + true + false + false + + + + Nodes depended on + d:text + true + + true + false + false + + + + Scheduled time for the event + d:datetime + + + Time zone of the scheduled time for the event + d:text + + + Comment for the event + d:text + + + Channel to publish to + d:text + + + The Id of the associated Publishing Event Workflow Instance + d:text + + + The status update message + d:text + + + The status update NodeRef used to generate a URL + d:text + + + The names of the channels to be notified of this status update. + d:text + true + + + + cm:auditable + + - - Publishing Connectiont - Holds details of an OAuth connection - cm:cmobject - - - Account Id - d:any - true - - - Provider Id - d:text - true - - - Provider Account Id - d:text - true - - - Access Token - d:text - true - - - Access Secret - d:text - true - - - Refresh Token - d:text - true - - - + + Publishing Connectiont + Holds details of an OAuth connection + cm:cmobject + + + Account Id + d:any + true + + + Provider Id + d:text + true + + + Provider Account Id + d:text + true + + + Access Token + d:text + true + + + Access Secret + d:text + true + + + Refresh Token + d:text + true + + + - + - - - Channel Info - Applied to a node that exists within a Delivery Channel - cm:titled - - - Containing Channel Node - d:noderef - false - - - Containing Channel Type - d:text - false - - - + + + Channel Info + Applied to a node that exists within a Delivery Channel + cm:titled + + + Containing Channel Node + d:noderef + false + + + Containing Channel Type + d:text + false + + + - - Channel Info - Applied to a published node - cm:titled - - - - false - true - - - cm:content - false - false - - - - - false - false - - - pub:PublishingEvent - false - true - - - - - - + + Channel Info + Applied to a published node + cm:titled + + + + false + true + + + cm:content + false + false + + + + + false + false + + + pub:PublishingEvent + false + true + + + + + + + OAuth1 Authenticated Delivery Channel + Applied to delivery channels that use OAuth1 + + + The value of the OAuth1 token + d:text + false + + + The secret of the OAuth1 token + d:text + false + + + + + + Username and Password Authenticated Delivery Channel + Applied to delivery channels that use OAuth1 + + + The authenticated channel username + d:text + false + + + The authenticated channel password + d:text + false + + + + + diff --git a/config/alfresco/model/slidesharePublishingModel.xml b/config/alfresco/model/slidesharePublishingModel.xml new file mode 100644 index 0000000000..95ae9d7001 --- /dev/null +++ b/config/alfresco/model/slidesharePublishingModel.xml @@ -0,0 +1,53 @@ + + + Alfresco YouTube Publishing Content Model + Alfresco + 2011-06-15 + 1.0 + + + + + + + + + + + + + + + SlideShare Delivery Channel + Node type used to represent SlideShare delivery channels + pub:DeliveryChannel + + slideshare:DeliveryChannelAspect + + + + + + + + SlideShare Delivery Channel Aspect + Applied to a node that represents a SlideShare delivery channel + pub:UserPasswordDeliveryChannelAspect + + + + SlideShare Asset + Applied to a node that has been published to SlideShare + + + SlideShare Asset Id + d:text + + + SlideShare Asset URL + d:text + + + + + diff --git a/config/alfresco/model/twitterPublishingModel.xml b/config/alfresco/model/twitterPublishingModel.xml new file mode 100644 index 0000000000..f08f5a04e0 --- /dev/null +++ b/config/alfresco/model/twitterPublishingModel.xml @@ -0,0 +1,47 @@ + + + Alfresco Twitter Publishing Content Model + Alfresco + 2011-06-15 + 1.0 + + + + + + + + + + + + + + + Twitter Delivery Channel + Node type used to represent Twitter delivery channels + pub:DeliveryChannel + + pub:OAuth1DeliveryChannelAspect + + + + + + + + Twitter Asset + Applied to a node that has been published to Twitter + + + Twitter Status Id + d:text + + + Twitter Status URL + d:text + + + + + diff --git a/config/alfresco/model/youtubePublishingModel.xml b/config/alfresco/model/youtubePublishingModel.xml index 17d806b21d..9b2d2ff807 100644 --- a/config/alfresco/model/youtubePublishingModel.xml +++ b/config/alfresco/model/youtubePublishingModel.xml @@ -32,18 +32,7 @@ YouTube Delivery Channel Applied to a node that represents a YouTube delivery channel - - - YouTube User Name - d:text - false - - - YouTube Password - d:text - false - - + pub:UserPasswordDeliveryChannelAspect diff --git a/config/alfresco/slideshare-publishing-context.xml b/config/alfresco/slideshare-publishing-context.xml new file mode 100644 index 0000000000..af80052544 --- /dev/null +++ b/config/alfresco/slideshare-publishing-context.xml @@ -0,0 +1,30 @@ + + + + + + + + alfresco/model/slidesharePublishingModel.xml + + + + + + + + + + + + + + + + + + + + + + diff --git a/config/alfresco/twitter-publishing-context.xml b/config/alfresco/twitter-publishing-context.xml new file mode 100644 index 0000000000..5ba6d0307e --- /dev/null +++ b/config/alfresco/twitter-publishing-context.xml @@ -0,0 +1,28 @@ + + + + + + + + alfresco/model/twitterPublishingModel.xml + + + + + + + + + + + + + + + + + + + + diff --git a/config/alfresco/youtube-publishing-context.xml b/config/alfresco/youtube-publishing-context.xml index 783f9c28bf..e71f81e2bc 100644 --- a/config/alfresco/youtube-publishing-context.xml +++ b/config/alfresco/youtube-publishing-context.xml @@ -26,7 +26,10 @@ - + + + + diff --git a/source/java/org/alfresco/repo/publishing/AbstractChannelType.java b/source/java/org/alfresco/repo/publishing/AbstractChannelType.java index fa25d33335..7cc8c97f1a 100644 --- a/source/java/org/alfresco/repo/publishing/AbstractChannelType.java +++ b/source/java/org/alfresco/repo/publishing/AbstractChannelType.java @@ -22,63 +22,71 @@ package org.alfresco.repo.publishing; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.Map; import org.alfresco.repo.transfer.CompositeNodeFilter; import org.alfresco.repo.transfer.CompositeNodeFinder; import org.alfresco.repo.transfer.PrimaryParentNodeFinder; import org.alfresco.service.ServiceRegistry; +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.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.transfer.NodeFilter; import org.alfresco.service.cmr.transfer.NodeFinder; +import org.alfresco.util.ParameterCheck; import org.springframework.beans.factory.InitializingBean; /** * @author Nick Smith * @since 4.0 - * + * */ public abstract class AbstractChannelType implements ChannelType, InitializingBean { private ServiceRegistry serviceRegistry; protected NodeFinder nodeFinder; protected NodeFilter nodeFilter; - + public void setChannelService(ChannelService channelService) { channelService.register(this); } /** - * @param serviceRegistry the serviceRegistry to set + * @param serviceRegistry + * the serviceRegistry to set */ public void setServiceRegistry(ServiceRegistry serviceRegistry) { this.serviceRegistry = serviceRegistry; } - + /** - * {@inheritDoc} - */ + * {@inheritDoc} + */ @Override public void afterPropertiesSet() throws Exception { - + Collection finders = getAllNodeFInders(); CompositeNodeFinder finder = new CompositeNodeFinder(finders); finder.setServiceRegistry(serviceRegistry); finder.init(); this.nodeFinder = finder; - + Collection filters = getAllNodeFIlters(); CompositeNodeFilter filter = new CompositeNodeFilter(filters); filter.setServiceRegistry(serviceRegistry); finder.init(); this.nodeFilter = filter; } - + /** - * @return a collection of {@link NodeFilter}s to be included in the {@link CompositeNodeFilter} returned by the getNodeFilter() method. + * @return a collection of {@link NodeFilter}s to be included in the + * {@link CompositeNodeFilter} returned by the getNodeFilter() + * method. */ protected Collection getAllNodeFIlters() { @@ -86,38 +94,78 @@ public abstract class AbstractChannelType implements ChannelType, InitializingBe } /** - * @return a collection of {@link NodeFinder}s to be included in the {@link CompositeNodeFinder} returned by the getNodeFinder() method. + * @return a collection of {@link NodeFinder}s to be included in the + * {@link CompositeNodeFinder} returned by the getNodeFinder() + * method. */ protected Collection getAllNodeFInders() { - //TODO Add dependency node finder. + // TODO Add dependency node finder. NodeFinder parentFinder = new PrimaryParentNodeFinder(); return Arrays.asList(parentFinder); } /** - * {@inheritDoc} - */ + * {@inheritDoc} + */ @Override public NodeFilter getNodeFilter() { return nodeFilter; } + /** - * {@inheritDoc} - */ + * {@inheritDoc} + */ @Override public NodeFinder getNodeFinder() { return nodeFinder; } - + /** - * {@inheritDoc} - */ + * {@inheritDoc} + */ @Override public int getMaximumStatusLength() { return -1; } + + @Override + public String getAuthorisationUrl(Channel channel, String callbackUrl) + { + //Returning a null here to indicate that we should use our own credential-gathering mechanism. + return null; + } + + @Override + public boolean acceptAuthorisationCallback(Channel channel, Map callbackHeaders, + Map callbackParams) + { + boolean result = false; + + ParameterCheck.mandatory("channel", channel); + ParameterCheck.mandatory("callbackHeaders", callbackHeaders); + ParameterCheck.mandatory("callbackParams", callbackParams); + if (!getId().equals(channel.getChannelType().getId())) + { + throw new IllegalArgumentException("Supplied channel is of the incorrect type. Expected " + getId() + + "; Received " + channel.getChannelType().getId()); + } + + NodeRef channelNodeRef = channel.getNodeRef(); + NodeService nodeService = serviceRegistry.getNodeService(); + + String[] username = callbackParams.get("username"); + String[] password = callbackParams.get("password"); + if (username != null && password != null) + { + nodeService.setProperty(channelNodeRef, PublishingModel.PROP_CHANNEL_USERNAME, username[0]); + nodeService.setProperty(channelNodeRef, PublishingModel.PROP_CHANNEL_PASSWORD, password[0]); + //TODO: BJR: 20110707: Should test the connection here + result = true; + } + return result; + } } diff --git a/source/java/org/alfresco/repo/publishing/ChannelImpl.java b/source/java/org/alfresco/repo/publishing/ChannelImpl.java index 5db79ed209..9200660195 100644 --- a/source/java/org/alfresco/repo/publishing/ChannelImpl.java +++ b/source/java/org/alfresco/repo/publishing/ChannelImpl.java @@ -111,7 +111,7 @@ public class ChannelImpl implements Channel @Override public void updateStatus(String status) { - channelType.updateStatus(status, getProperties()); + channelType.updateStatus(this, status, getProperties()); } } diff --git a/source/java/org/alfresco/repo/publishing/ChannelServiceImpl.java b/source/java/org/alfresco/repo/publishing/ChannelServiceImpl.java index a7a3ed7e58..d372955ac8 100644 --- a/source/java/org/alfresco/repo/publishing/ChannelServiceImpl.java +++ b/source/java/org/alfresco/repo/publishing/ChannelServiceImpl.java @@ -161,6 +161,7 @@ public class ChannelServiceImpl implements ChannelService } actualProps.put(ContentModel.PROP_NAME, name); actualProps.put(PROP_CHANNEL_TYPE_ID, channelType.getId()); + actualProps.put(PublishingModel.PROP_AUTHORISATION_COMPLETE, Boolean.FALSE); NodeRef channelNode = channelHelper.createChannelNode(channelContainer, channelType, name, actualProps); Channel channel = channelHelper.buildChannelObject(channelNode, this); diff --git a/source/java/org/alfresco/repo/publishing/MockChannelType.java b/source/java/org/alfresco/repo/publishing/MockChannelType.java index 34c546dd92..0985bb5e95 100644 --- a/source/java/org/alfresco/repo/publishing/MockChannelType.java +++ b/source/java/org/alfresco/repo/publishing/MockChannelType.java @@ -24,6 +24,7 @@ import java.util.Map; import java.util.Set; import org.alfresco.model.ContentModel; +import org.alfresco.service.cmr.publishing.channels.Channel; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.QName; @@ -91,7 +92,7 @@ public class MockChannelType extends AbstractChannelType * {@inheritDoc} */ @Override - public void updateStatus(String status, Map properties) + public void updateStatus(Channel channel, String status, Map properties) { //NOOP } @@ -149,4 +150,10 @@ public class MockChannelType extends AbstractChannelType return null; } + @Override + public String getAuthorisationUrl(Channel channel, String callbackUrl) + { + return ""; + } + } diff --git a/source/java/org/alfresco/repo/publishing/PublishEventActionTest.java b/source/java/org/alfresco/repo/publishing/PublishEventActionTest.java index b3447da882..a163055869 100644 --- a/source/java/org/alfresco/repo/publishing/PublishEventActionTest.java +++ b/source/java/org/alfresco/repo/publishing/PublishEventActionTest.java @@ -32,6 +32,7 @@ import static org.alfresco.model.ContentModel.PROP_LONGITUDE; import static org.alfresco.model.ContentModel.PROP_NAME; import static org.alfresco.model.ContentModel.TYPE_CONTENT; import static org.alfresco.repo.publishing.PublishingModel.ASSOC_LAST_PUBLISHING_EVENT; +import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyMap; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.never; @@ -55,6 +56,7 @@ 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.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.ChildAssociationRef; @@ -338,7 +340,7 @@ public class PublishEventActionTest extends AbstractPublishingIntegrationTest publishNode(source, status); String expMessage = message + url; - verify(channelType, times(1)).updateStatus(eq(expMessage), anyMap()); + verify(channelType, times(1)).updateStatus(any(Channel.class), eq(expMessage), anyMap()); } private NodeRef publishNode(NodeRef source) diff --git a/source/java/org/alfresco/repo/publishing/PublishingModel.java b/source/java/org/alfresco/repo/publishing/PublishingModel.java index 1df86daaee..89917b8667 100644 --- a/source/java/org/alfresco/repo/publishing/PublishingModel.java +++ b/source/java/org/alfresco/repo/publishing/PublishingModel.java @@ -44,6 +44,7 @@ public interface PublishingModel public static final QName ASPECT_CHANNEL_INFO= QName.createQName(NAMESPACE, "channelInfo"); public static final QName ASPECT_PUBLISHED = QName.createQName(NAMESPACE, "published"); + public static final QName ASPECT_OAUTH1_DELIVERY_CHANNEL = QName.createQName(NAMESPACE, "OAuth1DeliveryChannelAspect"); public static final QName PROP_CHANNEL = QName.createQName(NAMESPACE, "channel"); public static final QName PROP_CHANNEL_TYPE = QName.createQName(NAMESPACE, "channelType"); @@ -60,6 +61,13 @@ public interface PublishingModel public static final QName PROP_STATUS_UPDATE_CHANNEL_NAMES = QName.createQName(NAMESPACE, "statusUpdateChannelNames"); public static final QName PROP_STATUS_UPDATE_NODE_REF = QName.createQName(NAMESPACE, "statusUpdateNodeRef"); public static final QName PROP_STATUS_UPDATE_MESSAGE = QName.createQName(NAMESPACE, "statusUpdateMessage"); + public static final QName PROP_AUTHORISATION_COMPLETE = QName.createQName(NAMESPACE, "authorisationComplete"); + public static final QName PROP_OAUTH1_TOKEN_VALUE = QName.createQName(NAMESPACE, "oauth1TokenValue"); + public static final QName PROP_OAUTH1_TOKEN_SECRET = QName.createQName(NAMESPACE, "oauth1TokenSecret"); + public static final QName PROP_CHANNEL_USERNAME = QName.createQName(NAMESPACE, "channelUsername"); + public static final QName PROP_CHANNEL_PASSWORD = QName.createQName(NAMESPACE, "channelPassword"); + + // Publishing Connection Properties public static final QName PROP_ACCOUNT_ID= QName.createQName(NAMESPACE, "accountId"); public static final QName PROP_PROVIDER_ID= QName.createQName(NAMESPACE, "providerId"); diff --git a/source/java/org/alfresco/repo/publishing/facebook/FacebookChannelType.java b/source/java/org/alfresco/repo/publishing/facebook/FacebookChannelType.java new file mode 100644 index 0000000000..74d5478069 --- /dev/null +++ b/source/java/org/alfresco/repo/publishing/facebook/FacebookChannelType.java @@ -0,0 +1,168 @@ +/* + * 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.facebook; + +import java.io.Serializable; +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +import org.alfresco.repo.publishing.AbstractChannelType; +import org.alfresco.repo.publishing.PublishingModel; +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.ParameterCheck; +import org.springframework.social.connect.Connection; +import org.springframework.social.facebook.api.Facebook; +import org.springframework.social.oauth1.AuthorizedRequestToken; +import org.springframework.social.oauth1.OAuth1Operations; +import org.springframework.social.oauth1.OAuth1Parameters; +import org.springframework.social.oauth1.OAuthToken; +import org.springframework.social.oauth2.GrantType; +import org.springframework.social.oauth2.OAuth2Operations; +import org.springframework.social.oauth2.OAuth2Parameters; +import org.springframework.social.twitter.api.Twitter; + +public class FacebookChannelType extends AbstractChannelType +{ + public final static String ID = "facebook"; + private NodeService nodeService; + private FacebookPublishingHelper publishingHelper; + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + public void setPublishingHelper(FacebookPublishingHelper facebookPublishingHelper) + { + this.publishingHelper = facebookPublishingHelper; + } + + @Override + public boolean canPublish() + { + return false; + } + + @Override + public boolean canPublishStatusUpdates() + { + return true; + } + + @Override + public boolean canUnpublish() + { + return false; + } + + @Override + public QName getChannelNodeType() + { + return FacebookPublishingModel.TYPE_DELIVERY_CHANNEL; + } + + @Override + public String getId() + { + return ID; + } + + @Override + public Set getSupportedContentTypes() + { + return Collections.emptySet(); + } + + @Override + public Set getSupportedMimetypes() + { + return Collections.emptySet(); + } + + @Override + public void publish(NodeRef nodeToPublish, Map properties) + { + } + + @Override + public void unpublish(NodeRef nodeToUnpublish, Map properties) + { + } + + @Override + public void updateStatus(Channel channel, String status, Map properties) + { + Connection connection = publishingHelper.getFacebookConnectionForChannel(channel.getNodeRef()); + connection.updateStatus(status); + } + + @Override + public String getNodeUrl(NodeRef node) + { + String url = null; + if (node != null && nodeService.exists(node) && nodeService.hasAspect(node, FacebookPublishingModel.ASPECT_ASSET)) + { + url = (String)nodeService.getProperty(node, FacebookPublishingModel.PROP_ASSET_URL); + } + return url; + } + + @Override + public String getAuthorisationUrl(Channel channel, String callbackUrl) + { + ParameterCheck.mandatory("channel", channel); + ParameterCheck.mandatory("callbackUrl", callbackUrl); + if (!ID.equals(channel.getChannelType().getId())) + { + throw new IllegalArgumentException("Invalid channel type: " + channel.getChannelType().getId()); + } + + OAuth2Operations oauthOperations = publishingHelper.getConnectionFactory().getOAuthOperations(); + return oauthOperations.buildAuthorizeUrl(GrantType.AUTHORIZATION_CODE, new OAuth2Parameters(callbackUrl)); + } + + @Override + public boolean acceptAuthorisationCallback(Channel channel, Map callbackHeaders, + Map callbackParams) + { + boolean authorised = false; + //FIXME: BJR: 20110708: Write this. +// String[] verifier = callbackParams.get("oauth_verifier"); +// if (verifier != null) +// { +// OAuth2Operations oauthOperations = publishingHelper.getConnectionFactory().getOAuthOperations(); +// NodeRef channelNodeRef = channel.getNodeRef(); +// +// Map props = nodeService.getProperties(channelNodeRef); +// String tokenValue = (String) props.get(PublishingModel.PROP_OAUTH1_TOKEN_VALUE); +// String tokenSecret = (String) props.get(PublishingModel.PROP_OAUTH1_TOKEN_SECRET); +// OAuthToken token = new OAuthToken(tokenValue, tokenSecret); +// OAuthToken accessToken = oauthOperations.exchangeForAccessToken(new AuthorizedRequestToken(token, verifier[0]), null); +// nodeService.setProperty(channelNodeRef, PublishingModel.PROP_OAUTH1_TOKEN_VALUE, accessToken.getValue()); +// nodeService.setProperty(channelNodeRef, PublishingModel.PROP_OAUTH1_TOKEN_SECRET, accessToken.getSecret()); +// +// authorised = true; +// } + return authorised; + } +} diff --git a/source/java/org/alfresco/repo/publishing/facebook/FacebookPublishingHelper.java b/source/java/org/alfresco/repo/publishing/facebook/FacebookPublishingHelper.java new file mode 100644 index 0000000000..7356c4d0c8 --- /dev/null +++ b/source/java/org/alfresco/repo/publishing/facebook/FacebookPublishingHelper.java @@ -0,0 +1,71 @@ +/* + * 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.facebook; + +import org.alfresco.repo.publishing.PublishingModel; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.springframework.social.connect.Connection; +import org.springframework.social.facebook.api.Facebook; +import org.springframework.social.facebook.connect.FacebookConnectionFactory; +import org.springframework.social.oauth1.OAuthToken; +import org.springframework.social.oauth2.AccessGrant; +import org.springframework.social.twitter.api.Twitter; +import org.springframework.social.twitter.connect.TwitterConnectionFactory; + +public class FacebookPublishingHelper +{ + private NodeService nodeService; + private FacebookConnectionFactory connectionFactory; + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + public void setConnectionFactory(FacebookConnectionFactory connectionFactory) + { + this.connectionFactory = connectionFactory; + } + + public FacebookConnectionFactory getConnectionFactory() + { + return connectionFactory; + } + + public Connection getFacebookConnectionForChannel(NodeRef channelNode) + { + Connection connection = null; + if (nodeService.exists(channelNode) + && nodeService.hasAspect(channelNode, FacebookPublishingModel.ASPECT_DELIVERY_CHANNEL)) + { + String tokenValue = (String) nodeService.getProperty(channelNode, PublishingModel.PROP_OAUTH1_TOKEN_VALUE); + String tokenSecret = (String) nodeService.getProperty(channelNode, PublishingModel.PROP_OAUTH1_TOKEN_SECRET); + Boolean danceComplete = (Boolean) nodeService.getProperty(channelNode, PublishingModel.PROP_AUTHORISATION_COMPLETE); + + if (danceComplete) + { + AccessGrant token = new AccessGrant(" "); + connection = connectionFactory.createConnection(token); + } + } + return connection; + } + +} diff --git a/source/java/org/alfresco/repo/publishing/facebook/FacebookPublishingModel.java b/source/java/org/alfresco/repo/publishing/facebook/FacebookPublishingModel.java new file mode 100644 index 0000000000..9583213930 --- /dev/null +++ b/source/java/org/alfresco/repo/publishing/facebook/FacebookPublishingModel.java @@ -0,0 +1,40 @@ +/* + * 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.facebook; + +import org.alfresco.service.namespace.QName; + +/** + * @author Brian + * + */ +public interface FacebookPublishingModel +{ + public static final String NAMESPACE = "http://www.alfresco.org/model/publishing/facebook/1.0"; + public static final String PREFIX = "facebook"; + + public static final QName TYPE_DELIVERY_CHANNEL = QName.createQName(NAMESPACE, "DeliveryChannel"); + + public static final QName ASPECT_DELIVERY_CHANNEL = QName.createQName(NAMESPACE, "DeliveryChannelAspect"); + + 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/slideshare/SlideShareChannelType.java b/source/java/org/alfresco/repo/publishing/slideshare/SlideShareChannelType.java new file mode 100644 index 0000000000..4f6f32dbea --- /dev/null +++ b/source/java/org/alfresco/repo/publishing/slideshare/SlideShareChannelType.java @@ -0,0 +1,151 @@ +/* + * 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.slideshare; + +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.service.cmr.action.Action; +import org.alfresco.service.cmr.action.ActionService; +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; + +public class SlideShareChannelType extends AbstractChannelType +{ + public final static String ID = "slideshare"; + private final static Set DEFAULT_MIME_TYPES = new TreeSet(); + + private NodeService nodeService; + private ActionService actionService; + private Set permittedMimeTypes = Collections.unmodifiableSet(DEFAULT_MIME_TYPES); + + static + { + DEFAULT_MIME_TYPES.add(MimetypeMap.MIMETYPE_PPT); + DEFAULT_MIME_TYPES.add(MimetypeMap.MIMETYPE_PDF); + DEFAULT_MIME_TYPES.add(MimetypeMap.MIMETYPE_OPENDOCUMENT_PRESENTATION); + DEFAULT_MIME_TYPES.add(MimetypeMap.MIMETYPE_OPENXML_PRESENTATION); + DEFAULT_MIME_TYPES.add(MimetypeMap.MIMETYPE_IWORK_KEYNOTE); + DEFAULT_MIME_TYPES.add(MimetypeMap.MIMETYPE_IWORK_PAGES); + DEFAULT_MIME_TYPES.add(MimetypeMap.MIMETYPE_TEXT_PLAIN); + DEFAULT_MIME_TYPES.add(MimetypeMap.MIMETYPE_OPENDOCUMENT_TEXT); + DEFAULT_MIME_TYPES.add(MimetypeMap.MIMETYPE_TEXT_CSV); + DEFAULT_MIME_TYPES.add(MimetypeMap.MIMETYPE_EXCEL); + DEFAULT_MIME_TYPES.add(MimetypeMap.MIMETYPE_OPENXML_WORDPROCESSING); + DEFAULT_MIME_TYPES.add(MimetypeMap.MIMETYPE_OPENDOCUMENT_SPREADSHEET); + } + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + public void setActionService(ActionService actionService) + { + this.actionService = actionService; + } + + public void setPermittedMimeTypes(Set permittedMimeTypes) + { + if (permittedMimeTypes == null) + { + permittedMimeTypes = Collections.emptySet(); + } + this.permittedMimeTypes = Collections.unmodifiableSet(permittedMimeTypes); + } + + @Override + public boolean canPublish() + { + return true; + } + + @Override + public boolean canPublishStatusUpdates() + { + return false; + } + + @Override + public boolean canUnpublish() + { + return false; + } + + @Override + public QName getChannelNodeType() + { + return SlideSharePublishingModel.TYPE_DELIVERY_CHANNEL; + } + + @Override + public String getId() + { + return ID; + } + + @Override + public Set getSupportedContentTypes() + { + return Collections.emptySet(); + } + + @Override + public Set getSupportedMimetypes() + { + return permittedMimeTypes; + } + + @Override + public void publish(NodeRef nodeToPublish, Map properties) + { + Action publishAction = actionService.createAction(SlideSharePublishAction.NAME); + actionService.executeAction(publishAction, nodeToPublish); + } + + @Override + public void unpublish(NodeRef nodeToUnpublish, Map properties) + { + throw new UnsupportedOperationException(); + } + + @Override + public void updateStatus(Channel channel, String status, Map properties) + { + throw new UnsupportedOperationException(); + } + + @Override + public String getNodeUrl(NodeRef node) + { + String url = null; + if (node != null && nodeService.exists(node) && nodeService.hasAspect(node, SlideSharePublishingModel.ASPECT_ASSET)) + { + url = (String)nodeService.getProperty(node, SlideSharePublishingModel.PROP_PLAYER_URL); + } + return url; + } +} diff --git a/source/java/org/alfresco/repo/publishing/slideshare/SlideSharePublishAction.java b/source/java/org/alfresco/repo/publishing/slideshare/SlideSharePublishAction.java new file mode 100644 index 0000000000..e93fe2a6b9 --- /dev/null +++ b/source/java/org/alfresco/repo/publishing/slideshare/SlideSharePublishAction.java @@ -0,0 +1,138 @@ +/* + * 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.slideshare; + +import java.io.File; +import java.util.List; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.repo.action.executer.ActionExecuterAbstractBase; +import org.alfresco.repo.content.filestore.FileContentReader; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.action.ParameterDefinition; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.tagging.TaggingService; +import org.alfresco.util.Pair; +import org.alfresco.util.TempFileProvider; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import com.benfante.jslideshare.SlideShareAPI; + +public class SlideSharePublishAction extends ActionExecuterAbstractBase +{ + private final static Log log = LogFactory.getLog(SlideSharePublishAction.class); + + public static final String NAME = "publish_slideshare"; + + private NodeService nodeService; + private ContentService contentService; + private TaggingService taggingService; + private SlideSharePublishingHelper slideShareHelper; + + public void setSlideShareHelper(SlideSharePublishingHelper slideShareHelper) + { + this.slideShareHelper = slideShareHelper; + } + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + public void setContentService(ContentService contentService) + { + this.contentService = contentService; + } + + public void setTaggingService(TaggingService taggingService) + { + this.taggingService = taggingService; + } + + @Override + protected void executeImpl(Action action, NodeRef nodeRef) + { + SlideShareAPI api = slideShareHelper.getSlideShareApi(); + Pair usernamePassword = slideShareHelper.getSlideShareCredentialsForNode(nodeRef); + if (api == null || usernamePassword == null) + { + throw new AlfrescoRuntimeException("publish.failed.unable_to_connect_to_service_provider"); + } + + ContentReader reader = contentService.getReader(nodeRef, ContentModel.PROP_CONTENT); + if (reader.exists()) + { + File contentFile; + boolean deleteContentFileOnCompletion = false; + if (FileContentReader.class.isAssignableFrom(reader.getClass())) + { + //Grab the content straight from the content store if we can... + contentFile = ((FileContentReader)reader).getFile(); + } + else + { + //...otherwise copy it to a temp file and use the copy... + File tempDir = TempFileProvider.getLongLifeTempDir("slideshare"); + contentFile = TempFileProvider.createTempFile("slideshare", "", tempDir); + reader.getContent(contentFile); + deleteContentFileOnCompletion = true; + } + + String name = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); + String title = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_TITLE); + if (title == null || title.length() == 0) + { + title = name; + } + String description = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_DESCRIPTION); + if (description == null || description.length() == 0) + { + description = title; + } + + List tagList = taggingService.getTags(nodeRef); + StringBuilder tags = new StringBuilder(); + for (String tag : tagList) + { + tags.append(tag); + tags.append(' '); + } + + String assetId = api.uploadSlideshow(usernamePassword.getFirst(), usernamePassword.getSecond(), title, + contentFile, description, tags.toString(), false, false, false, false, false); + nodeService.setProperty(nodeRef, SlideSharePublishingModel.PROP_ASSET_ID, assetId); + + if (deleteContentFileOnCompletion) + { + contentFile.delete(); + } + } + } + + + @Override + protected void addParameterDefinitions(List paramList) + { + } +} diff --git a/source/java/org/alfresco/repo/publishing/slideshare/SlideSharePublishingHelper.java b/source/java/org/alfresco/repo/publishing/slideshare/SlideSharePublishingHelper.java new file mode 100644 index 0000000000..1b33f5ea9e --- /dev/null +++ b/source/java/org/alfresco/repo/publishing/slideshare/SlideSharePublishingHelper.java @@ -0,0 +1,64 @@ +/* + * 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.slideshare; + +import org.alfresco.repo.publishing.PublishingModel; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.util.Pair; + +import com.benfante.jslideshare.SlideShareAPI; +import com.benfante.jslideshare.SlideShareAPIFactory; + +public class SlideSharePublishingHelper +{ + private NodeService nodeService; + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + + public SlideShareAPI getSlideShareApi() + { + return SlideShareAPIFactory.getSlideShareAPI("hhjh", "oijkl"); + } + + + public Pair getSlideShareCredentialsForNode(NodeRef publishNode) + { + Pair result = null; + if (nodeService.exists(publishNode)) + { + NodeRef parent = nodeService.getPrimaryParent(publishNode).getParentRef(); + if (nodeService.hasAspect(parent, SlideSharePublishingModel.ASPECT_DELIVERY_CHANNEL)) + { + String username = (String) nodeService.getProperty(parent, PublishingModel.PROP_CHANNEL_USERNAME); + String password = (String) nodeService.getProperty(parent, PublishingModel.PROP_CHANNEL_PASSWORD); + if (username != null && password != null) + { + result = new Pair(username, password); + } + } + } + return result; + } + +} diff --git a/source/java/org/alfresco/repo/publishing/slideshare/SlideSharePublishingModel.java b/source/java/org/alfresco/repo/publishing/slideshare/SlideSharePublishingModel.java new file mode 100644 index 0000000000..90b76625da --- /dev/null +++ b/source/java/org/alfresco/repo/publishing/slideshare/SlideSharePublishingModel.java @@ -0,0 +1,40 @@ +/* + * 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.slideshare; + +import org.alfresco.service.namespace.QName; + +/** + * @author Brian + * + */ +public interface SlideSharePublishingModel +{ + public static final String NAMESPACE = "http://www.alfresco.org/model/publishing/youtube/1.0"; + public static final String PREFIX = "youtube"; + + public static final QName TYPE_DELIVERY_CHANNEL = QName.createQName(NAMESPACE, "DeliveryChannel"); + + public static final QName ASPECT_DELIVERY_CHANNEL = QName.createQName(NAMESPACE, "DeliveryChannelAspect"); + + 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_PLAYER_URL = QName.createQName(NAMESPACE, "assetUrl"); +} diff --git a/source/java/org/alfresco/repo/publishing/springsocial/ConnectionSerializer.java b/source/java/org/alfresco/repo/publishing/springsocial/ConnectionSerializer.java deleted file mode 100644 index 26c00f19b2..0000000000 --- a/source/java/org/alfresco/repo/publishing/springsocial/ConnectionSerializer.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2005-2010 Alfresco Software Limited. - * - * This file is part of Alfresco - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - */ - -package org.alfresco.repo.publishing.springsocial; - -import org.springframework.social.connect.ConnectionData; - -/** - * @author Nick Smith - * @since 4.0 - * - */ -public class ConnectionSerializer -{ - - public ConnectionData deSerialize() - { - Long expireTime = null; - String refreshToken = null; - String secret = null; - String accessToken = null; - String providerId = null; - String imageUrl = null; - String profileUrl = null; - String providerUserId = null; - String displayName = null; - return new ConnectionData(providerId, providerUserId, displayName, profileUrl, imageUrl, accessToken, secret, refreshToken, expireTime); - } -} diff --git a/source/java/org/alfresco/repo/publishing/springsocial/OAuth1ChannelType.java b/source/java/org/alfresco/repo/publishing/springsocial/OAuth1ChannelType.java deleted file mode 100644 index 959fe519e5..0000000000 --- a/source/java/org/alfresco/repo/publishing/springsocial/OAuth1ChannelType.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2005-2010 Alfresco Software Limited. - * - * This file is part of Alfresco - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - */ - -package org.alfresco.repo.publishing.springsocial; - -import java.io.Serializable; -import java.util.Map; -import java.util.Set; - -import org.alfresco.repo.publishing.AbstractChannelType; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.namespace.QName; -import org.springframework.social.oauth1.OAuth1ServiceProvider; - -/** - * @author Nick Smith - * @since 4.0 - * - */ -public abstract class OAuth1ChannelType extends AbstractChannelType -{ - OAuth1ServiceProvider serviceProvider; - - /** - * {@inheritDoc} - */ - @Override - public void publish(NodeRef nodeToPublish, Map properties) - { - // TODO Auto-generated method stub - - } - - /** - * {@inheritDoc} - */ - @Override - public void unpublish(NodeRef nodeToUnpublish, Map properties) - { - // TODO Auto-generated method stub - - } - - /** - * {@inheritDoc} - */ - @Override - public void updateStatus(String status, Map properties) - { - - // TODO Auto-generated method stub - - } - -} diff --git a/source/java/org/alfresco/repo/publishing/test/TestChannelType1.java b/source/java/org/alfresco/repo/publishing/test/TestChannelType1.java index a3c5d8ac59..1a34c31ff9 100644 --- a/source/java/org/alfresco/repo/publishing/test/TestChannelType1.java +++ b/source/java/org/alfresco/repo/publishing/test/TestChannelType1.java @@ -25,6 +25,7 @@ import java.util.Set; import org.alfresco.repo.publishing.AbstractChannelType; import org.alfresco.repo.publishing.PublishingModel; +import org.alfresco.service.cmr.publishing.channels.Channel; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.transfer.NodeFilter; import org.alfresco.service.cmr.transfer.NodeFinder; @@ -112,9 +113,16 @@ public class TestChannelType1 extends AbstractChannelType } @Override - public void updateStatus(String status, Map properties) + public void updateStatus(Channel channel, String status, Map properties) { //Deliberately blank } + @Override + public String getAuthorisationUrl(Channel channel, String callbackUrl) + { + // TODO Auto-generated method stub + return null; + } + } diff --git a/source/java/org/alfresco/repo/publishing/test/TestChannelType2.java b/source/java/org/alfresco/repo/publishing/test/TestChannelType2.java index c71e07411d..2014fd78a1 100644 --- a/source/java/org/alfresco/repo/publishing/test/TestChannelType2.java +++ b/source/java/org/alfresco/repo/publishing/test/TestChannelType2.java @@ -25,6 +25,7 @@ import java.util.Set; import org.alfresco.repo.publishing.AbstractChannelType; import org.alfresco.repo.publishing.PublishingModel; +import org.alfresco.service.cmr.publishing.channels.Channel; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.transfer.NodeFilter; import org.alfresco.service.cmr.transfer.NodeFinder; @@ -112,9 +113,16 @@ public class TestChannelType2 extends AbstractChannelType } @Override - public void updateStatus(String status, Map properties) + public void updateStatus(Channel channel, String status, Map properties) { //Deliberately blank } + @Override + public String getAuthorisationUrl(Channel channel, String callbackUrl) + { + // TODO Auto-generated method stub + return null; + } + } diff --git a/source/java/org/alfresco/repo/publishing/test/TestChannelType3.java b/source/java/org/alfresco/repo/publishing/test/TestChannelType3.java index 1c692aa4eb..57fe2246e8 100644 --- a/source/java/org/alfresco/repo/publishing/test/TestChannelType3.java +++ b/source/java/org/alfresco/repo/publishing/test/TestChannelType3.java @@ -25,6 +25,7 @@ import java.util.Set; import org.alfresco.repo.publishing.AbstractChannelType; import org.alfresco.repo.publishing.PublishingModel; +import org.alfresco.service.cmr.publishing.channels.Channel; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.transfer.NodeFilter; import org.alfresco.service.cmr.transfer.NodeFinder; @@ -112,9 +113,16 @@ public class TestChannelType3 extends AbstractChannelType } @Override - public void updateStatus(String status, Map properties) + public void updateStatus(Channel channel, String status, Map properties) { //Deliberately blank } + @Override + public String getAuthorisationUrl(Channel channel, String callbackUrl) + { + // TODO Auto-generated method stub + return null; + } + } diff --git a/source/java/org/alfresco/repo/publishing/twitter/TwitterChannelType.java b/source/java/org/alfresco/repo/publishing/twitter/TwitterChannelType.java new file mode 100644 index 0000000000..6060f1414c --- /dev/null +++ b/source/java/org/alfresco/repo/publishing/twitter/TwitterChannelType.java @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.publishing.twitter; + +import java.io.Serializable; +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +import org.alfresco.repo.publishing.AbstractChannelType; +import org.alfresco.repo.publishing.PublishingModel; +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.ParameterCheck; +import org.springframework.social.connect.Connection; +import org.springframework.social.oauth1.AuthorizedRequestToken; +import org.springframework.social.oauth1.OAuth1Operations; +import org.springframework.social.oauth1.OAuth1Parameters; +import org.springframework.social.oauth1.OAuthToken; +import org.springframework.social.twitter.api.Twitter; + +public class TwitterChannelType extends AbstractChannelType +{ + public final static String ID = "twitter"; + private NodeService nodeService; + private TwitterPublishingHelper publishingHelper; + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + public void setPublishingHelper(TwitterPublishingHelper twitterPublishingHelper) + { + this.publishingHelper = twitterPublishingHelper; + } + + @Override + public boolean canPublish() + { + return false; + } + + @Override + public boolean canPublishStatusUpdates() + { + return true; + } + + @Override + public boolean canUnpublish() + { + return false; + } + + @Override + public QName getChannelNodeType() + { + return TwitterPublishingModel.TYPE_DELIVERY_CHANNEL; + } + + @Override + public String getId() + { + return ID; + } + + @Override + public Set getSupportedContentTypes() + { + return Collections.emptySet(); + } + + @Override + public Set getSupportedMimetypes() + { + return Collections.emptySet(); + } + + @Override + public void publish(NodeRef nodeToPublish, Map properties) + { + } + + @Override + public void unpublish(NodeRef nodeToUnpublish, Map properties) + { + } + + @Override + public void updateStatus(Channel channel, String status, Map properties) + { + Connection connection = publishingHelper.getTwitterConnectionForChannel(channel.getNodeRef()); + connection.getApi().timelineOperations().updateStatus(status); + } + + @Override + public String getNodeUrl(NodeRef node) + { + String url = null; + if (node != null && nodeService.exists(node) && nodeService.hasAspect(node, TwitterPublishingModel.ASPECT_ASSET)) + { + url = (String)nodeService.getProperty(node, TwitterPublishingModel.PROP_ASSET_URL); + } + return url; + } + + @Override + public String getAuthorisationUrl(Channel channel, String callbackUrl) + { + ParameterCheck.mandatory("channel", channel); + ParameterCheck.mandatory("callbackUrl", callbackUrl); + if (!ID.equals(channel.getChannelType().getId())) + { + throw new IllegalArgumentException("Invalid channel type: " + channel.getChannelType().getId()); + } + + OAuth1Operations oauthOperations = publishingHelper.getConnectionFactory().getOAuthOperations(); + OAuthToken requestToken = oauthOperations.fetchRequestToken(callbackUrl, null); + + NodeRef channelNodeRef = channel.getNodeRef(); + nodeService.setProperty(channelNodeRef, PublishingModel.PROP_OAUTH1_TOKEN_SECRET, requestToken.getSecret()); + nodeService.setProperty(channelNodeRef, PublishingModel.PROP_OAUTH1_TOKEN_VALUE, requestToken.getValue()); + + return oauthOperations.buildAuthorizeUrl(requestToken.getValue(), OAuth1Parameters.NONE); + } + + @Override + public boolean acceptAuthorisationCallback(Channel channel, Map callbackHeaders, + Map callbackParams) + { + boolean authorised = false; + String[] verifier = callbackParams.get("oauth_verifier"); + if (verifier != null) + { + OAuth1Operations oauthOperations = publishingHelper.getConnectionFactory().getOAuthOperations(); + NodeRef channelNodeRef = channel.getNodeRef(); + + Map props = nodeService.getProperties(channelNodeRef); + String tokenValue = (String) props.get(PublishingModel.PROP_OAUTH1_TOKEN_VALUE); + String tokenSecret = (String) props.get(PublishingModel.PROP_OAUTH1_TOKEN_SECRET); + OAuthToken token = new OAuthToken(tokenValue, tokenSecret); + OAuthToken accessToken = oauthOperations.exchangeForAccessToken(new AuthorizedRequestToken(token, verifier[0]), null); + nodeService.setProperty(channelNodeRef, PublishingModel.PROP_OAUTH1_TOKEN_VALUE, accessToken.getValue()); + nodeService.setProperty(channelNodeRef, PublishingModel.PROP_OAUTH1_TOKEN_SECRET, accessToken.getSecret()); + + authorised = true; + } + return authorised; + } +} diff --git a/source/java/org/alfresco/repo/publishing/twitter/TwitterPublishingHelper.java b/source/java/org/alfresco/repo/publishing/twitter/TwitterPublishingHelper.java new file mode 100644 index 0000000000..fa1fbc96b5 --- /dev/null +++ b/source/java/org/alfresco/repo/publishing/twitter/TwitterPublishingHelper.java @@ -0,0 +1,68 @@ +/* + * 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.twitter; + +import org.alfresco.repo.publishing.PublishingModel; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.springframework.social.connect.Connection; +import org.springframework.social.oauth1.OAuthToken; +import org.springframework.social.twitter.api.Twitter; +import org.springframework.social.twitter.connect.TwitterConnectionFactory; + +public class TwitterPublishingHelper +{ + private NodeService nodeService; + private TwitterConnectionFactory connectionFactory; + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + public void setConnectionFactory(TwitterConnectionFactory connectionFactory) + { + this.connectionFactory = connectionFactory; + } + + public TwitterConnectionFactory getConnectionFactory() + { + return connectionFactory; + } + + public Connection getTwitterConnectionForChannel(NodeRef channelNode) + { + Connection connection = null; + if (nodeService.exists(channelNode) + && nodeService.hasAspect(channelNode, PublishingModel.ASPECT_OAUTH1_DELIVERY_CHANNEL)) + { + String tokenValue = (String) nodeService.getProperty(channelNode, PublishingModel.PROP_OAUTH1_TOKEN_VALUE); + String tokenSecret = (String) nodeService.getProperty(channelNode, PublishingModel.PROP_OAUTH1_TOKEN_SECRET); + Boolean danceComplete = (Boolean) nodeService.getProperty(channelNode, PublishingModel.PROP_AUTHORISATION_COMPLETE); + + if (danceComplete) + { + OAuthToken token = new OAuthToken(tokenValue, tokenSecret); + connection = connectionFactory.createConnection(token); + } + } + return connection; + } + +} diff --git a/source/java/org/alfresco/repo/publishing/twitter/TwitterPublishingModel.java b/source/java/org/alfresco/repo/publishing/twitter/TwitterPublishingModel.java new file mode 100644 index 0000000000..b20b89f558 --- /dev/null +++ b/source/java/org/alfresco/repo/publishing/twitter/TwitterPublishingModel.java @@ -0,0 +1,38 @@ +/* + * 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.twitter; + +import org.alfresco.service.namespace.QName; + +/** + * @author Brian + * + */ +public interface TwitterPublishingModel +{ + public static final String NAMESPACE = "http://www.alfresco.org/model/publishing/twitter/1.0"; + public static final String PREFIX = "twitter"; + + 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/youtube/YouTubeChannelType.java b/source/java/org/alfresco/repo/publishing/youtube/YouTubeChannelType.java index 523be4f35e..615fc2a9c3 100644 --- a/source/java/org/alfresco/repo/publishing/youtube/YouTubeChannelType.java +++ b/source/java/org/alfresco/repo/publishing/youtube/YouTubeChannelType.java @@ -26,6 +26,7 @@ import java.util.Set; import org.alfresco.repo.publishing.AbstractChannelType; import org.alfresco.service.cmr.action.Action; import org.alfresco.service.cmr.action.ActionService; +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; @@ -103,7 +104,7 @@ public class YouTubeChannelType extends AbstractChannelType } @Override - public void updateStatus(String status, Map properties) + public void updateStatus(Channel channel, String status, Map properties) { throw new UnsupportedOperationException(); } diff --git a/source/java/org/alfresco/repo/publishing/youtube/YouTubePublishingHelper.java b/source/java/org/alfresco/repo/publishing/youtube/YouTubePublishingHelper.java index 83b6f8e0c7..3b4fb5a506 100644 --- a/source/java/org/alfresco/repo/publishing/youtube/YouTubePublishingHelper.java +++ b/source/java/org/alfresco/repo/publishing/youtube/YouTubePublishingHelper.java @@ -18,6 +18,7 @@ */ package org.alfresco.repo.publishing.youtube; +import org.alfresco.repo.publishing.PublishingModel; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.apache.commons.logging.Log; @@ -44,8 +45,8 @@ public class YouTubePublishingHelper NodeRef parent = nodeService.getPrimaryParent(publishNode).getParentRef(); if (nodeService.hasAspect(parent, YouTubePublishingModel.ASPECT_DELIVERY_CHANNEL)) { - String youtubeUsername = (String) nodeService.getProperty(parent, YouTubePublishingModel.PROP_USERNAME); - String youtubePassword = (String) nodeService.getProperty(parent, YouTubePublishingModel.PROP_PASSWORD); + String youtubeUsername = (String) nodeService.getProperty(parent, PublishingModel.PROP_CHANNEL_USERNAME); + String youtubePassword = (String) nodeService.getProperty(parent, PublishingModel.PROP_CHANNEL_PASSWORD); service = new YouTubeService("Alfresco Kickoff Demo", "AI39si71pRNHkfExcTpqcZewDtI4GHWuPAXyRPL2Xq-RQUBWlE1bqn77ANXEL5lZUWFDz6ZlS_XWCw8hlr2BJY1TnC-EMs4e4g"); try diff --git a/source/java/org/alfresco/repo/publishing/youtube/YouTubePublishingModel.java b/source/java/org/alfresco/repo/publishing/youtube/YouTubePublishingModel.java index 219710fa8d..8fc0f64438 100644 --- a/source/java/org/alfresco/repo/publishing/youtube/YouTubePublishingModel.java +++ b/source/java/org/alfresco/repo/publishing/youtube/YouTubePublishingModel.java @@ -33,8 +33,6 @@ public interface YouTubePublishingModel public static final QName TYPE_DELIVERY_CHANNEL = QName.createQName(NAMESPACE, "DeliveryChannel"); public static final QName ASPECT_DELIVERY_CHANNEL = QName.createQName(NAMESPACE, "DeliveryChannelAspect"); - public static final QName PROP_USERNAME = QName.createQName(NAMESPACE, "username"); - public static final QName PROP_PASSWORD = QName.createQName(NAMESPACE, "password"); public static final QName ASPECT_ASSET = QName.createQName(NAMESPACE, "AssetAspect"); public static final QName PROP_ASSET_ID = QName.createQName(NAMESPACE, "assetId"); diff --git a/source/java/org/alfresco/repo/publishing/youtube/YouTubeTest.java b/source/java/org/alfresco/repo/publishing/youtube/YouTubeTest.java index 9daa9b3c8b..1f8c9e1fd5 100644 --- a/source/java/org/alfresco/repo/publishing/youtube/YouTubeTest.java +++ b/source/java/org/alfresco/repo/publishing/youtube/YouTubeTest.java @@ -25,6 +25,7 @@ import java.util.Map; import org.alfresco.model.ContentModel; import org.alfresco.repo.publishing.EnvironmentImpl; +import org.alfresco.repo.publishing.PublishingModel; import org.alfresco.repo.publishing.PublishingQueueImpl; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.transaction.RetryingTransactionHelper; @@ -104,8 +105,8 @@ public class YouTubeTest extends BaseSpringTest public NodeRef execute() throws Throwable { Map props = new HashMap(); - props.put(YouTubePublishingModel.PROP_USERNAME, "YOUR_USER_NAME"); - props.put(YouTubePublishingModel.PROP_PASSWORD, "YOUR_PASSWORD"); + props.put(PublishingModel.PROP_CHANNEL_USERNAME, "demochilledpenguin"); + props.put(PublishingModel.PROP_CHANNEL_PASSWORD, "D3moChilledPenguin"); Channel channel = channelService.createChannel(siteId, YouTubeChannelType.ID, "YouTubeChannel", props); NodeRef channelNode = channel.getNodeRef(); diff --git a/source/java/org/alfresco/service/cmr/publishing/channels/ChannelType.java b/source/java/org/alfresco/service/cmr/publishing/channels/ChannelType.java index e5c5fd77aa..ac0ec2dbfd 100644 --- a/source/java/org/alfresco/service/cmr/publishing/channels/ChannelType.java +++ b/source/java/org/alfresco/service/cmr/publishing/channels/ChannelType.java @@ -40,7 +40,7 @@ public interface ChannelType NodeFilter getNodeFilter(); void publish(NodeRef nodeToPublish, Map properties); void unpublish(NodeRef nodeToUnpublish, Map properties); - void updateStatus(String status, Map properties); + void updateStatus(Channel channel, String status, Map properties); boolean canPublish(); boolean canUnpublish(); @@ -50,4 +50,8 @@ public interface ChannelType Set getSupportedContentTypes(); String getNodeUrl(NodeRef node); int getMaximumStatusLength(); + + String getAuthorisationUrl(Channel channel, String callbackUrl); + public boolean acceptAuthorisationCallback(Channel channel, Map callbackHeaders, + Map callbackParams); }