From a0834aed8ed758423ee7c094a475c33c274632e5 Mon Sep 17 00:00:00 2001 From: Mike Hatfield Date: Wed, 20 Feb 2008 09:36:44 +0000 Subject: [PATCH] Blog Integration module moved into core git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@8333 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- config/alfresco/blog-context.xml | 33 +- .../alfresco/model/BlogIntegrationModel.java | 53 +++ .../repo/action/executer/BlogAction.java | 171 ++++++++ .../BaseBlogIntegrationImplementation.java | 96 +++++ .../repo/blogIntegration/BlogDetails.java | 213 ++++++++++ .../BlogIntegrationImplementation.java | 90 +++++ .../BlogIntegrationRuntimeException.java | 82 ++++ .../BlogIntegrationService.java | 98 +++++ .../BlogIntegrationServiceImpl.java | 375 ++++++++++++++++++ .../BlogIntegrationServiceSystemTest.java | 259 ++++++++++++ .../DefaultBlogIntegrationImplementation.java | 232 +++++++++++ .../typepad/TypepadIntegration.java | 60 +++ .../wordpress/WordPressIntegration.java | 47 +++ 13 files changed, 1781 insertions(+), 28 deletions(-) create mode 100644 source/java/org/alfresco/model/BlogIntegrationModel.java create mode 100644 source/java/org/alfresco/repo/action/executer/BlogAction.java create mode 100644 source/java/org/alfresco/repo/blogIntegration/BaseBlogIntegrationImplementation.java create mode 100644 source/java/org/alfresco/repo/blogIntegration/BlogDetails.java create mode 100644 source/java/org/alfresco/repo/blogIntegration/BlogIntegrationImplementation.java create mode 100644 source/java/org/alfresco/repo/blogIntegration/BlogIntegrationRuntimeException.java create mode 100644 source/java/org/alfresco/repo/blogIntegration/BlogIntegrationService.java create mode 100644 source/java/org/alfresco/repo/blogIntegration/BlogIntegrationServiceImpl.java create mode 100644 source/java/org/alfresco/repo/blogIntegration/BlogIntegrationServiceSystemTest.java create mode 100644 source/java/org/alfresco/repo/blogIntegration/DefaultBlogIntegrationImplementation.java create mode 100644 source/java/org/alfresco/repo/blogIntegration/typepad/TypepadIntegration.java create mode 100644 source/java/org/alfresco/repo/blogIntegration/wordpress/WordPressIntegration.java diff --git a/config/alfresco/blog-context.xml b/config/alfresco/blog-context.xml index 3b1019da4f..dca512fa97 100644 --- a/config/alfresco/blog-context.xml +++ b/config/alfresco/blog-context.xml @@ -7,7 +7,7 @@ - org.alfresco.module.blogIntegration.BlogIntegrationService + org.alfresco.repo.blogIntegration.BlogIntegrationService @@ -35,7 +35,7 @@ - + @@ -46,42 +46,19 @@ - + - + - - - - - - classpath:alfresco/module/org.alfresco.module.blogIntegration/ui/web-client-custom.xml - - - - - - - - - - - - - - - - - - + diff --git a/source/java/org/alfresco/model/BlogIntegrationModel.java b/source/java/org/alfresco/model/BlogIntegrationModel.java new file mode 100644 index 0000000000..4c096a107f --- /dev/null +++ b/source/java/org/alfresco/model/BlogIntegrationModel.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing + */ +package org.alfresco.model; + +import org.alfresco.service.namespace.QName; + +/** + * @author Roy Wetherall + */ +public interface BlogIntegrationModel +{ + static final String MODEL_URL = "http://www.alfresco.org/model/blogintegration/1.0"; + static final String MODEL_PREFIX = "blg"; + + static final QName ASPECT_BLOG_DETAILS = QName.createQName(MODEL_URL, "blogDetails"); + static final QName PROP_BLOG_IMPLEMENTATION = QName.createQName(MODEL_URL, "blogImplementation"); + static final QName PROP_ID = QName.createQName(MODEL_URL, "id"); + static final QName PROP_NAME = QName.createQName(MODEL_URL, "name"); + static final QName PROP_DESCRIPTION = QName.createQName(MODEL_URL, "description"); + static final QName PROP_URL = QName.createQName(MODEL_URL, "url"); + static final QName PROP_USER_NAME = QName.createQName(MODEL_URL, "userName"); + static final QName PROP_PASSWORD = QName.createQName(MODEL_URL, "password"); + + static final QName ASPECT_BLOG_POST = QName.createQName(MODEL_URL, "blogPost"); + static final QName PROP_POST_ID = QName.createQName(MODEL_URL, "postId"); + static final QName PROP_PUBLISHED = QName.createQName(MODEL_URL, "published"); + static final QName PROP_LINK = QName.createQName(MODEL_URL, "link"); + static final QName PROP_POSTED = QName.createQName(MODEL_URL, "posted"); + static final QName PROP_LAST_UPDATE = QName.createQName(MODEL_URL, "lastUpdate"); + static final QName ASSOC_BLOG_DETAILS = QName.createQName(MODEL_URL, "blogDetails"); +} diff --git a/source/java/org/alfresco/repo/action/executer/BlogAction.java b/source/java/org/alfresco/repo/action/executer/BlogAction.java new file mode 100644 index 0000000000..1732db238e --- /dev/null +++ b/source/java/org/alfresco/repo/action/executer/BlogAction.java @@ -0,0 +1,171 @@ +/** + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing + */ + +package org.alfresco.repo.action.executer; + +import java.util.List; + +import org.alfresco.repo.action.ParameterDefinitionImpl; +import org.alfresco.repo.blogIntegration.BlogDetails; +import org.alfresco.repo.blogIntegration.BlogIntegrationRuntimeException; +import org.alfresco.repo.blogIntegration.BlogIntegrationService; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.action.ParameterDefinition; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.QName; + +import org.alfresco.model.BlogIntegrationModel; +import org.alfresco.model.ContentModel; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Post blog repository action + * + * @author mikeh + */ +public class BlogAction extends ActionExecuterAbstractBase implements BlogIntegrationModel +{ + public static final String NAME = "blog-post"; + public static final String PARAM_BLOG_ACTION = "action"; + + private static Log logger = LogFactory.getLog(BlogAction.class); + + private DictionaryService dictionaryService; + private NodeService nodeService; + private BlogIntegrationService blogIntegrationService; + + /** + * Set the node service + * + * @param nodeService the node service + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * Set the dictionary service + * + * @param dictionaryService the dictionary service + */ + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + /** + * Set the blog integration service + * + * @param blogIntegrationService the blog integration service + */ + public void setBlogIntegrationService(BlogIntegrationService blogIntegrationService) + { + this.blogIntegrationService = blogIntegrationService; + } + + /** + * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + protected void executeImpl(Action action, NodeRef actionedUponNodeRef) + { + String blogAction = (String)action.getParameterValue(PARAM_BLOG_ACTION); + try + { + if ("post".equals(blogAction) == true) + { + QName type = this.nodeService.getType(actionedUponNodeRef); + if (this.dictionaryService.isSubClass(type, ContentModel.TYPE_CONTENT) == true) + { + List list = this.blogIntegrationService.getBlogDetails(actionedUponNodeRef); + if (list.size() != 0) + { + // Take the 'nearest' blog details + BlogDetails blogDetails = list.get(0); + this.blogIntegrationService.newPost(blogDetails, actionedUponNodeRef, ContentModel.PROP_CONTENT, true); + } + } + } + else if ("update".equals(blogAction) == true) + { + QName type = this.nodeService.getType(actionedUponNodeRef); + if (this.nodeService.hasAspect(actionedUponNodeRef, ASPECT_BLOG_POST) == true && + this.dictionaryService.isSubClass(type, ContentModel.TYPE_CONTENT) == true) + { + this.blogIntegrationService.updatePost(actionedUponNodeRef, ContentModel.PROP_CONTENT, true); + } + } + else if ("remove".equals(blogAction) == true) + { + QName type = this.nodeService.getType(actionedUponNodeRef); + if (this.nodeService.hasAspect(actionedUponNodeRef, ASPECT_BLOG_POST) == true && + this.dictionaryService.isSubClass(type, ContentModel.TYPE_CONTENT) == true) + { + this.blogIntegrationService.deletePost(actionedUponNodeRef); + } + } + else + { + throw new BlogIntegrationRuntimeException("Invalid action has been specified '" + blogAction + "'"); + } + + action.setParameterValue(PARAM_RESULT, ""); + } + catch (BlogIntegrationRuntimeException ex) + { + action.setParameterValue(PARAM_RESULT, ex.getMessage()); + } + catch (Exception ex) + { + action.setParameterValue(PARAM_RESULT, "Action failed. Please check blog configuration parameters."); + } + } + + /** + * @see org.alfresco.repo.action.ParameterizedItemAbstractBase#addParameterDefinitions(java.util.List) + */ + @Override + protected void addParameterDefinitions(List paramList) + { + // Add definitions for action parameters + paramList.add( + new ParameterDefinitionImpl(PARAM_BLOG_ACTION, + DataTypeDefinition.TEXT, + true, + getParamDisplayLabel(PARAM_BLOG_ACTION))); + + paramList.add( + new ParameterDefinitionImpl(PARAM_RESULT, + DataTypeDefinition.TEXT, + false, + getParamDisplayLabel(PARAM_RESULT))); + + } +} diff --git a/source/java/org/alfresco/repo/blogIntegration/BaseBlogIntegrationImplementation.java b/source/java/org/alfresco/repo/blogIntegration/BaseBlogIntegrationImplementation.java new file mode 100644 index 0000000000..a6ec5b8b6a --- /dev/null +++ b/source/java/org/alfresco/repo/blogIntegration/BaseBlogIntegrationImplementation.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing + */ +package org.alfresco.repo.blogIntegration; + +/** + * Base blog implementation class. Extend this when writting a blog integration implementation. + * + * @author Roy Wetherall + */ +public abstract class BaseBlogIntegrationImplementation implements BlogIntegrationImplementation +{ + /** Blog integration service */ + private BlogIntegrationService blogIntegrationService; + + /** Integration name */ + private String name; + + /** Display name */ + private String displayName; + + /** + * Sets the blog integration service + * + * @param blogIntegrationService the blog integration service + */ + public void setBlogIntegrationService(BlogIntegrationService blogIntegrationService) + { + this.blogIntegrationService = blogIntegrationService; + } + + /** + * Registers the blog implementation with the blog integration service. + */ + public void register() + { + this.blogIntegrationService.register(this); + } + + /** + * Sets the name of the blog integration service + * + * @param name the name + */ + public void setName(String name) + { + this.name = name; + } + + /** + * @see org.alfresco.module.blogIntegration.BlogIntegrationImplementation#getName() + */ + public String getName() + { + return this.name; + } + + /** + * Sets the display name + * + * @param displayName the display name + */ + public void setDisplayName(String displayName) + { + this.displayName = displayName; + } + + /** + * @see org.alfresco.module.blogIntegration.BlogIntegrationImplementation#getDisplayName() + */ + public String getDisplayName() + { + return this.displayName; + } +} diff --git a/source/java/org/alfresco/repo/blogIntegration/BlogDetails.java b/source/java/org/alfresco/repo/blogIntegration/BlogDetails.java new file mode 100644 index 0000000000..381c22e9a0 --- /dev/null +++ b/source/java/org/alfresco/repo/blogIntegration/BlogDetails.java @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing + */ +package org.alfresco.repo.blogIntegration; + +import java.io.Serializable; +import java.util.Map; + +import org.alfresco.model.BlogIntegrationModel; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.QName; + +/** + * Blog details. Contains the detail of a blog. + * + * @author Roy Wetherall + */ +public class BlogDetails implements BlogIntegrationModel +{ + /** Node that has the blog details aspect applied */ + private NodeRef nodeRef; + + /** The blog implementation name (eg: wordpress, typepad, etc) */ + private String implementationName; + + /** The blog id */ + private String blogId; + + /** The blog URL */ + private String url; + + /** The user name */ + private String userName; + + /** The password */ + private String password; + + /** The display name of the blog */ + private String name; + + /** The description of the blog */ + private String description; + + /** + * Create a BlogDetails object from a node that has the blogDetails aspect applied. + * + * @param nodeService the node service + * @param nodeRef the node reference + * @return BlogDetails the blog details + */ + public static BlogDetails createBlogDetails(NodeService nodeService, NodeRef nodeRef) + { + // Check for the blog details aspect + if (nodeService.hasAspect(nodeRef, ASPECT_BLOG_DETAILS) == false) + { + throw new BlogIntegrationRuntimeException("Can not create blog details object since node does not have blogDetails aspect."); + } + + // Get the blog details + Map props = nodeService.getProperties(nodeRef); + return new BlogDetails( + (String)props.get(PROP_BLOG_IMPLEMENTATION), + (String)props.get(PROP_ID), + (String)props.get(PROP_URL), + (String)props.get(PROP_USER_NAME), + (String)props.get(PROP_PASSWORD), + (String)props.get(PROP_NAME), + (String)props.get(PROP_DESCRIPTION), + nodeRef); + } + + /** + * Constructor + * + * @param implementationName the implementation name + * @param blogId the blog id + * @param url the blog URL + * @param userName the user name + * @param password the password + * @param name the name + * @param description the description + */ + public BlogDetails(String implementationName, String blogId, String url, String userName, String password, String name, String description) + { + this(implementationName, blogId, url, userName, password, name, description, null); + } + + /** + * Constructor + * + * @param implementationName the implementation name + * @param blogId the blog id + * @param url the blog URL + * @param userName the user name + * @param password the password + * @param name the name + * @param description the description + * @param nodeRef the node reference + */ + public BlogDetails(String implementationName, String blogId, String url, String userName, String password, String name, String description, NodeRef nodeRef) + { + this.implementationName = implementationName; + this.blogId = blogId; + this.url = url; + this.userName = userName; + this.password = password; + this.name = name; + this.description = description; + this.nodeRef = nodeRef; + } + + /** + * Gets the node reference + * + * @return NodeRef the node reference + */ + public NodeRef getNodeRef() + { + return nodeRef; + } + + /** + * Get the implementation name + * + * @return String the implementation name + */ + public String getImplementationName() + { + return this.implementationName; + } + + /** + * Get the blog id + * + * @return String the blog id + */ + public String getBlogId() + { + return this.blogId; + } + + /** + * Get the blog URL + * + * @return String the blog URL + */ + public String getUrl() + { + return this.url; + } + + /** + * Get the user name + * + * @return String the user name + */ + public String getUserName() + { + return this.userName; + } + + /** + * Get the password + * + * @return String the password + */ + public String getPassword() + { + return this.password; + } + + /** + * Get the name + * + * @return String the name + */ + public String getName() + { + return name; + } + + /** + * Get the description + * + * @return String the description + */ + public String getDescription() + { + return description; + } +} diff --git a/source/java/org/alfresco/repo/blogIntegration/BlogIntegrationImplementation.java b/source/java/org/alfresco/repo/blogIntegration/BlogIntegrationImplementation.java new file mode 100644 index 0000000000..c8b9a5a604 --- /dev/null +++ b/source/java/org/alfresco/repo/blogIntegration/BlogIntegrationImplementation.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing + */ +package org.alfresco.repo.blogIntegration; + +import java.util.Map; + +/** + * Blog integration implementation interface + * + * @author Roy Wetherall + */ +public interface BlogIntegrationImplementation +{ + /** + * Gets the name of the blog integration + * + * @return String the name of the blog integration + */ + String getName(); + + /** + * Gets the display name of the blog integration + * + * @return String the display name of the blog integration + */ + String getDisplayName(); + + /** + * Create a new post on the blog. + * + * @param blogDetails the blog details + * @param title the title of the post + * @param body the body of the post + * @param publish indicates whether the post is published or not + * @return String the newly created post id + */ + String newPost(BlogDetails blogDetails, String title, String body, boolean publish); + + /** + * Update an exisiting blog post + * + * @param blogDetails + * @param postId + * @param title + * @param body + * @param publish + * @return + */ + boolean updatePost(BlogDetails blogDetails, String postId, String title, String body, boolean publish); + + /** + * Get the details of an existing blog post + * + * @param blogDetails + * @param postId + * @return + */ + Map getPost(BlogDetails blogDetails, String postId); + + /** + * Delete an existing blog post + * + * @param blogDetails + * @param postId + * @return + */ + boolean deletePost(BlogDetails blogDetails, String postId); +} diff --git a/source/java/org/alfresco/repo/blogIntegration/BlogIntegrationRuntimeException.java b/source/java/org/alfresco/repo/blogIntegration/BlogIntegrationRuntimeException.java new file mode 100644 index 0000000000..bf2f7ab042 --- /dev/null +++ b/source/java/org/alfresco/repo/blogIntegration/BlogIntegrationRuntimeException.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing + */ +package org.alfresco.repo.blogIntegration; + +import org.alfresco.error.AlfrescoRuntimeException; + +/** + * Blog integration runtime exception + * + * @author Roy Wetherall + */ +public class BlogIntegrationRuntimeException extends AlfrescoRuntimeException +{ + private static final long serialVersionUID = -159901552962025003L; + + /** + * Constructor + * + * @param msgId + */ + public BlogIntegrationRuntimeException(String msgId) + { + super(msgId); + } + + /** + * Constructor + * + * @param msgId + * @param msgParams + */ + public BlogIntegrationRuntimeException(String msgId, Object[] msgParams) + { + super(msgId, msgParams); + } + + /** + * Constructor + * + * @param msgId + * @param cause + */ + public BlogIntegrationRuntimeException(String msgId, Throwable cause) + { + super(msgId, cause); + } + + /** + * Constructor + * + * @param msgId + * @param msgParams + * @param cause + */ + public BlogIntegrationRuntimeException(String msgId, Object[] msgParams, Throwable cause) + { + super(msgId, msgParams, cause); + } + +} diff --git a/source/java/org/alfresco/repo/blogIntegration/BlogIntegrationService.java b/source/java/org/alfresco/repo/blogIntegration/BlogIntegrationService.java new file mode 100644 index 0000000000..dc6975ec89 --- /dev/null +++ b/source/java/org/alfresco/repo/blogIntegration/BlogIntegrationService.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing + */ +package org.alfresco.repo.blogIntegration; + +import java.util.List; + +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; + +/** + * Blog integration service. + * + * @author Roy Wetherall + * + */ +public interface BlogIntegrationService +{ + /** + * Register a new blog integration implementation with the service + * + * @param implementation the implementation + */ + void register(BlogIntegrationImplementation implementation); + + /** + * Get the named blog integration implementation, null if name not recognised + * + * @param implementationName the implementation name + * @return BlogIntegrationImplementation the blog integration implementation + */ + BlogIntegrationImplementation getBlogIntegrationImplementation(String implementationName); + + /** + * Get a list of the registered integration implementations. + * + * @return List list of registered blog integration implementations + */ + List getBlogIntegrationImplementations(); + + /** + * Given a node reference, gets a list of 'in scope' BlogDetails. + * + * The node itself and then the primary parent hierarchy is searched and any blog details found returned in + * a list, with the 'nearest' first. + * + * @param nodeRef the node reference + * @return List list of the blog details found 'in scope' for the node, empty if none found + */ + List getBlogDetails(NodeRef nodeRef); + + /** + * Posts the content of a node to the blog specified + * + * @param blogDetails + * @param nodeRef + * @param contentProperty + * @param publish + */ + void newPost(BlogDetails blogDetails, NodeRef nodeRef, QName contentProperty, boolean publish); + + /** + * + * @param postId + * @param nodeRef + * @param contentProperty + * @param publish + */ + void updatePost(NodeRef nodeRef, QName contentProperty, boolean publish); + + /** + * + * @param postId + * @param nodeRef + */ + void deletePost(NodeRef nodeRef); +} diff --git a/source/java/org/alfresco/repo/blogIntegration/BlogIntegrationServiceImpl.java b/source/java/org/alfresco/repo/blogIntegration/BlogIntegrationServiceImpl.java new file mode 100644 index 0000000000..0986ddce58 --- /dev/null +++ b/source/java/org/alfresco/repo/blogIntegration/BlogIntegrationServiceImpl.java @@ -0,0 +1,375 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing + */ +package org.alfresco.repo.blogIntegration; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.model.BlogIntegrationModel; +import org.alfresco.model.ContentModel; +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.service.cmr.repository.AssociationRef; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.QName; + +/** + * Blog integration service implementation + * + * @author Roy Wetherall + */ +public class BlogIntegrationServiceImpl implements BlogIntegrationService, BlogIntegrationModel +{ + /** Node service */ + private NodeService nodeService; + + /** Content service */ + private ContentService contentService; + + /** Registered blog integration implemenatations */ + private Map implementations = new HashMap(5); + + /** Supported mimetypes */ + public static List supportedMimetypes = new ArrayList(5); + + /** Static initialisation of supported mimetypes */ + static + { + supportedMimetypes.add(MimetypeMap.MIMETYPE_TEXT_PLAIN); + supportedMimetypes.add(MimetypeMap.MIMETYPE_HTML); + } + + /** + * Set the node service + * + * @param nodeService the node service + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * Set the content service + * + * @param contentService the content service + */ + public void setContentService(ContentService contentService) + { + this.contentService = contentService; + } + + /** + * @see org.alfresco.module.blogIntegration.BlogIntegrationService#register(org.alfresco.module.blogIntegration.BlogIntegrationImplementation) + */ + public void register(BlogIntegrationImplementation implementation) + { + if (this.implementations.containsKey(implementation.getName()) == true) + { + throw new BlogIntegrationRuntimeException("A blog implementation with name '" + implementation.getName() + "' has already been registered."); + } + this.implementations.put(implementation.getName(), implementation); + } + + /** + * @see org.alfresco.module.blogIntegration.BlogIntegrationService#getBlogIntegrationImplementation(java.lang.String) + */ + public BlogIntegrationImplementation getBlogIntegrationImplementation(String implementationName) + { + return this.implementations.get(implementationName); + } + + /** + * @see org.alfresco.module.blogIntegration.BlogIntegrationService#getBlogIntegrationImplementations() + */ + public List getBlogIntegrationImplementations() + { + return new ArrayList(this.implementations.values()); + } + + /** + * @see org.alfresco.module.blogIntegration.BlogIntegrationService#getBlogDetails(org.alfresco.service.cmr.repository.NodeRef) + */ + public List getBlogDetails(NodeRef nodeRef) + { + List result = new ArrayList(5); + + // First check the node itself + if (this.nodeService.hasAspect(nodeRef, ASPECT_BLOG_DETAILS) == true) + { + result.add(BlogDetails.createBlogDetails(this.nodeService, nodeRef)); + } + + // Now walk up the parent hiearchy adding details as they are found + getBlogDetailsImpl(nodeRef, result); + return result; + } + + /** + * Helper method that recurses up the primary parent hierarchy checking for + * blog details + * + * @param nodeRef the node reference + * @param blogDetails list of blog details + */ + private void getBlogDetailsImpl(NodeRef nodeRef, List blogDetails) + { + // Check the parent assoc + ChildAssociationRef parentAssoc = this.nodeService.getPrimaryParent(nodeRef); + if (parentAssoc != null) + { + // Check for the blog details + NodeRef parent = parentAssoc.getParentRef(); + if (parent != null) + { + if (this.nodeService.hasAspect(parent, ASPECT_BLOG_DETAILS) == true) + { + blogDetails.add(BlogDetails.createBlogDetails(this.nodeService, parent)); + } + + // Recurse + getBlogDetailsImpl(parent, blogDetails); + } + } + } + + /** + * @see org.alfresco.module.blogIntegration.BlogIntegrationService#newPost(org.alfresco.module.blogIntegration.BlogDetails, org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName, boolean) + */ + public void newPost(BlogDetails blogDetails, NodeRef nodeRef, QName contentProperty, boolean publish) + { + // Get the blog implementation + BlogIntegrationImplementation implementation = getImplementation(blogDetails.getImplementationName()); + + // Check that this node has not already been posted to a blog + if (this.nodeService.hasAspect(nodeRef, ASPECT_BLOG_POST) == true) + { + throw new BlogIntegrationRuntimeException("Can not create new blog post since this conten has already been posted to a blog."); + } + + // Get the posts body + ContentReader contentReader = this.contentService.getReader(nodeRef, contentProperty); + if (contentReader == null) + { + throw new BlogIntegrationRuntimeException("No content found for new blog entry."); + } + + // Check the mimetype + String body = null; + if (supportedMimetypes.contains(contentReader.getMimetype()) == true) + { + // Get the content + body = contentReader.getContentString(); + } + else + { + throw new BlogIntegrationRuntimeException("The content mimetype '" + contentReader.getMimetype() + "' is not supported."); + } + + // Get the posts title + String title = (String)this.nodeService.getProperty(nodeRef, ContentModel.PROP_TITLE); + if (title == null || title.length() == 0) + { + if (body.length() > 23) + { + // Get the title from the first 22 character plus ' ...' + title = body.substring(0, 23) + " ..."; + } + else + { + title = body; + } + } + + // Post the new blog entry + String postId = implementation.newPost(blogDetails, title, body, true); + + // Get the blog details node if the is one + NodeRef blogDetailsNodeRef = blogDetails.getNodeRef(); + if (blogDetailsNodeRef != null) + { + // Now get the details of the newly created post + Map details = implementation.getPost(blogDetails, postId); + String link = (String)details.get("link"); + + // Add the details of the new post to the node + Map props = new HashMap(5); + props.put(PROP_POST_ID, postId); + if (link != null) + { + props.put(PROP_LINK, link); + } + Date now = new Date(); + props.put(PROP_POSTED, now); + props.put(PROP_LAST_UPDATE, now); + props.put(PROP_PUBLISHED, Boolean.valueOf(publish)); + this.nodeService.addAspect(nodeRef, ASPECT_BLOG_POST, props); + + // Associate to the blog details + this.nodeService.createAssociation(nodeRef, blogDetailsNodeRef, ASSOC_BLOG_DETAILS); + } + } + + /** + * Gets the blog implementation based on its name + * + * @param implementationName the implementation name + * @return BlogIntegrationImplementation the blog integration + */ + private BlogIntegrationImplementation getImplementation(String implementationName) + { + if (this.implementations.containsKey(implementationName) == false) + { + throw new BlogIntegrationRuntimeException("There is no blog implementation present for '" + implementationName + "'"); + } + return this.implementations.get(implementationName); + } + + /** + * @see org.alfresco.module.blogIntegration.BlogIntegrationService#updatePost(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName, boolean) + */ + public void updatePost(NodeRef nodeRef, QName contentProperty, boolean publish) + { + // Get the blog details and post id + BlogDetails blogDetails = null; + String postId = null; + if (this.nodeService.hasAspect(nodeRef, ASPECT_BLOG_POST) == true) + { + List assocs = this.nodeService.getTargetAssocs(nodeRef, ASSOC_BLOG_DETAILS); + if (assocs.size() == 0) + { + throw new BlogIntegrationRuntimeException("Can not resolve blog details for update because blogDetails association is not populated."); + } + else + { + blogDetails = BlogDetails.createBlogDetails(this.nodeService, assocs.get(0).getTargetRef()); + postId = (String)this.nodeService.getProperty(nodeRef, PROP_POST_ID); + } + } + else + { + throw new BlogIntegrationRuntimeException("Can not update blog post as this node has not been previously posted to a blog."); + } + + // Get the blog implementation + BlogIntegrationImplementation implementation = getImplementation(blogDetails.getImplementationName()); + + // Get the posts title + String title = (String)this.nodeService.getProperty(nodeRef, ContentModel.PROP_TITLE); + if (title == null || title.length() == 0) + { + throw new BlogIntegrationRuntimeException("No title available for update blog post. Set the title property and re-try."); + } + + // Get the posts body + ContentReader contentReader = this.contentService.getReader(nodeRef, contentProperty); + if (contentReader == null) + { + throw new BlogIntegrationRuntimeException("No content found for update blog entry."); + } + + // Check the mimetype + String body = null; + if (supportedMimetypes.contains(contentReader.getMimetype()) == true) + { + // Get the content + body = contentReader.getContentString(); + } + else + { + throw new BlogIntegrationRuntimeException("The content mimetype '" + contentReader.getMimetype() + "' is not supported."); + } + + // Update the blog post + boolean result = implementation.updatePost(blogDetails, postId, title, body, publish); + + // Check the return result + if (result == false) + { + throw new BlogIntegrationRuntimeException("The update of the post unexpectedly failed. Check your blog for more information."); + } + + // Now get the details of the newly created post + Map details = implementation.getPost(blogDetails, postId); + String link = (String)details.get("link"); + + // Update the post details accordingly + Map props = this.nodeService.getProperties(nodeRef); + Date now = new Date(); + props.put(PROP_LAST_UPDATE, now); + props.put(PROP_PUBLISHED, Boolean.valueOf(publish)); + props.put(PROP_LINK, link); + this.nodeService.setProperties(nodeRef, props); + } + + /** + * @see org.alfresco.module.blogIntegration.BlogIntegrationService#deletePost(org.alfresco.service.cmr.repository.NodeRef) + */ + public void deletePost(NodeRef nodeRef) + { + // Get the blog details and post id + BlogDetails blogDetails = null; + String postId = null; + if (this.nodeService.hasAspect(nodeRef, ASPECT_BLOG_POST) == true) + { + List assocs = this.nodeService.getTargetAssocs(nodeRef, ASSOC_BLOG_DETAILS); + if (assocs.size() == 0) + { + throw new BlogIntegrationRuntimeException("Can not resolve blog details for delete because blogDetails association is not populated."); + } + else + { + blogDetails = BlogDetails.createBlogDetails(this.nodeService, assocs.get(0).getTargetRef()); + postId = (String)this.nodeService.getProperty(nodeRef, PROP_POST_ID); + } + } + else + { + throw new BlogIntegrationRuntimeException("Can not delete blog post as this node has not been previously posted to a blog."); + } + + // Get the blog implementation + BlogIntegrationImplementation implementation = getImplementation(blogDetails.getImplementationName()); + + // Delete the post + boolean result = implementation.deletePost(blogDetails, postId); + + // Check the return result + if (result == false) + { + throw new BlogIntegrationRuntimeException("Deleting the post unexpectedly failed. Check your blog for more information."); + } + + // Remove the postDetails aspect from the node + this.nodeService.removeAspect(nodeRef, ASPECT_BLOG_POST); + } +} diff --git a/source/java/org/alfresco/repo/blogIntegration/BlogIntegrationServiceSystemTest.java b/source/java/org/alfresco/repo/blogIntegration/BlogIntegrationServiceSystemTest.java new file mode 100644 index 0000000000..f6397d1f3e --- /dev/null +++ b/source/java/org/alfresco/repo/blogIntegration/BlogIntegrationServiceSystemTest.java @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing + */ +package org.alfresco.repo.blogIntegration; + +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.model.BlogIntegrationModel; +import org.alfresco.model.ContentModel; +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.service.cmr.repository.AssociationRef; +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.repository.StoreRef; +import org.alfresco.service.cmr.search.ResultSet; +import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.BaseAlfrescoSpringTest; + +/** + * @author Roy Wetherall + */ +public class BlogIntegrationServiceSystemTest extends BaseAlfrescoSpringTest implements BlogIntegrationModel +{ + /** + * Wordpress details + * + * http://paulhh.wordpress.com/xmlrpc.php + * paulhh + * 114eb1 + */ + + /** + * Typepad details + * + * http://www.typepad.com/t/api + * http://rwetherall.typepad.com/my_test_blog + * 1340792 + * rwetherall + * + */ + + /** Blog Details **/ + private static final String BLOG = "wordpress"; + private static final String BLOG_URL = "http://paulhh.wordpress.com"; + private static final String BLOG_USER = "paulhh"; + private static final String BLOG_PWD = "114eb1"; + private static final String BLOG_ID = "0"; + + + private static final String BLOG_NAME = "Test blog details"; + private static final String BLOG_DESCRIPTION = "These are the details used to test the blog integration service"; + + /** Blog entry */ + private static final String TITLE = "My Test Post @ " + new Date().toString(); + //private static final String TITLE = ""; + private static final String MODIFIED_TITLE = "My Test Post Modified @ " + new Date().toString(); + private static final String DESCRIPTION = "This is a description of my test post."; + private static final String POST_CONTENT = "Hello and welcome to my test post. This has been posted from the blog integration system test @ " + new Date().toString(); + private static final String MODIFIED_POST_CONTENT = "Hello and welcome to my MODIFIED test post. This has been posted and MODIFIED from the blog integration system test @ " + new Date().toString(); + private static final boolean PUBLISH = true; + + private NodeService nodeService; + private ContentService contentService; + private SearchService searchService; + private BlogIntegrationService blogService; + + private NodeRef nodeRef; + private NodeRef blogDetailsNodeRef; + + @Override + protected void onSetUpInTransaction() + throws Exception + { + super.onSetUpInTransaction(); + + // Get references to the relevant services + this.nodeService = (NodeService)this.applicationContext.getBean("nodeService"); + this.contentService = (ContentService)this.applicationContext.getBean("contentService"); + this.searchService = (SearchService)this.applicationContext.getBean("searchService"); + this.blogService = (BlogIntegrationService)this.applicationContext.getBean("blogIntegrationService"); + + // Get a reference to the company home node + ResultSet results1 = this.searchService.query( + new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore"), + SearchService.LANGUAGE_XPATH, + "app:company_home"); + NodeRef companyHome = results1.getNodeRefs().get(0); + + // Create the blog details node + this.blogDetailsNodeRef = this.nodeService.createNode( + companyHome, + ContentModel.ASSOC_CHILDREN, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "testFolder"), + ContentModel.TYPE_FOLDER).getChildRef(); + this.nodeService.setProperty(this.blogDetailsNodeRef, ContentModel.PROP_NAME, "testFolder"); + Map props2 = new HashMap(5); + props2.put(PROP_BLOG_IMPLEMENTATION, BLOG); + props2.put(PROP_ID, BLOG_ID); + props2.put(PROP_NAME, BLOG_NAME); + props2.put(PROP_DESCRIPTION, BLOG_DESCRIPTION); + props2.put(PROP_URL, BLOG_URL); + props2.put(PROP_USER_NAME, BLOG_USER); + props2.put(PROP_PASSWORD, BLOG_PWD); + this.nodeService.addAspect(this.blogDetailsNodeRef, ASPECT_BLOG_DETAILS, props2); + + // Create the content node + Map props = new HashMap(1); + props.put(ContentModel.PROP_NAME, "myBlogEntry.txt"); + this.nodeRef = this.nodeService.createNode( + this.blogDetailsNodeRef, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "myBlogEntry.txt"), + ContentModel.TYPE_CONTENT, + props).getChildRef(); + + // Add the titled aspect + Map titledProps = new HashMap(2); + titledProps.put(ContentModel.PROP_TITLE, TITLE); + titledProps.put(ContentModel.PROP_DESCRIPTION, DESCRIPTION); + this.nodeService.addAspect(this.nodeRef, ContentModel.ASPECT_TITLED, titledProps); + + // Add some content + ContentWriter contentWriter = this.contentService.getWriter(this.nodeRef, ContentModel.PROP_CONTENT, true); + contentWriter.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + contentWriter.setEncoding("UTF-8"); + contentWriter.putContent(POST_CONTENT); + } + + public void testGetBlogIntegrationImplementations() + { + List list = this.blogService.getBlogIntegrationImplementations(); + assertNotNull(list); + assertEquals(2, list.size()); + + BlogIntegrationImplementation blog = this.blogService.getBlogIntegrationImplementation(BLOG); + assertNotNull(blog); + assertEquals(BLOG, blog.getName()); + } + + public void testGetBlogDetails() + { + List details = this.blogService.getBlogDetails(this.nodeRef); + assertNotNull(details); + assertEquals(1, details.size()); + assertEquals(BLOG_URL, details.get(0).getUrl()); + + List details2 = this.blogService.getBlogDetails(this.blogDetailsNodeRef); + assertNotNull(details2); + assertEquals(1, details2.size()); + assertEquals(BLOG_URL, details2.get(0).getUrl()); + } + + public void testNewPost() + { + // Create the blog details + BlogDetails blogDetails = BlogDetails.createBlogDetails(this.nodeService, this.blogDetailsNodeRef); + + // Do a quick check on the blog details + assertEquals(this.blogDetailsNodeRef, blogDetails.getNodeRef()); + assertEquals(BLOG, blogDetails.getImplementationName()); + assertEquals(BLOG_ID, blogDetails.getBlogId()); + assertEquals(BLOG_NAME, blogDetails.getName()); + assertEquals(BLOG_DESCRIPTION, blogDetails.getDescription()); + assertEquals(BLOG_URL, blogDetails.getUrl()); + assertEquals(BLOG_USER, blogDetails.getUserName()); + assertEquals(BLOG_PWD, blogDetails.getPassword()); + + // Post and publish the content contained on the node + this.blogService.newPost(blogDetails, this.nodeRef, ContentModel.PROP_CONTENT, PUBLISH); + + // Check the details of the node after the post + assertTrue(this.nodeService.hasAspect(this.nodeRef, ASPECT_BLOG_POST)); + assertNotNull(this.nodeService.getProperty(this.nodeRef, PROP_POST_ID)); + System.out.println("The newly create post has id " + this.nodeService.getProperty(this.nodeRef, PROP_POST_ID)); + List assocs = this.nodeService.getTargetAssocs(this.nodeRef, ASSOC_BLOG_DETAILS); + assertEquals(1, assocs.size()); + NodeRef testRef = assocs.get(0).getTargetRef(); + assertEquals(blogDetailsNodeRef, testRef); + + // TODO check the other stuff + + + // Check that im not allowed to create another new post with the same node + try + { + this.blogService.newPost(blogDetails, this.nodeRef, ContentModel.PROP_CONTENT, PUBLISH); + } + catch (BlogIntegrationRuntimeException e) + { + // Expected + } + + // Edit the title and content + this.nodeService.setProperty(this.nodeRef, ContentModel.PROP_TITLE, MODIFIED_TITLE); + } + + public void testUpdatePost() + { + // Create the blog details + BlogDetails blogDetails = BlogDetails.createBlogDetails(this.nodeService, this.blogDetailsNodeRef); + + // Post and publish the content contained on the node + this.blogService.newPost(blogDetails, this.nodeRef, ContentModel.PROP_CONTENT, PUBLISH); + + // Edit the title and content of the node + this.nodeService.setProperty(this.nodeRef, ContentModel.PROP_TITLE, MODIFIED_TITLE); + ContentWriter contentWriter = this.contentService.getWriter(this.nodeRef, ContentModel.PROP_CONTENT, true); + contentWriter.putContent(MODIFIED_POST_CONTENT); + + // Update the post + this.blogService.updatePost(this.nodeRef, ContentModel.PROP_CONTENT, PUBLISH); + + // Check the updated meta-data .... TODO + } + + public void testDeletePost() + { + // Create the blog details + BlogDetails blogDetails = BlogDetails.createBlogDetails(this.nodeService, this.blogDetailsNodeRef); + + // Post and publish the content contained on the node + this.blogService.newPost(blogDetails, this.nodeRef, ContentModel.PROP_CONTENT, PUBLISH); + + // Delete the post + this.blogService.deletePost(this.nodeRef); + + // Check the aspect has bee removed from the node + assertFalse(this.nodeService.hasAspect(this.nodeRef, ASPECT_BLOG_POST)); + } +} diff --git a/source/java/org/alfresco/repo/blogIntegration/DefaultBlogIntegrationImplementation.java b/source/java/org/alfresco/repo/blogIntegration/DefaultBlogIntegrationImplementation.java new file mode 100644 index 0000000000..b8fc40eafe --- /dev/null +++ b/source/java/org/alfresco/repo/blogIntegration/DefaultBlogIntegrationImplementation.java @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing + */ +package org.alfresco.repo.blogIntegration; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; + +import marquee.xmlrpc.XmlRpcClient; +import marquee.xmlrpc.XmlRpcException; +import marquee.xmlrpc.XmlRpcParser; +import marquee.xmlrpc.XmlRpcSerializer; +import marquee.xmlrpc.serializers.HashtableSerializer; + +/** + * Default blog integration implementation. Uses various standard XML PRC blogging API to satisfy the + * blog integration implementation interface. + * + * Based on origional contribution by Sudhakar Selvaraj. + * + * @author Roy Wetherall + */ +public abstract class DefaultBlogIntegrationImplementation extends BaseBlogIntegrationImplementation +{ + /** Blog actions */ + protected static final String ACTION_NEW_POST = "metaWeblog.newPost"; + protected static final String ACTION_EDIT_POST = "metaWeblog.editPost"; + protected static final String ACTION_GET_POST = "metaWeblog.getPost"; + protected static final String ACTION_DELETE_POST = "blogger.deletePost"; + + /** + * Gets the XML RPC end point URL for the given blog details. + * + * @param blogDetails blog details + * @return String the end point URL + */ + protected abstract String getEndpointURL(BlogDetails blogDetails); + + /** + * @see org.alfresco.module.blogIntegration.BlogIntegrationImplementation#newPost(org.alfresco.module.blogIntegration.BlogDetails, java.lang.String, java.lang.String, boolean) + */ + public String newPost(BlogDetails blogDetails, String title, String body, boolean publish) + { + // Create the hash table containing details of the post's content + Hashtable content = new Hashtable(); + content.put("title", title); + content.put("description", body); + + // Create a list of parameters + List params = new ArrayList(5); + params.add(blogDetails.getBlogId()); + params.add(blogDetails.getUserName()); + params.add(blogDetails.getPassword()); + params.add(content); + params.add(publish); + + // Create the new post + return (String)execute(getEndpointURL(blogDetails), ACTION_NEW_POST, params); + } + + /** + * @see org.alfresco.module.blogIntegration.BlogIntegrationImplementation#updatePost(org.alfresco.module.blogIntegration.BlogDetails, java.lang.String, java.lang.String, java.lang.String, boolean) + */ + public boolean updatePost(BlogDetails blogDetails, String postId, String title, String body, boolean publish) + { + // Create the hash table containing details of the post's content + Hashtable content = new Hashtable(); + content.put("title", title); + content.put("description", body); + + // Create a list of parameters + List params = new ArrayList(5); + params.add(postId); + params.add(blogDetails.getUserName()); + params.add(blogDetails.getPassword()); + params.add(content); + params.add(publish); + + // Create the new post + Object result = execute(getEndpointURL(blogDetails), ACTION_EDIT_POST, params); + + if (result.getClass().equals(Boolean.class)) + { + return ((Boolean)result).booleanValue(); + } + return false; + + } + + /** + * @see org.alfresco.module.blogIntegration.BlogIntegrationImplementation#getPost(org.alfresco.module.blogIntegration.BlogDetails, java.lang.String) + */ + @SuppressWarnings("unchecked") + public Map getPost(BlogDetails blogDetails, String postId) + { + // Create a list of parameters + List params = new ArrayList(3); + params.add(postId); + params.add(blogDetails.getUserName()); + params.add(blogDetails.getPassword()); + + // Get the post details + return (Map)execute(getEndpointURL(blogDetails), ACTION_GET_POST, params); + } + + /** + * @see org.alfresco.module.blogIntegration.BlogIntegrationImplementation#deletePost(org.alfresco.module.blogIntegration.BlogDetails, java.lang.String) + */ + public boolean deletePost(BlogDetails blogDetails, String postId) + { + // Create a list of parameters + List params = new ArrayList(5); + // Use the blog id for the app key + params.add(blogDetails.getBlogId()); + params.add(postId); + params.add(blogDetails.getUserName()); + params.add(blogDetails.getPassword()); + params.add(true); + + // Delete post + Object result = execute(getEndpointURL(blogDetails), ACTION_DELETE_POST, params); + if (result.getClass().equals(Boolean.class)) + { + return ((Boolean)result).booleanValue(); + } + return false; + } + + /** + * Helper method to get the XML RPC client + * + * @param url + * @return + */ + private XmlRpcClient getClient(String url) + { + XmlRpcClient client = null; + try + { + XmlRpcSerializer.registerCustomSerializer(new HashtableSerializer()); + XmlRpcParser.setDriver("org.apache.xerces.parsers.SAXParser"); + client = new XmlRpcClient(new URL(url)); + } + catch (MalformedURLException exception) + { + throw new BlogIntegrationRuntimeException("Blog url '" + url + "' is invalid.", exception); + } + + return client; + + } + + /** + * Executes an XML RPC method + * + * @param url + * @param method + * @param params + * @return + */ + protected Object execute(String url, String method, List params) + { + Object result = null; + + try + { + XmlRpcClient client = getClient(url); + result = client.invoke(method, params); + } + catch (XmlRpcException exception) + { + throw new BlogIntegrationRuntimeException("Failed to execute blog action '" + method + "' @ url '" + url + "'", exception); + } + + return result; + } + + /** + * Checks a url for a protocol and adds http if none present + * + * @param url the url + * @return String the checked url + */ + protected String checkForProtocol(String url) + { + if (url.indexOf("://") == -1) + { + url = "http://" + url; + } + return url; + } + + /** + * Checks the url for a trailing slash and adds one if none present + * + * @param url the url + * @return String the checked url + */ + protected String checkForTrainlingSlash(String url) + { + if (url.endsWith("/") == false) + { + url = url + "/"; + } + return url; + } +} diff --git a/source/java/org/alfresco/repo/blogIntegration/typepad/TypepadIntegration.java b/source/java/org/alfresco/repo/blogIntegration/typepad/TypepadIntegration.java new file mode 100644 index 0000000000..cb817153f0 --- /dev/null +++ b/source/java/org/alfresco/repo/blogIntegration/typepad/TypepadIntegration.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing + */ +package org.alfresco.repo.blogIntegration.typepad; + +import org.alfresco.repo.blogIntegration.BlogDetails; +import org.alfresco.repo.blogIntegration.DefaultBlogIntegrationImplementation; + +/** + * Typepad integration implementation + * + * @author Roy Wetherall + */ +public class TypepadIntegration extends DefaultBlogIntegrationImplementation +{ + /** + * @see org.alfresco.module.blogIntegration.DefaultBlogIntegrationImplementation#getEndpointURL(org.alfresco.module.blogIntegration.BlogDetails) + */ + @Override + protected String getEndpointURL(BlogDetails blogDetails) + { + return "http://www.typepad.com/t/api"; + } + + /** + * For some reason typepad returns a hash table rather than the expected boolean result. + * + * @see org.alfresco.module.blogIntegration.BlogIntegrationImplementation#deletePost(org.alfresco.module.blogIntegration.BlogDetails, java.lang.String) + */ + @Override + public boolean deletePost(BlogDetails blogDetails, String postId) + { + // NOTE: At the time of testing typepad.com failed when making this call, for now the implementation is + // being overriden to return success + + return true; + } + +} diff --git a/source/java/org/alfresco/repo/blogIntegration/wordpress/WordPressIntegration.java b/source/java/org/alfresco/repo/blogIntegration/wordpress/WordPressIntegration.java new file mode 100644 index 0000000000..2f570d5f23 --- /dev/null +++ b/source/java/org/alfresco/repo/blogIntegration/wordpress/WordPressIntegration.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing + */ +package org.alfresco.repo.blogIntegration.wordpress; + +import org.alfresco.repo.blogIntegration.BlogDetails; +import org.alfresco.repo.blogIntegration.DefaultBlogIntegrationImplementation; + +/** + * @author Roy Wetherall + */ +public class WordPressIntegration extends DefaultBlogIntegrationImplementation +{ + private static String ENDPOINT = "xmlrpc.php"; + + /** + * @see org.alfresco.module.blogIntegration.DefaultBlogIntegrationImplementation#getEndpointURL(org.alfresco.module.blogIntegration.BlogDetails) + */ + @Override + protected String getEndpointURL(BlogDetails blogDetails) + { + String endpoint = checkForProtocol(blogDetails.getUrl()); + return checkForTrainlingSlash(endpoint) + ENDPOINT; + } + +}