Publishing:

- Added start of the Flickr channel. Channel creation and authorisation is complete (written as a new Spring Social module). API not currently working.
- Fixed a problem with SlideShare API: it relies on the file extension to determine the type of the file, so needed to alter the way the files are sent.

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@29085 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Brian Remmington
2011-07-15 15:47:26 +00:00
parent 176a08d093
commit aeb467a3d8
34 changed files with 1520 additions and 61 deletions

View File

@@ -27,5 +27,6 @@
<import resource="classpath*:alfresco/youtube-publishing-context.xml" /> <import resource="classpath*:alfresco/youtube-publishing-context.xml" />
<import resource="classpath*:alfresco/twitter-publishing-context.xml" /> <import resource="classpath*:alfresco/twitter-publishing-context.xml" />
<import resource="classpath*:alfresco/slideshare-publishing-context.xml" /> <import resource="classpath*:alfresco/slideshare-publishing-context.xml" />
<import resource="classpath*:alfresco/flickr-publishing-context.xml" />
<import resource="classpath*:alfresco/domain/*-context.xml" /> <import resource="classpath*:alfresco/domain/*-context.xml" />
</beans> </beans>

View File

@@ -0,0 +1,36 @@
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>
<beans>
<bean parent="dictionaryModelBootstrap" depends-on="dictionaryBootstrap">
<property name="models">
<list>
<value>alfresco/model/flickrPublishingModel.xml</value>
</list>
</property>
</bean>
<bean id="flickrDeliveryChannelType" class="org.alfresco.repo.publishing.flickr.FlickrChannelType" parent="baseChannelType" >
<property name="nodeService" ref="NodeService" />
<property name="publishingHelper" ref="flickrPublishingHelper" />
<property name="actionService" ref="ActionService" />
</bean>
<bean id="flickrPublishingHelper" class="org.alfresco.repo.publishing.flickr.FlickrPublishingHelper">
<property name="nodeService" ref="NodeService" />
<property name="connectionFactory">
<bean class="org.springframework.social.flickr.connect.FlickrConnectionFactory">
<constructor-arg value="cc61f5d37569cec8c04fce8b4db3f37d" />
<constructor-arg value="75f29c6fc18a1b0c" />
</bean>
</property>
</bean>
<bean id="publish_flickr" parent="action-executer" class="org.alfresco.repo.publishing.flickr.FlickrPublishAction">
<property name="nodeService" ref="NodeService" />
<property name="taggingService" ref="TaggingService" />
<property name="contentService" ref="ContentService" />
<property name="flickrHelper" ref="flickrPublishingHelper" />
</bean>
</beans>

View File

@@ -0,0 +1,47 @@
<model name="flickr:publishingmodel" xmlns="http://www.alfresco.org/model/dictionary/1.0">
<description>Alfresco Flickr Publishing Content Model</description>
<author>Alfresco</author>
<published>2011-07-13</published>
<version>1.0</version>
<imports>
<import uri="http://www.alfresco.org/model/dictionary/1.0" prefix="d" />
<import uri="http://www.alfresco.org/model/system/1.0" prefix="sys" />
<import uri="http://www.alfresco.org/model/content/1.0" prefix="cm" />
<import uri="http://www.alfresco.org/model/publishing/1.0" prefix="pub" />
</imports>
<namespaces>
<namespace uri="http://www.alfresco.org/model/publishing/flickr/1.0" prefix="flickr" />
</namespaces>
<types>
<type name="flickr:DeliveryChannel">
<title>Flickr Delivery Channel</title>
<description>Node type used to represent Flickr delivery channels</description>
<parent>pub:DeliveryChannel</parent>
<mandatory-aspects>
<aspect>pub:OAuth1DeliveryChannelAspect</aspect>
</mandatory-aspects>
</type>
</types>
<aspects>
<aspect name="flickr:AssetAspect">
<title>Flickr Asset</title>
<description>Applied to a node that has been published to Flickr</description>
<properties>
<property name="flickr:assetId">
<title>Flickr asset Id</title>
<type>d:text</type>
</property>
<property name="flickr:assetUrl">
<title>Flickr Asset URL</title>
<type>d:text</type>
</property>
</properties>
</aspect>
</aspects>
</model>

View File

@@ -30,6 +30,7 @@
<bean id="slideshareDeliveryChannelType" class="org.alfresco.repo.publishing.slideshare.SlideShareChannelType" parent="baseChannelType" > <bean id="slideshareDeliveryChannelType" class="org.alfresco.repo.publishing.slideshare.SlideShareChannelType" parent="baseChannelType" >
<property name="actionService" ref="ActionService" /> <property name="actionService" ref="ActionService" />
<property name="nodeService" ref="NodeService" /> <property name="nodeService" ref="NodeService" />
<property name="publishingHelper" ref="slidesharePublishingHelper" />
</bean> </bean>

View File

@@ -107,7 +107,6 @@
<bean id="urlShortener" class="org.alfresco.repo.urlshortening.BitlyUrlShortenerImpl" > <bean id="urlShortener" class="org.alfresco.repo.urlshortening.BitlyUrlShortenerImpl" >
<property name="username" value="${urlshortening.bitly.username}" /> <property name="username" value="${urlshortening.bitly.username}" />
<property name="apiKey" value="${urlshortening.bitly.api.key}" /> <property name="apiKey" value="${urlshortening.bitly.api.key}" />
<property name="urlLength" value="${urlshortening.bitly.url.length}" />
</bean> </bean>
</beans> </beans>

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

View File

@@ -175,16 +175,18 @@ public class ChannelServiceImpl implements ChannelService
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public void deleteChannel(String siteId, String channelName) public void deleteChannel(Channel channel)
{ {
Set<NodeRef> containers = getAllChannelContainers(siteId); List<NodeRef> allChannelNodes = new ArrayList<NodeRef>();
for (NodeRef channelContainer : containers) NodeRef editorialNode = channel.getNodeRef();
allChannelNodes.add(editorialNode);
for (AssociationRef assoc : nodeService.getSourceAssocs(editorialNode, PublishingModel.ASSOC_EDITORIAL_CHANNEL))
{ {
NodeRef channel = nodeService.getChildByName(channelContainer, ContentModel.ASSOC_CONTAINS, channelName); allChannelNodes.add(assoc.getSourceRef());
if (channel != null) }
{ for (NodeRef channelNode : allChannelNodes)
nodeService.deleteNode(channel); {
} nodeService.deleteNode(channelNode);
} }
} }

