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); }