/**
* Copyright (c) 2003-2005, David A. Czarnecki
* All rights reserved.
*
* Portions Copyright (c) 2003-2005 by Mark Lussier
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of the "David A. Czarnecki" and "blojsom" nor the names of
* its contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Products derived from this software may not be called "blojsom",
* nor may "blojsom" appear in their name, without prior written permission of
* David A. Czarnecki.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.blojsom.extension.xmlrpc.handlers;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.xmlrpc.XmlRpcException;
import org.blojsom.BlojsomException;
import org.blojsom.blog.BlogCategory;
import org.blojsom.blog.BlogEntry;
import org.blojsom.blog.BlogUser;
import org.blojsom.blog.Trackback;
import org.blojsom.fetcher.BlojsomFetcher;
import org.blojsom.plugin.trackback.TrackbackPlugin;
import org.blojsom.util.BlojsomMetaDataConstants;
import org.blojsom.util.BlojsomUtils;
import java.io.File;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.Vector;
/**
* MovableType API handler
*
* @author David Czarnecki
* @version $Id: MovableTypeAPIHandler.java,v 1.1.2.1 2005/07/21 04:30:23 johnan Exp $
* @since blojsom 2.20
*/
public class MovableTypeAPIHandler extends AbstractBlojsomAPIHandler {
protected static final String MEMBER_DATECREATED = "dateCreated";
protected static final String MEMBER_USERID = "userid";
protected static final String MEMBER_POSTID = "postid";
protected static final String MEMBER_TITLE = "title";
protected static final String MEMBER_CATEGORYID = "categoryId";
protected static final String MEMBER_CATEGORYNAME = "categoryName";
protected static final String MEMBER_ISPRIMARY = "isPrimary";
protected static final String MEMBER_KEY = "key";
protected static final String MEMBER_LABEL = "label";
protected static final String MEMBER_PING_TITLE = "pingTitle";
protected static final String MEMBER_PING_URL = "pingURL";
protected static final String MEMBER_PING_IP = "pingIP";
private static final String MOVABLETYPE_API_PERMISSION = "post_via_movabletype_api";
protected static final String API_PREFIX = "mt";
protected Log _logger = LogFactory.getLog(MovableTypeAPIHandler.class);
/**
* Construct a new MovableType API handler
*/
public MovableTypeAPIHandler() {
}
/**
* Attach a blog instance to the API Handler so that it can interact with the blog
*
* @param blogUser an instance of BlogUser
* @throws org.blojsom.BlojsomException If there is an error setting the blog user instance or properties for the handler
* @see org.blojsom.blog.BlogUser
*/
public void setBlogUser(BlogUser blogUser) throws BlojsomException {
_blogUser = blogUser;
_blog = _blogUser.getBlog();
_blogEntryExtension = _blog.getBlogProperty(BLOG_XMLRPC_ENTRY_EXTENSION_IP);
if (BlojsomUtils.checkNullOrBlank(_blogEntryExtension)) {
_blogEntryExtension = DEFAULT_BLOG_XMLRPC_ENTRY_EXTENSION;
}
}
/**
* Gets the name of API Handler. Used to bind to XML-RPC
*
* @return The API Name (ie: blogger)
*/
public String getName() {
return API_PREFIX;
}
/**
* Returns a bandwidth-friendly list of the most recent posts in the system.
*
* @param blogID Blog ID
* @param username Username
* @param password Password
* @param numberOfPosts Number of titles to retrieve
* @return Bandwidth-friendly list of the most recent posts in the system
* @throws Exception If there is an error retrieving post titles
*/
public Object getRecentPostTitles(String blogID, String username, String password, int numberOfPosts) throws Exception {
_logger.debug("getRecentPostTitles() Called ===========[ SUPPORTED ]=====");
_logger.debug(" BlogId: " + blogID);
_logger.debug(" UserId: " + username);
_logger.debug(" Password: *********");
_logger.debug(" Numposts: " + numberOfPosts);
Vector recentPosts = new Vector();
blogID = BlojsomUtils.normalize(blogID);
try {
_authorizationProvider.loadAuthenticationCredentials(_blogUser);
_authorizationProvider.authorize(_blogUser, null, username, password);
checkXMLRPCPermission(username, MOVABLETYPE_API_PERMISSION);
// Quick verify that the category is valid
File blogCategoryFile = new File(_blog.getBlogHome() + BlojsomUtils.removeInitialSlash(blogID));
if (blogCategoryFile.exists() && blogCategoryFile.isDirectory()) {
String requestedCategory = BlojsomUtils.removeInitialSlash(blogID);
BlogCategory blogCategory = _fetcher.newBlogCategory();
blogCategory.setCategory(blogID);
blogCategory.setCategoryURL(_blog.getBlogURL() + requestedCategory);
BlogEntry[] entries;
Map fetchMap = new HashMap();
if (BlojsomUtils.checkNullOrBlank(requestedCategory)) {
fetchMap.put(BlojsomFetcher.FETCHER_FLAVOR, DEFAULT_FLAVOR_HTML);
fetchMap.put(BlojsomFetcher.FETCHER_NUM_POSTS_INTEGER, new Integer(numberOfPosts));
entries = _fetcher.fetchEntries(fetchMap, _blogUser);
} else {
fetchMap.put(BlojsomFetcher.FETCHER_CATEGORY, blogCategory);
fetchMap.put(BlojsomFetcher.FETCHER_NUM_POSTS_INTEGER, new Integer(numberOfPosts));
entries = _fetcher.fetchEntries(fetchMap, _blogUser);
}
if (entries != null && entries.length > 0) {
for (int x = 0; x < entries.length; x++) {
BlogEntry entry = entries[x];
Hashtable entrystruct = new Hashtable();
entrystruct.put(MEMBER_DATECREATED, entry.getDate());
if (BlojsomUtils.checkMapForKey(entry.getMetaData(), BlojsomMetaDataConstants.BLOG_ENTRY_METADATA_AUTHOR)) {
entrystruct.put(MEMBER_USERID, (String) entry.getMetaData().get(BlojsomMetaDataConstants.BLOG_ENTRY_METADATA_AUTHOR));
} else {
entrystruct.put(MEMBER_USERID, _blog.getBlogOwner());
}
entrystruct.put(MEMBER_POSTID, entry.getId());
entrystruct.put(MEMBER_TITLE, entry.getTitle());
recentPosts.add(entrystruct);
}
}
}
return recentPosts;
} catch (BlojsomException e) {
_logger.error("Failed to authenticate user [" + username + "] with password [" + password + "]");
throw new XmlRpcException(AUTHORIZATION_EXCEPTION, AUTHORIZATION_EXCEPTION_MSG);
}
}
/**
* Returns a list of all categories defined in the weblog.
*
* @param blogID Blog ID
* @param username Username
* @param password Password
* @return List of all categories defined in the weblog
* @throws Exception If there is an error getting the category list
*/
public Object getCategoryList(String blogID, String username, String password) throws Exception {
_logger.debug("getCategories() Called =====[ SUPPORTED ]=====");
_logger.debug(" BlogId: " + blogID);
_logger.debug(" UserId: " + username);
_logger.debug(" Password: *********");
try {
_authorizationProvider.loadAuthenticationCredentials(_blogUser);
_authorizationProvider.authorize(_blogUser, null, username, password);
checkXMLRPCPermission(username, MOVABLETYPE_API_PERMISSION);
Vector result;
BlogCategory[] categories = _fetcher.fetchCategories(null, _blogUser);
if (categories != null) {
result = new Vector(categories.length);
for (int x = 0; x < categories.length; x++) {
Hashtable catlist = new Hashtable(3);
BlogCategory category = categories[x];
String categoryId = category.getCategory();
if (categoryId.length() > 1) {
categoryId = BlojsomUtils.removeInitialSlash(categoryId);
}
String description;
Map metadata = category.getMetaData();
if (metadata != null && metadata.containsKey(DESCRIPTION_KEY)) {
description = (String) metadata.get(DESCRIPTION_KEY);
} else {
description = category.getEncodedCategory();
}
catlist.put(MEMBER_CATEGORYID, categoryId);
catlist.put(MEMBER_CATEGORYNAME, description);
result.add(catlist);
}
} else {
throw new XmlRpcException(NOBLOGS_EXCEPTION, NOBLOGS_EXCEPTION_MSG);
}
return result;
} catch (BlojsomException e) {
_logger.error("Failed to authenticate user [" + username + "] with password [" + password + "]");
throw new XmlRpcException(AUTHORIZATION_EXCEPTION, AUTHORIZATION_EXCEPTION_MSG);
}
}
/**
* Returns a list of all categories to which the post is assigned. Since we only support
* single categories at the moment, just return a single structure.
*
* @param postID Post ID
* @param username Username
* @param password Password
* @return An array of structs containing String categoryName, String categoryId, and boolean isPrimary
*/
public Object getPostCategories(String postID, String username, String password) throws Exception {
_logger.debug("getPost() Called =========[ SUPPORTED ]=====");
_logger.debug(" PostId: " + postID);
_logger.debug(" UserId: " + username);
_logger.debug(" Password: *********");
Vector result = new Vector();
try {
_authorizationProvider.loadAuthenticationCredentials(_blogUser);
_authorizationProvider.authorize(_blogUser, null, username, password);
checkXMLRPCPermission(username, MOVABLETYPE_API_PERMISSION);
String category;
String permalink;
String match = "?" + PERMALINK_PARAM + "=";
int pos = postID.indexOf(match);
if (pos != -1) {
category = postID.substring(0, pos);
category = BlojsomUtils.normalize(category);
category = BlojsomUtils.urlDecode(category);
permalink = postID.substring(pos + match.length());
BlogCategory blogCategory = _fetcher.newBlogCategory();
blogCategory.setCategory(category);
blogCategory.setCategoryURL(_blog.getBlogURL() + category);
Map fetchMap = new HashMap();
fetchMap.put(BlojsomFetcher.FETCHER_CATEGORY, blogCategory);
fetchMap.put(BlojsomFetcher.FETCHER_PERMALINK, permalink);
BlogEntry[] entries = _fetcher.fetchEntries(fetchMap, _blogUser);
if (entries != null && entries.length > 0) {
BlogEntry entry = entries[0];
Hashtable categoryContent = new Hashtable(3);
String categoryId = entry.getBlogCategory().getCategory();
if (categoryId.length() > 1) {
categoryId = BlojsomUtils.removeInitialSlash(categoryId);
}
String description;
Map metadata = entry.getBlogCategory().getMetaData();
if (metadata != null && metadata.containsKey(DESCRIPTION_KEY)) {
description = (String) metadata.get(DESCRIPTION_KEY);
} else {
description = entry.getBlogCategory().getEncodedCategory();
}
categoryContent.put(MEMBER_CATEGORYID, categoryId);
categoryContent.put(MEMBER_CATEGORYNAME, description);
categoryContent.put(MEMBER_ISPRIMARY, Boolean.TRUE);
result.add(categoryContent);
return result;
} else {
throw new XmlRpcException(INVALID_POSTID, INVALID_POSTID_MSG);
}
} else {
throw new XmlRpcException(INVALID_POSTID, INVALID_POSTID_MSG);
}
} catch (BlojsomException e) {
_logger.error("Failed to authenticate user [" + username + "] with password [" + password + "]");
throw new XmlRpcException(AUTHORIZATION_EXCEPTION, AUTHORIZATION_EXCEPTION_MSG);
}
}
/**
* Sets the categories for a post.
*
* @param postID Post ID
* @param username Username
* @param password Password
* @param categories Array of structs containing String categoryId and boolean isPrimary
* @return true
if categories set for a post
* @throws Exception If there is an error setting the categories for a post
*/
public boolean setPostCategories(String postID, String username, String password, Vector categories) throws Exception {
throw new XmlRpcException(UNSUPPORTED_EXCEPTION, UNSUPPORTED_EXCEPTION_MSG);
}
/**
* Retrieve information about the XML-RPC methods supported by the server.
*
* @return Array of method names supported by the server
* @throws Exception If there is an error retrieving the list of supported XML-RPC methods.
*/
public Object supportedMethods() throws Exception {
Vector result = new Vector();
result.add("blogger.newPost");
result.add("blogger.editPost");
result.add("blogger.getPost");
result.add("blogger.deletePost");
result.add("blogger.getRecentPosts");
result.add("blogger.getUsersBlogs");
result.add("blogger.getUserInfo");
result.add("metaWeblog.getUsersBlogs");
result.add("metaWeblog.getCategories");
result.add("metaWeblog.newPost");
result.add("metaWeblog.editPost");
result.add("metaWeblog.getPost");
result.add("metaWeblog.deletePost");
result.add("metaWeblog.getRecentPosts");
result.add("metaWeblog.newMediaObject");
result.add("mt.getRecentPostTitles");
result.add("mt.getCategoryList");
result.add("mt.getPostCategories");
result.add("mt.supportedMethods");
result.add("mt.supportedTextFilters");
result.add("mt.getTrackbackPings");
return result;
}
/**
* Retrieve information about the text formatting plugins supported by the server.
*
* @return An array of structs containing String key and String label. key is the
* unique string identifying a text formatting plugin, and label is the readable
* description to be displayed to a user
* @throws Exception If there is an error retrieving the list of plugins
*/
public Object supportedTextFilters() throws Exception {
Vector result = new Vector();
// Return an empty list as we need to figure out a way to determine supported formatting plugins
return result;
}
/**
* Retrieve the list of TrackBack pings posted to a particular entry
*
* @param postID Post ID
* @return An array of structs containing String pingTitle (the title of the entry sent
* in the ping), String pingURL (the URL of the entry), and String pingIP (the IP address
* of the host that sent the ping)
* @throws Exception If there is an error retrieving trackbacks for an entry
*/
public Object getTrackbackPings(String postID) throws Exception {
_logger.debug("getTrackbackPings() Called =========[ SUPPORTED ]=====");
_logger.debug(" PostId: " + postID);
Vector trackbackPings = new Vector();
try {
String category;
String permalink;
String match = "?" + PERMALINK_PARAM + "=";
int pos = postID.indexOf(match);
if (pos != -1) {
category = postID.substring(0, pos);
category = BlojsomUtils.normalize(category);
category = BlojsomUtils.urlDecode(category);
permalink = postID.substring(pos + match.length());
BlogCategory blogCategory = _fetcher.newBlogCategory();
blogCategory.setCategory(category);
blogCategory.setCategoryURL(_blog.getBlogURL() + category);
Map fetchMap = new HashMap();
fetchMap.put(BlojsomFetcher.FETCHER_CATEGORY, blogCategory);
fetchMap.put(BlojsomFetcher.FETCHER_PERMALINK, permalink);
BlogEntry[] entries = _fetcher.fetchEntries(fetchMap, _blogUser);
if (entries != null && entries.length > 0) {
BlogEntry entry = entries[0];
Trackback[] trackbacks = entry.getTrackbacksAsArray();
for (int i = 0; i < trackbacks.length; i++) {
Hashtable trackbackInformation = new Hashtable(3);
trackbackInformation.put(MEMBER_PING_TITLE, trackbacks[i].getTitle());
trackbackInformation.put(MEMBER_PING_URL, trackbacks[i].getUrl());
if (BlojsomUtils.checkMapForKey(trackbacks[i].getMetaData(), TrackbackPlugin.BLOJSOM_TRACKBACK_PLUGIN_METADATA_IP)) {
trackbackInformation.put(MEMBER_PING_IP, trackbacks[i].getMetaData().get(TrackbackPlugin.BLOJSOM_TRACKBACK_PLUGIN_METADATA_IP));
} else {
trackbackInformation.put(MEMBER_PING_IP, "");
}
trackbackPings.add(trackbackInformation);
}
return trackbackPings;
} else {
throw new XmlRpcException(INVALID_POSTID, INVALID_POSTID_MSG);
}
} else {
throw new XmlRpcException(INVALID_POSTID, INVALID_POSTID_MSG);
}
} catch (BlojsomException e) {
_logger.error(UNKNOWN_EXCEPTION_MSG, e);
throw new XmlRpcException(UNKNOWN_EXCEPTION, UNKNOWN_EXCEPTION_MSG);
}
}
/**
* Publish (rebuild) all of the static files related to an entry from your weblog. Equivalent to saving an entry in the system (but without the ping)
*
* @param postID Post ID
* @param username Username
* @param password Password
* @return true
if post published
* @throws Exception If there is an error publishing the post
*/
public boolean publishPost(String postID, String username, String password) throws Exception {
throw new XmlRpcException(UNSUPPORTED_EXCEPTION, UNSUPPORTED_EXCEPTION_MSG);
}
}