View File

@@ -97,7 +97,8 @@ public class ChannelServiceImplIntegratedTest extends AbstractPublishingIntegrat
{ {
testCreateChannel(); testCreateChannel();
channelService.deleteChannel(siteId, channelName); Channel channel = channelService.getChannel(siteId, channelName);
channelService.deleteChannel(channel);
List<Channel> channels = channelService.getChannels(siteId); List<Channel> channels = channelService.getChannels(siteId);
assertTrue(channels.isEmpty()); assertTrue(channels.isEmpty());

View File

@@ -24,10 +24,7 @@ import org.alfresco.service.cmr.repository.NodeService;
import org.springframework.social.connect.Connection; import org.springframework.social.connect.Connection;
import org.springframework.social.facebook.api.Facebook; import org.springframework.social.facebook.api.Facebook;
import org.springframework.social.facebook.connect.FacebookConnectionFactory; import org.springframework.social.facebook.connect.FacebookConnectionFactory;
import org.springframework.social.oauth1.OAuthToken;
import org.springframework.social.oauth2.AccessGrant; import org.springframework.social.oauth2.AccessGrant;
import org.springframework.social.twitter.api.Twitter;
import org.springframework.social.twitter.connect.TwitterConnectionFactory;
public class FacebookPublishingHelper public class FacebookPublishingHelper
{ {

View File

@@ -0,0 +1,183 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.publishing.flickr;
import java.io.Serializable;
import java.util.Collections;
import java.util.HashMap;
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.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;
import org.alfresco.util.ParameterCheck;
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.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
public class FlickrChannelType extends AbstractChannelType
{
public final static String ID = "flickr";
private NodeService nodeService;
private FlickrPublishingHelper publishingHelper;
private ActionService actionService;
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
public void setPublishingHelper(FlickrPublishingHelper flickrPublishingHelper)
{
this.publishingHelper = flickrPublishingHelper;
}
public void setActionService(ActionService actionService)
{
this.actionService = actionService;
}
@Override
public boolean canPublish()
{
return true;
}
@Override
public boolean canPublishStatusUpdates()
{
return false;
}
@Override
public boolean canUnpublish()
{
return true;
}
@Override
public QName getChannelNodeType()
{
return FlickrPublishingModel.TYPE_DELIVERY_CHANNEL;
}
@Override
public String getId()
{
return ID;
}
@Override
public Set<QName> getSupportedContentTypes()
{
return Collections.emptySet();
}
@Override
public Set<String> getSupportedMimetypes()
{
return Collections.emptySet();
}
@Override
public void publish(NodeRef nodeToPublish, Map<QName, Serializable> properties)
{
Action publishAction = actionService.createAction(FlickrPublishAction.NAME);
actionService.executeAction(publishAction, nodeToPublish);
}
@Override
public void unpublish(NodeRef nodeToUnpublish, Map<QName, Serializable> properties)
{
}
@Override
public void updateStatus(Channel channel, String status, Map<QName, Serializable> properties)
{
throw new UnsupportedOperationException();
}
@Override
public String getNodeUrl(NodeRef node)
{
String url = null;
if (node != null && nodeService.exists(node) && nodeService.hasAspect(node, FlickrPublishingModel.ASPECT_ASSET))
{
url = (String)nodeService.getProperty(node, FlickrPublishingModel.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());
MultiValueMap<String, String> params = new LinkedMultiValueMap<String, String>();
params.add("perms", "delete");
OAuth1Parameters oauthParams = new OAuth1Parameters(callbackUrl, params);
return oauthOperations.buildAuthorizeUrl(requestToken.getValue(), oauthParams);
}
@Override
public boolean acceptAuthorisationCallback(Channel channel, Map<String, String[]> callbackHeaders,
Map<String, String[]> callbackParams)
{
boolean authorised = false;
String[] verifier = callbackParams.get("oauth_verifier");
if (verifier != null)
{
OAuth1Operations oauthOperations = publishingHelper.getConnectionFactory().getOAuthOperations();
NodeRef channelNodeRef = channel.getNodeRef();
Map<QName, Serializable> currentProps = nodeService.getProperties(channelNodeRef);
String tokenValue = (String) currentProps.get(PublishingModel.PROP_OAUTH1_TOKEN_VALUE);
String tokenSecret = (String) currentProps.get(PublishingModel.PROP_OAUTH1_TOKEN_SECRET);
OAuthToken token = new OAuthToken(tokenValue, tokenSecret);
OAuthToken accessToken = oauthOperations.exchangeForAccessToken(new AuthorizedRequestToken(token, verifier[0]), null);
Map<QName, Serializable> newProps = new HashMap<QName, Serializable>();
newProps.put(PublishingModel.PROP_OAUTH1_TOKEN_VALUE, accessToken.getValue());
newProps.put(PublishingModel.PROP_OAUTH1_TOKEN_SECRET, accessToken.getSecret());
getChannelService().updateChannel(channel, newProps);
authorised = true;
}
return authorised;
}
}

View File

@@ -0,0 +1,137 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.publishing.flickr;
import java.io.File;
import java.util.List;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.executer.ActionExecuterAbstractBase;
import org.alfresco.repo.content.filestore.FileContentReader;
import org.alfresco.repo.publishing.flickr.springsocial.api.Flickr;
import org.alfresco.repo.publishing.flickr.springsocial.api.MediaOperations;
import org.alfresco.repo.publishing.flickr.springsocial.api.PhotoMetadata;
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.TempFileProvider;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.social.connect.Connection;
public class FlickrPublishAction extends ActionExecuterAbstractBase
{
private final static Log log = LogFactory.getLog(FlickrPublishAction.class);
public static final String NAME = "publish_flickr";
private NodeService nodeService;
private ContentService contentService;
private TaggingService taggingService;
private FlickrPublishingHelper flickrHelper;
public void setFlickrHelper(FlickrPublishingHelper helper)
{
this.flickrHelper = helper;
}
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 nodeToPublish)
{
ContentReader reader = contentService.getReader(nodeToPublish, 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("flickr");
contentFile = TempFileProvider.createTempFile("flickr", "", tempDir);
reader.getContent(contentFile);
deleteContentFileOnCompletion = true;
}
try
{
Resource res = new FileSystemResource(contentFile);
Connection<Flickr> connection = flickrHelper.getConnectionForPublishNode(nodeToPublish);
String name = (String) nodeService.getProperty(nodeToPublish, ContentModel.PROP_NAME);
String title = (String) nodeService.getProperty(nodeToPublish, ContentModel.PROP_TITLE);
if (title == null || title.length() == 0)
{
title = name;
}
String description = (String) nodeService.getProperty(nodeToPublish, ContentModel.PROP_DESCRIPTION);
if (description == null || description.length() == 0)
{
description = title;
}
MediaOperations mediaOps = connection.getApi().mediaOperations();
PhotoMetadata metadata = mediaOps.createPhotoMetadata();
metadata.setTitle(title);
metadata.setDescription(description);
String id = mediaOps.postPhoto(res, metadata);
log.info("Posted image " + name + " to Flickr with id " + id);
nodeService.setProperty(nodeToPublish, FlickrPublishingModel.PROP_ASSET_ID, id);
}
finally
{
if (deleteContentFileOnCompletion)
{
contentFile.delete();
}
}
}
}
@Override
protected void addParameterDefinitions(List<ParameterDefinition> paramList)
{
// TODO Auto-generated method stub
}
}

View File

@@ -0,0 +1,69 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.publishing.flickr;
import org.alfresco.repo.publishing.PublishingModel;
import org.alfresco.repo.publishing.flickr.springsocial.api.Flickr;
import org.alfresco.repo.publishing.flickr.springsocial.connect.FlickrConnectionFactory;
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;
public class FlickrPublishingHelper
{
private NodeService nodeService;
private FlickrConnectionFactory connectionFactory;
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
public void setConnectionFactory(FlickrConnectionFactory connectionFactory)
{
this.connectionFactory = connectionFactory;
}
public FlickrConnectionFactory getConnectionFactory()
{
return connectionFactory;
}
public Connection<Flickr> getConnectionForPublishNode(NodeRef publishNode)
{
Connection<Flickr> connection = null;
NodeRef channelNode = nodeService.getPrimaryParent(publishNode).getParentRef();
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;
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.publishing.flickr;
import org.alfresco.service.namespace.QName;
/**
* @author Brian
*
*/
public interface FlickrPublishingModel
{
public static final String NAMESPACE = "http://www.alfresco.org/model/publishing/flickr/1.0";
public static final String PREFIX = "flickr";
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");
}

View File

@@ -0,0 +1,158 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.publishing.flickr;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.MimetypeMap;
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;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ActionService;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.publishing.channels.Channel;
import org.alfresco.service.cmr.publishing.channels.ChannelService;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.site.SiteService;
import org.alfresco.service.cmr.site.SiteVisibility;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.BaseSpringTest;
import org.alfresco.util.GUID;
import org.apache.commons.fileupload.MultipartStream;
import org.junit.Assert;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
/**
* @author Brian
*
*/
public class FlickrTest extends BaseSpringTest
{
protected ServiceRegistry serviceRegistry;
protected SiteService siteService;
protected FileFolderService fileFolderService;
protected NodeService nodeService;
protected String siteId;
protected PublishingQueueImpl queue;
protected EnvironmentImpl environment;
protected NodeRef docLib;
private ChannelService channelService;
private RetryingTransactionHelper transactionHelper;
public void onSetUp() throws Exception
{
serviceRegistry = (ServiceRegistry) getApplicationContext().getBean("ServiceRegistry");
channelService = (ChannelService) getApplicationContext().getBean("channelService");
AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());
siteService = serviceRegistry.getSiteService();
fileFolderService = serviceRegistry.getFileFolderService();
nodeService = serviceRegistry.getNodeService();
transactionHelper = serviceRegistry.getRetryingTransactionHelper();
siteId = GUID.generate();
siteService.createSite("test", siteId, "Site created by publishing test", "Site created by publishing test",
SiteVisibility.PUBLIC);
docLib = siteService.createContainer(siteId, SiteService.DOCUMENT_LIBRARY, ContentModel.TYPE_FOLDER, null);
}
public void onTearDown()
{
siteService.deleteSite(siteId);
}
public void testBlank()
{
}
//Note that this test isn't normally run, as it requires a valid Flickr OAuth token.
//To run it, remove the initial 'x' from the method name and set the appropriate values where the
//text "YOUR_OAUTH_TOKEN_VALUE" and "YOUR_OAUTH_TOKEN_SECRET" appear. Note that these can be quite tricky to obtain...
public void xtestFlickrPublishAndUnpublishActions() throws Exception
{
final NodeRef contentNode = transactionHelper.doInTransaction(new RetryingTransactionCallback<NodeRef>()
{
public NodeRef execute() throws Throwable
{
Map<QName, Serializable> props = new HashMap<QName, Serializable>();
props.put(PublishingModel.PROP_OAUTH1_TOKEN_VALUE, "YOUR_OAUTH_TOKEN_VALUE");
props.put(PublishingModel.PROP_OAUTH1_TOKEN_SECRET, "YOUR_OAUTH_TOKEN_SECRET");
props.put(PublishingModel.PROP_AUTHORISATION_COMPLETE, Boolean.TRUE);
Channel channel = channelService.createChannel(siteId, FlickrChannelType.ID, "FlickrTestChannel", props);
//This looks a little odd, but a new channel always has its "authorisation complete" flag
//forced off initially. This will force it on for this channel...
channelService.updateChannel(channel, props);
NodeRef channelNode = channel.getNodeRef();
Resource file = new ClassPathResource("test/alfresco/TestImageFile.png");
Map<QName, Serializable> contentProps = new HashMap<QName, Serializable>();
contentProps.put(ContentModel.PROP_NAME, "Test Image");
NodeRef contentNode = nodeService.createNode(channelNode, ContentModel.ASSOC_CONTAINS,
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "testImage"),
ContentModel.TYPE_CONTENT, contentProps).getChildRef();
ContentService contentService = serviceRegistry.getContentService();
ContentWriter writer = contentService.getWriter(contentNode, ContentModel.PROP_CONTENT, true);
writer.setMimetype(MimetypeMap.MIMETYPE_IMAGE_PNG);
writer.putContent(file.getFile());
return contentNode;
}
});
MultipartStream stream = new MultipartStream(null, null);
transactionHelper.doInTransaction(new RetryingTransactionCallback<NodeRef>()
{
public NodeRef execute() throws Throwable
{
ActionService actionService = serviceRegistry.getActionService();
Action publishAction = actionService.createAction(FlickrPublishAction.NAME);
actionService.executeAction(publishAction, contentNode);
Map<QName, Serializable> props = nodeService.getProperties(contentNode);
Assert.assertTrue(nodeService.hasAspect(contentNode, FlickrPublishingModel.ASPECT_ASSET));
Assert.assertNotNull(props.get(FlickrPublishingModel.PROP_ASSET_ID));
System.out.println("Asset id: " + props.get(FlickrPublishingModel.PROP_ASSET_ID));
// Action unpublishAction = actionService.createAction(YouTubeUnpublishAction.NAME);
// actionService.executeAction(unpublishAction, contentNode);
// props = nodeService.getProperties(contentNode);
// Assert.assertFalse(nodeService.hasAspect(contentNode, YouTubePublishingModel.ASPECT_ASSET));
// Assert.assertNull(props.get(YouTubePublishingModel.PROP_ASSET_ID));
// Assert.assertNull(props.get(YouTubePublishingModel.PROP_PLAYER_URL));
return null;
}
});
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright 2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.alfresco.repo.publishing.flickr.springsocial.api;
import org.alfresco.repo.publishing.flickr.springsocial.api.MediaOperations;
import org.alfresco.repo.publishing.flickr.springsocial.api.impl.FlickrTemplate;
import org.springframework.social.ApiBinding;
/**
* Interface specifying a basic set of operations for interacting with Facebook.
* Implemented by {@link FlickrTemplate}.
*
* @author Craig Walls
*/
public interface Flickr extends ApiBinding
{
boolean test();
/**
* API for performing operations on albums, photos, and videos.
*/
MediaOperations mediaOperations();
}

View File

@@ -0,0 +1,121 @@
/*
* Copyright 2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.alfresco.repo.publishing.flickr.springsocial.api;
import org.springframework.social.facebook.api.ImageType;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.ResponseExtractor;
/**
* Defines low-level operations against Facebook's Graph API
* @author Craig Walls
*/
public interface GraphApi {
/**
* Fetches an object, extracting it into the type via the given {@link ResponseExtractor}.
* Requires appropriate permission to fetch the object.
* @param objectId the Facebook object's ID
* @param type the Java type to fetch
* @return an Java object representing the requested Facebook object.
*/
<T> T fetchObject(String objectId, Class<T> type);
/**
* Fetches an object, extracting it into the type via the given {@link ResponseExtractor}.
* Requires appropriate permission to fetch the object.
* @param objectId the Facebook object's ID
* @param type the Java type to fetch
* @param queryParameters query parameters to include in the request
* @return an Java object representing the requested Facebook object.
*/
<T> T fetchObject(String objectId, Class<T> type, MultiValueMap<String, String> queryParameters);
/**
* Fetches connections, extracting them into a Java type via the given {@link ResponseExtractor}.
* Requires appropriate permission to fetch the object connection.
* @param objectId the ID of the object to retrieve the connections for.
* @param connectionType the connection type.
* @param type the Java type to fetch
* @param fields the fields to include in the response.
* @return a list of Java objects representing the Facebook objects in the connections.
*/
<T> T fetchConnections(String objectId, String connectionType, Class<T> type, String... fields);
/**
* Fetches connections, extracting them into a Java type via the given {@link ResponseExtractor}.
* Requires appropriate permission to fetch the object connection.
* @param objectId the ID of the object to retrieve the connections for.
* @param connectionType the connection type.
* @param type the Java type to fetch
* @param queryParameters query parameters to include in the request
* @return a list of Java objects representing the Facebook objects in the connections.
*/
<T> T fetchConnections(String objectId, String connectionType, Class<T> type, MultiValueMap<String, String> queryParameters);
/**
* Fetches an image as an array of bytes.
* @param objectId the object ID
* @param connectionType the connection type
* @param imageType the type of image to retrieve (eg., small, normal, large, or square)
* @return an image as an array of bytes.
*/
byte[] fetchImage(String objectId, String connectionType, ImageType imageType);
/**
* Publishes data to an object's connection.
* Requires appropriate permission to publish to the object connection.
* @param objectId the object ID to publish to.
* @param connectionType the connection type to publish to.
* @param data the data to publish to the connection.
* @return the ID of the newly published object.
*/
String publish(String objectId, String connectionType, MultiValueMap<String, Object> data);
/**
* Publishes data to an object's connection.
* Requires appropriate permission to publish to the object connection.
* This differs from publish() only in that it doesn't attempt to extract the ID from the response.
* This is because some publish operations do not return an ID in the response.
* @param objectId the object ID to publish to.
* @param connectionType the connection type to publish to.
* @param data the data to publish to the connection.
*/
void post(String objectId, String connectionType, MultiValueMap<String, String> data);
/**
* Deletes an object.
* Requires appropriate permission to delete the object.
* @param objectId the object ID
*/
void delete(String objectId);
/**
* Deletes an object connection.
* Requires appropriate permission to delete the object connection.
* @param objectId the object ID
* @param connectionType the connection type
*/
void delete(String objectId, String connectionType);
static final String GRAPH_API_URL = "https://graph.facebook.com/";
static final String OBJECT_URL = GRAPH_API_URL + "{objectId}";
static final String CONNECTION_URL = OBJECT_URL + "/{connection}";
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.alfresco.repo.publishing.flickr.springsocial.api;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.social.ApiException;
import org.springframework.social.InsufficientPermissionException;
import org.springframework.social.MissingAuthorizationException;
public interface MediaOperations {
PhotoMetadata createPhotoMetadata();
/**
* Uploads a photo to an album created specifically for the application.
* Requires "publish_stream" permission.
* If no album exists for the application, it will be created.
* @param photo A {@link Resource} for the photo data. The given Resource must implement the getFilename() method (such as {@link FileSystemResource} or {@link ClassPathResource}).
* @return the ID of the photo.
* @throws ApiException if there is an error while communicating with Facebook.
* @throws InsufficientPermissionException if the user has not granted "publish_stream" permission.
* @throws MissingAuthorizationException if FacebookTemplate was not created with an access token.
*/
String postPhoto(Resource photo, PhotoMetadata metadata);
}

View File

@@ -0,0 +1,14 @@
package org.alfresco.repo.publishing.flickr.springsocial.api;
public interface PhotoMetadata
{
void setDescription(String description);
void setTitle(String title);
String getDescription();
String getTitle();
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright 2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.alfresco.repo.publishing.flickr.springsocial.api;
public interface UserOperations {
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.alfresco.repo.publishing.flickr.springsocial.api.impl;
import org.springframework.social.MissingAuthorizationException;
class AbstractFlickrOperations {
private final boolean isAuthorized;
public AbstractFlickrOperations(boolean isAuthorized) {
this.isAuthorized = isAuthorized;
}
protected void requireAuthorization() {
if (!isAuthorized) {
throw new MissingAuthorizationException();
}
}
}

View File

@@ -0,0 +1,144 @@
/*
* Copyright 2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.alfresco.repo.publishing.flickr.springsocial.api.impl;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Map;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.social.ExpiredAuthorizationException;
import org.springframework.social.InsufficientPermissionException;
import org.springframework.social.InternalServerErrorException;
import org.springframework.social.InvalidAuthorizationException;
import org.springframework.social.MissingAuthorizationException;
import org.springframework.social.NotAuthorizedException;
import org.springframework.social.OperationNotPermittedException;
import org.springframework.social.ResourceNotFoundException;
import org.springframework.social.RevokedAuthorizationException;
import org.springframework.social.UncategorizedApiException;
import org.springframework.social.facebook.api.NotAFriendException;
import org.springframework.social.facebook.api.ResourceOwnershipException;
import org.springframework.web.client.DefaultResponseErrorHandler;
/**
* Subclass of {@link DefaultResponseErrorHandler} that handles errors from Facebook's
* Graph API, interpreting them into appropriate exceptions.
* @author Craig Walls
*/
class FlickrErrorHandler extends DefaultResponseErrorHandler {
@Override
public void handleError(ClientHttpResponse response) throws IOException {
Map<String, String> errorDetails = extractErrorDetailsFromResponse(response);
if (errorDetails == null) {
handleUncategorizedError(response, errorDetails);
}
handleFacebookError(response.getStatusCode(), errorDetails);
// if not otherwise handled, do default handling and wrap with UncategorizedApiException
handleUncategorizedError(response, errorDetails);
}
@Override
public boolean hasError(ClientHttpResponse response) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(response.getBody()));
return super.hasError(response) || (reader.ready() && reader.readLine().startsWith("{\"error\":"));
}
/**
* Examines the error data returned from Facebook and throws the most applicable exception.
* @param errorDetails a Map containing a "type" and a "message" corresponding to the Graph API's error response structure.
*/
void handleFacebookError(HttpStatus statusCode, Map<String, String> errorDetails) {
// Can't trust the type to be useful. It's often OAuthException, even for things not OAuth-related.
// Can rely only on the message (which itself isn't very consistent).
String message = errorDetails.get("message");
if (statusCode == HttpStatus.OK) {
if (message.contains("Some of the aliases you requested do not exist")) {
throw new ResourceNotFoundException(message);
}
} else if (statusCode == HttpStatus.BAD_REQUEST) {
if (message.contains("Unknown path components")) {
throw new ResourceNotFoundException(message);
} else if (message.equals("An access token is required to request this resource.")) {
throw new MissingAuthorizationException();
} else if (message.equals("An active access token must be used to query information about the current user.")) {
throw new MissingAuthorizationException();
} else if (message.startsWith("Error validating access token")) {
if (message.contains("Session has expired at unix time")) {
throw new ExpiredAuthorizationException();
} else if (message.contains("The session has been invalidated because the user has changed the password.")) {
throw new RevokedAuthorizationException();
} else if (message.contains("The session is invalid because the user logged out.")) {
throw new RevokedAuthorizationException();
} else if (message.contains("has not authorized application")) {
// Per https://developers.facebook.com/blog/post/500/, this could be in the message when the user removes the application.
// In reality, "The session has been invalidated because the user has changed the password." is what you get in that case.
// Leaving this check in place in case there FB does return this message (could be a bug in FB?)
throw new RevokedAuthorizationException();
} else {
throw new InvalidAuthorizationException(message);
}
} else if (message.equals("Error validating application.")) { // Access token with incorrect app ID
throw new InvalidAuthorizationException(message);
} else if (message.equals("Invalid access token signature.")) { // Access token that fails signature validation
throw new InvalidAuthorizationException(message);
}
} else if (statusCode == HttpStatus.UNAUTHORIZED) {
throw new NotAuthorizedException(message);
} else if (statusCode == HttpStatus.FORBIDDEN) {
if (message.contains("Requires extended permission")) {
String requiredPermission = message.split(": ")[1];
throw new InsufficientPermissionException(requiredPermission);
} else {
throw new OperationNotPermittedException(message);
}
} else if (statusCode == HttpStatus.INTERNAL_SERVER_ERROR) {
if (message.equals("User must be an owner of the friendlist")) { // watch for pattern in similar message in other resources
throw new ResourceOwnershipException(message);
} else if (message.equals("The member must be a friend of the current user.")) {
throw new NotAFriendException(message);
} else {
throw new InternalServerErrorException(message);
}
}
}
private void handleUncategorizedError(ClientHttpResponse response, Map<String, String> errorDetails) {
try {
super.handleError(response);
} catch (Exception e) {
if (errorDetails != null) {
throw new UncategorizedApiException(errorDetails.get("message"), e);
} else {
throw new UncategorizedApiException("No error details from Facebook", e);
}
}
}
/*
* Attempts to extract Facebook error details from the response.
* Returns null if the response doesn't match the expected JSON error response.
*/
private Map<String, String> extractErrorDetailsFromResponse(ClientHttpResponse response) throws IOException {
return null;
}
}

View File

@@ -0,0 +1,114 @@
/*
* Copyright 2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.alfresco.repo.publishing.flickr.springsocial.api.impl;
import java.net.URI;
import java.util.List;
import org.alfresco.repo.publishing.flickr.springsocial.api.Flickr;
import org.alfresco.repo.publishing.flickr.springsocial.api.MediaOperations;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.social.NotAuthorizedException;
import org.springframework.social.oauth1.AbstractOAuth1ApiBinding;
import org.springframework.social.support.ClientHttpRequestFactorySelector;
import org.springframework.social.support.URIBuilder;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
/**
* <p>This is the central class for interacting with Facebook.</p>
* <p>
* There are some operations, such as searching, that do not require OAuth
* authentication. In those cases, you may use a {@link FlickrTemplate} that is
* created through the default constructor and without any OAuth details.
* Attempts to perform secured operations through such an instance, however,
* will result in {@link NotAuthorizedException} being thrown.
* </p>
* @author Craig Walls
*/
public class FlickrTemplate extends AbstractOAuth1ApiBinding implements Flickr {
private final String REST_ENDPOINT = "http://api.flickr.com/services/rest/";
private String consumerKey;
private MediaOperations mediaOperations;
/**
* Create a new instance of FacebookTemplate.
* This constructor creates a new FacebookTemplate able to perform unauthenticated operations against Facebook's Graph API.
* Some operations do not require OAuth authentication.
* For example, retrieving a specified user's profile or feed does not require authentication (although the data returned will be limited to what is publicly available).
* A FacebookTemplate created with this constructor will support those operations.
* Those operations requiring authentication will throw {@link NotAuthorizedException}.
*/
public FlickrTemplate() {
initialize();
}
/**
* Create a new instance of FacebookTemplate.
* This constructor creates the FacebookTemplate using a given access token.
* @param accessToken An access token given by Facebook after a successful OAuth 2 authentication (or through Facebook's JS library).
*/
public FlickrTemplate(String consumerKey, String consumerSecret, String accessToken, String accessTokenSecret) {
super(consumerKey, consumerSecret, accessToken, accessTokenSecret);
this.consumerKey = consumerKey;
initialize();
}
private void initSubApis() {
mediaOperations = new MediaTemplate(consumerKey, getRestTemplate(), isAuthorized());
}
@Override
public void setRequestFactory(ClientHttpRequestFactory requestFactory) {
// Wrap the request factory with a BufferingClientHttpRequestFactory so that the error handler can do repeat reads on the response.getBody()
super.setRequestFactory(ClientHttpRequestFactorySelector.bufferRequests(requestFactory));
}
public MediaOperations mediaOperations() {
return mediaOperations;
}
@Override
protected List<HttpMessageConverter<?>> getMessageConverters() {
List<HttpMessageConverter<?>> messageConverters = super.getMessageConverters();
messageConverters.add(new ByteArrayHttpMessageConverter());
return messageConverters;
}
// private helpers
private void initialize() {
getRestTemplate().setErrorHandler(new FlickrErrorHandler());
// Wrap the request factory with a BufferingClientHttpRequestFactory so that the error handler can do repeat reads on the response.getBody()
super.setRequestFactory(ClientHttpRequestFactorySelector.bufferRequests(getRestTemplate().getRequestFactory()));
initSubApis();
}
@Override
public boolean test()
{
MultiValueMap<String, String> params = new LinkedMultiValueMap<String, String>();
params.add("api_key", consumerKey);
params.add("format", "json");
params.add("method", "flickr.test.login");
params.add("nojsoncallback", "1");
URI uri = URIBuilder.fromUri(REST_ENDPOINT).queryParams(params).build();
getRestTemplate().getForObject(uri, String.class);
return true;
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright 2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.alfresco.repo.publishing.flickr.springsocial.api.impl;
import java.net.URI;
import org.alfresco.repo.publishing.flickr.springsocial.api.MediaOperations;
import org.alfresco.repo.publishing.flickr.springsocial.api.PhotoMetadata;
import org.alfresco.repo.publishing.flickr.springsocial.api.impl.AbstractFlickrOperations;
import org.springframework.core.io.Resource;
import org.springframework.social.support.URIBuilder;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
class MediaTemplate extends AbstractFlickrOperations implements MediaOperations
{
private final RestTemplate restTemplate;
private String consumerKey;
public MediaTemplate(String consumerKey, RestTemplate restTemplate, boolean isAuthorizedForUser)
{
super(isAuthorizedForUser);
this.restTemplate = restTemplate;
this.consumerKey = consumerKey;
}
public String postPhoto(Resource photo, PhotoMetadata metadata)
{
requireAuthorization();
MultiValueMap<String, Object> parts = new LinkedMultiValueMap<String, Object>();
parts.set("api_key", consumerKey);
if (metadata.getDescription() != null)
parts.set("description", metadata.getDescription());
parts.set("photo", photo);
if (metadata.getTitle() != null)
parts.set("title", metadata.getTitle());
URI uri = URIBuilder.fromUri("http://api.flickr.com/services/upload/").build();
String response = restTemplate.postForObject(uri, parts, String.class);
return (String) response;
}
@Override
public PhotoMetadata createPhotoMetadata()
{
return new PhotoMetadataImpl();
}
}

View File

@@ -0,0 +1,29 @@
package org.alfresco.repo.publishing.flickr.springsocial.api.impl;
import org.alfresco.repo.publishing.flickr.springsocial.api.PhotoMetadata;
public class PhotoMetadataImpl implements PhotoMetadata
{
private String title;
private String description;
public String getTitle()
{
return title;
}
public void setTitle(String title)
{
this.title = title;
}
public String getDescription()
{
return description;
}
public void setDescription(String description)
{
this.description = description;
}
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright 2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.alfresco.repo.publishing.flickr.springsocial.api.impl;
import org.alfresco.repo.publishing.flickr.springsocial.api.UserOperations;
class UserTemplate extends AbstractFlickrOperations implements UserOperations {
public UserTemplate(boolean isAuthorized)
{
super(isAuthorized);
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright 2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.alfresco.repo.publishing.flickr.springsocial.connect;
import org.alfresco.repo.publishing.flickr.springsocial.api.Flickr;
import org.springframework.social.ApiException;
import org.springframework.social.connect.ApiAdapter;
import org.springframework.social.connect.ConnectionValues;
import org.springframework.social.connect.UserProfile;
import org.springframework.social.connect.UserProfileBuilder;
/**
* Facebook ApiAdapter implementation.
*
* @author Keith Donald
*/
public class FlickrAdapter implements ApiAdapter<Flickr>
{
public boolean test(Flickr flickr)
{
try
{
flickr.test();
return true;
}
catch (ApiException e)
{
return false;
}
}
public void setConnectionValues(Flickr facebook, ConnectionValues values)
{
}
public UserProfile fetchUserProfile(Flickr facebook)
{
return new UserProfileBuilder().setName("Brian").setFirstName("Brian").setLastName(
"Brian").setEmail("Brian").setUsername("Brian").build();
}
public void updateStatus(Flickr facebook, String message)
{
}
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright 2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.alfresco.repo.publishing.flickr.springsocial.connect;
import org.alfresco.repo.publishing.flickr.springsocial.api.Flickr;
import org.alfresco.repo.publishing.flickr.springsocial.connect.FlickrAdapter;
import org.springframework.social.connect.support.OAuth1ConnectionFactory;
public class FlickrConnectionFactory extends OAuth1ConnectionFactory<Flickr> {
public FlickrConnectionFactory(String consumerKey, String consumerSecret) {
super("flickr", new FlickrServiceProvider(consumerKey, consumerSecret), new FlickrAdapter());
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright 2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.alfresco.repo.publishing.flickr.springsocial.connect;
import org.alfresco.repo.publishing.flickr.springsocial.api.Flickr;
import org.alfresco.repo.publishing.flickr.springsocial.api.impl.FlickrTemplate;
import org.springframework.social.oauth1.AbstractOAuth1ServiceProvider;
import org.springframework.social.oauth1.OAuth1Template;
public class FlickrServiceProvider extends AbstractOAuth1ServiceProvider<Flickr> {
public FlickrServiceProvider(String consumerKey, String consumerSecret) {
super(consumerKey, consumerSecret, new OAuth1Template(consumerKey, consumerSecret,
"http://www.flickr.com/services/oauth/request_token",
"http://www.flickr.com/services/oauth/authorize",
"http://www.flickr.com/services/oauth/access_token"));
}
public Flickr getApi(String accessToken, String secret) {
return new FlickrTemplate(getConsumerKey(), getConsumerSecret(), accessToken, secret);
}
}

View File

@@ -0,0 +1,4 @@
/**
* Flickr service provider connection repository and API adapter implementations.
*/
package org.alfresco.repo.publishing.flickr.springsocial.connect;

View File

@@ -22,9 +22,7 @@ import java.io.Serializable;
import java.util.Collections; import java.util.Collections;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.TreeSet;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.publishing.AbstractChannelType; import org.alfresco.repo.publishing.AbstractChannelType;
import org.alfresco.service.cmr.action.Action; import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ActionService; import org.alfresco.service.cmr.action.ActionService;
@@ -36,28 +34,11 @@ import org.alfresco.service.namespace.QName;
public class SlideShareChannelType extends AbstractChannelType public class SlideShareChannelType extends AbstractChannelType
{ {
public final static String ID = "slideshare"; public final static String ID = "slideshare";
private final static Set<String> DEFAULT_MIME_TYPES = new TreeSet<String>();
private NodeService nodeService; private NodeService nodeService;
private ActionService actionService; private ActionService actionService;
private Set<String> permittedMimeTypes = Collections.unmodifiableSet(DEFAULT_MIME_TYPES); private SlideSharePublishingHelper publishingHelper;
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) public void setNodeService(NodeService nodeService)
{ {
this.nodeService = nodeService; this.nodeService = nodeService;
@@ -68,13 +49,9 @@ public class SlideShareChannelType extends AbstractChannelType
this.actionService = actionService; this.actionService = actionService;
} }
public void setPermittedMimeTypes(Set<String> permittedMimeTypes) public void setPublishingHelper(SlideSharePublishingHelper publishingHelper)
{ {
if (permittedMimeTypes == null) this.publishingHelper = publishingHelper;
{
permittedMimeTypes = Collections.emptySet();
}
this.permittedMimeTypes = Collections.unmodifiableSet(permittedMimeTypes);
} }
@Override @Override
@@ -116,7 +93,7 @@ public class SlideShareChannelType extends AbstractChannelType
@Override @Override
public Set<String> getSupportedMimetypes() public Set<String> getSupportedMimetypes()
{ {
return permittedMimeTypes; return publishingHelper.getAllowedMimeTypes().keySet();
} }
@Override @Override

View File

@@ -24,7 +24,6 @@ import java.util.List;
import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel; import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.executer.ActionExecuterAbstractBase; 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.Action;
import org.alfresco.service.cmr.action.ParameterDefinition; import org.alfresco.service.cmr.action.ParameterDefinition;
import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentReader;
@@ -38,6 +37,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import com.benfante.jslideshare.SlideShareAPI; import com.benfante.jslideshare.SlideShareAPI;
import com.benfante.jslideshare.messages.Slideshow;
public class SlideSharePublishAction extends ActionExecuterAbstractBase public class SlideSharePublishAction extends ActionExecuterAbstractBase
{ {
@@ -84,20 +84,19 @@ public class SlideSharePublishAction extends ActionExecuterAbstractBase
if (reader.exists()) if (reader.exists())
{ {
File contentFile; File contentFile;
String mime = reader.getMimetype();
String extension = slideShareHelper.getAllowedMimeTypes().get(mime);
if (extension == null) extension = "";
boolean deleteContentFileOnCompletion = false; boolean deleteContentFileOnCompletion = false;
if (FileContentReader.class.isAssignableFrom(reader.getClass()))
{ //SlideShare seems to work entirely off file extension, so we always copy onto the
//Grab the content straight from the content store if we can... //file system and upload from there.
contentFile = ((FileContentReader)reader).getFile(); File tempDir = TempFileProvider.getLongLifeTempDir("slideshare");
} contentFile = TempFileProvider.createTempFile("slideshare", extension, tempDir);
else reader.getContent(contentFile);
{ deleteContentFileOnCompletion = true;
//...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 name = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_NAME);
String title = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_TITLE); String title = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_TITLE);
@@ -121,8 +120,17 @@ public class SlideSharePublishAction extends ActionExecuterAbstractBase
String assetId = api.uploadSlideshow(usernamePassword.getFirst(), usernamePassword.getSecond(), title, String assetId = api.uploadSlideshow(usernamePassword.getFirst(), usernamePassword.getSecond(), title,
contentFile, description, tags.toString(), false, false, false, false, false); contentFile, description, tags.toString(), false, false, false, false, false);
// String url = api.getSlideshow(assetId).getPermalink();
String url = null; String url = null;
Slideshow slides = api.getSlideshow(assetId);
if (slides != null)
{
url = slides.getPermalink();
if (log.isInfoEnabled())
{
log.info("SlideShare has provided a URL for asset " + assetId + ": " + url);
}
}
if (log.isInfoEnabled()) if (log.isInfoEnabled())
{ {
log.info("File " + name + " has been published to SlideShare with id " + assetId + " at URL " + url); log.info("File " + name + " has been published to SlideShare with id " + assetId + " at URL " + url);

View File

@@ -18,6 +18,11 @@
*/ */
package org.alfresco.repo.publishing.slideshare; package org.alfresco.repo.publishing.slideshare;
import java.util.Collections;
import java.util.Map;
import java.util.TreeMap;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.publishing.PublishingModel; import org.alfresco.repo.publishing.PublishingModel;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.NodeService;
@@ -28,6 +33,24 @@ import com.benfante.jslideshare.SlideShareConnector;
public class SlideSharePublishingHelper public class SlideSharePublishingHelper
{ {
private final static Map<String,String> DEFAULT_MIME_TYPES = new TreeMap<String,String>();
static
{
DEFAULT_MIME_TYPES.put(MimetypeMap.MIMETYPE_PPT, ".ppt");
DEFAULT_MIME_TYPES.put(MimetypeMap.MIMETYPE_PDF, ".pdf");
DEFAULT_MIME_TYPES.put(MimetypeMap.MIMETYPE_OPENDOCUMENT_PRESENTATION, ".odp");
DEFAULT_MIME_TYPES.put(MimetypeMap.MIMETYPE_OPENXML_PRESENTATION, ".pptx");
DEFAULT_MIME_TYPES.put(MimetypeMap.MIMETYPE_IWORK_KEYNOTE, "");
DEFAULT_MIME_TYPES.put(MimetypeMap.MIMETYPE_IWORK_PAGES, "");
DEFAULT_MIME_TYPES.put(MimetypeMap.MIMETYPE_TEXT_PLAIN, ".txt");
DEFAULT_MIME_TYPES.put(MimetypeMap.MIMETYPE_OPENDOCUMENT_TEXT, ".odt");
DEFAULT_MIME_TYPES.put(MimetypeMap.MIMETYPE_TEXT_CSV, ".csv");
DEFAULT_MIME_TYPES.put(MimetypeMap.MIMETYPE_EXCEL, ".xls");
DEFAULT_MIME_TYPES.put(MimetypeMap.MIMETYPE_OPENXML_WORDPROCESSING, ".docx");
DEFAULT_MIME_TYPES.put(MimetypeMap.MIMETYPE_OPENDOCUMENT_SPREADSHEET, ".ods");
}
private Map<String, String> allowedMimeTypes = Collections.unmodifiableMap(DEFAULT_MIME_TYPES);
private NodeService nodeService; private NodeService nodeService;
private SlideShareConnector slideshareConnector; private SlideShareConnector slideshareConnector;
@@ -41,6 +64,16 @@ public class SlideSharePublishingHelper
this.slideshareConnector = slideshareConnector; this.slideshareConnector = slideshareConnector;
} }
public Map<String, String> getAllowedMimeTypes()
{
return allowedMimeTypes;
}
public void setAllowedMimeTypes(Map<String, String> allowedMimeTypes)
{
this.allowedMimeTypes = Collections.unmodifiableMap(allowedMimeTypes);
}
public SlideShareAPI getSlideShareApi() public SlideShareAPI getSlideShareApi()
{ {
return createApiObject(); return createApiObject();

View File

@@ -17,7 +17,7 @@ public class BitlyUrlShortenerImpl implements UrlShortener
{ {
private static final Log log = LogFactory.getLog(BitlyUrlShortenerImpl.class); private static final Log log = LogFactory.getLog(BitlyUrlShortenerImpl.class);
private int urlLength; private int urlLength = 20;
private String username; private String username;
private String apiKey = "R_ca15c6c89e9b25ccd170bafd209a0d4f"; private String apiKey = "R_ca15c6c89e9b25ccd170bafd209a0d4f";
private HttpClient httpClient; private HttpClient httpClient;

View File

@@ -64,11 +64,10 @@ public interface ChannelService
Channel createChannel(String siteId, String channelTypeId, String name, Map<QName, Serializable> properties); Channel createChannel(String siteId, String channelTypeId, String name, Map<QName, Serializable> properties);
/** /**
* Remove the channel with the specified name on the specified Share site. * Remove the specified channel.
* @param siteId The identifier of the Share site that contains the channel to be deleted. * @param channel The channel to delete.
* @param channelName The name of the channel that is to be deleted.
*/ */
void deleteChannel(String siteId, String channelName); void deleteChannel(Channel channel);
/** /**
* Rename the specified channel * Rename the specified channel