Added: incubator/roller/trunk/src/org/roller/presentation/webservices/xmlrpc/BaseAPIHandler.java URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/webservices/xmlrpc/BaseAPIHandler.java?rev=395633&view=auto ============================================================================== --- incubator/roller/trunk/src/org/roller/presentation/webservices/xmlrpc/BaseAPIHandler.java (added) +++ incubator/roller/trunk/src/org/roller/presentation/webservices/xmlrpc/BaseAPIHandler.java Thu Apr 20 09:22:49 2006 @@ -0,0 +1,239 @@ +/* + * Created on Apr 11, 2003 + */ +package org.roller.presentation.webservices.xmlrpc; + +import java.io.Serializable; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.xmlrpc.XmlRpcException; +import org.roller.config.RollerConfig; +import org.roller.model.RollerFactory; +import org.roller.model.UserManager; +import org.roller.pojos.UserData; +import org.roller.pojos.WebsiteData; +import org.roller.presentation.RollerContext; +import org.roller.presentation.RollerRequest; +import org.roller.presentation.cache.CacheManager; +import org.roller.util.Utilities; + +/** + * Base API handler does user validation, provides exception types, etc. + * @author David M Johnson + */ +public class BaseAPIHandler implements Serializable +{ + static final long serialVersionUID = -698186274794937582L; + + private static Log mLogger = + LogFactory.getFactory().getInstance(BaseAPIHandler.class); + + public static final int AUTHORIZATION_EXCEPTION = 0001; + public static final String AUTHORIZATION_EXCEPTION_MSG = + "Invalid Username and/or Password"; + + public static final int UNKNOWN_EXCEPTION = 1000; + public static final String UNKNOWN_EXCEPTION_MSG = + "An error occured processing your request"; + + public static final int UNSUPPORTED_EXCEPTION = 1001; + public static final String UNSUPPORTED_EXCEPTION_MSG = + "Unsupported method - Roller does not support this method"; + + public static final int USER_DISABLED = 1002; + public static final String USER_DISABLED_MSG = + "User is disabled"; + + public static final int WEBLOG_NOT_FOUND = 1003; + public static final String WEBLOG_NOT_FOUND_MSG = + "Weblog is not found or is disabled"; + + public static final int WEBLOG_DISABLED = 1004; + public static final String WEBLOG_DISABLED_MSG = + "Weblog is not found or is disabled"; + + public static final int BLOGGERAPI_DISABLED = 1005; + public static final String BLOGGERAPI_DISABLED_MSG = + "Weblog does not exist or XML-RPC disabled in web"; + + public static final int INVALID_POSTID = 2000; + public static final String INVALID_POSTID_MSG = + "The entry postid you submitted is invalid"; + + //public static final int NOBLOGS_EXCEPTION = 3000; + //public static final String NOBLOGS_EXCEPTION_MSG = + //"There are no categories defined for your user"; + + public static final int UPLOAD_DENIED_EXCEPTION = 4000; + public static final String UPLOAD_DENIED_EXCEPTION_MSG = + "Upload denied"; + + //------------------------------------------------------------------------ + public BaseAPIHandler() + { + } + + //------------------------------------------------------------------------ + //public void prep( HttpServletRequest req ) + //{ + //mRoller = RollerContext.getRoller(req); + //mContextUrl = RollerContext.getRollerContext(req).getAbsoluteContextUrl(req); + // + + //------------------------------------------------------------------------ + /** + * Returns website, but only if user authenticates and is authorized to edit. + * @param blogid Blogid sent in request (used as website's hanldle) + * @param username Username sent in request + * @param password Password sent in requeset + */ + protected WebsiteData validate(String blogid, String username, String password) + throws Exception + { + boolean authenticated = false; + boolean userEnabled = false; + boolean weblogEnabled = false; + boolean apiEnabled = false; + boolean weblogFound = false; + WebsiteData website = null; + UserData user = null; + try + { + // Get Roller request object for current thread + RollerRequest rreq = RollerRequest.getRollerRequest(); + + UserManager userMgr = RollerFactory.getRoller().getUserManager(); + user = userMgr.getUserByUsername(username); + userEnabled = user.getEnabled().booleanValue(); + + website = userMgr.getWebsiteByHandle(blogid); + if (website != null) { + weblogFound = true; + weblogEnabled = website.getEnabled().booleanValue(); + apiEnabled = website.getEnableBloggerApi().booleanValue(); + } + + if (user != null) + { + // are passwords encrypted? + RollerContext rollerContext = + RollerContext.getRollerContext(); + String encrypted = + RollerConfig.getProperty("passwds.encryption.enabled"); + //System.out.print("password was [" + password + "] "); + if ("true".equalsIgnoreCase(encrypted)) + { + password = Utilities.encodePassword(password, + RollerConfig.getProperty("passwds.encryption.algorithm")); + } + //System.out.println("is now [" + password + "]"); + authenticated= user.getPassword().equals(password); + if (authenticated) + { + //RollerFactory.getRoller().setUser(user); + } + } + } + catch (Exception e) + { + mLogger.error("ERROR internal error validating user", e); + } + + if ( !authenticated ) + { + throw new XmlRpcException( + AUTHORIZATION_EXCEPTION, AUTHORIZATION_EXCEPTION_MSG); + } + if ( !userEnabled ) + { + throw new XmlRpcException( + USER_DISABLED, USER_DISABLED_MSG); + } + if ( !weblogEnabled ) + { + throw new XmlRpcException( + WEBLOG_DISABLED, WEBLOG_DISABLED_MSG); + } + if ( !weblogFound ) + { + throw new XmlRpcException( + WEBLOG_NOT_FOUND, WEBLOG_NOT_FOUND_MSG); + } + if ( !apiEnabled ) + { + throw new XmlRpcException( + BLOGGERAPI_DISABLED, BLOGGERAPI_DISABLED_MSG); + } + return website; + } + + //------------------------------------------------------------------------ + /** + * Returns true if username/password are valid and user is not disabled. + * @param username Username sent in request + * @param password Password sent in requeset + */ + protected boolean validateUser(String username, String password) + throws Exception + { + boolean authenticated = false; + boolean enabled = false; + UserData user = null; + try + { + // Get Roller request object for current thread + RollerRequest rreq = RollerRequest.getRollerRequest(); + + UserManager userMgr = RollerFactory.getRoller().getUserManager(); + user = userMgr.getUserByUsername(username); + + enabled = user.getEnabled().booleanValue(); + if (enabled) + { + // are passwords encrypted? + RollerContext rollerContext = + RollerContext.getRollerContext(); + String encrypted = + RollerConfig.getProperty("passwds.encryption.enabled"); + //System.out.print("password was [" + password + "] "); + if ("true".equalsIgnoreCase(encrypted)) + { + password = Utilities.encodePassword(password, + RollerConfig.getProperty("passwds.encryption.algorithm")); + } + //System.out.println("is now [" + password + "]"); + authenticated = user.getPassword().equals(password); + if (authenticated) + { + //RollerFactory.getRoller().setUser(user); + } + } + } + catch (Exception e) + { + mLogger.error("ERROR internal error validating user", e); + } + + if ( !enabled ) + { + throw new XmlRpcException( + BLOGGERAPI_DISABLED, BLOGGERAPI_DISABLED_MSG); + } + + if ( !authenticated ) + { + throw new XmlRpcException( + AUTHORIZATION_EXCEPTION, AUTHORIZATION_EXCEPTION_MSG); + } + return authenticated; + } + + //------------------------------------------------------------------------ + protected void flushPageCache(WebsiteData website) throws Exception + { + RollerRequest rreq = RollerRequest.getRollerRequest(); + //PageCacheFilter.removeFromCache( rreq.getRequest(), website); + CacheManager.invalidate(website); + } +}
Added: incubator/roller/trunk/src/org/roller/presentation/webservices/xmlrpc/BloggerAPIHandler.java URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/webservices/xmlrpc/BloggerAPIHandler.java?rev=395633&view=auto ============================================================================== --- incubator/roller/trunk/src/org/roller/presentation/webservices/xmlrpc/BloggerAPIHandler.java (added) +++ incubator/roller/trunk/src/org/roller/presentation/webservices/xmlrpc/BloggerAPIHandler.java Thu Apr 20 09:22:49 2006 @@ -0,0 +1,488 @@ + +package org.roller.presentation.webservices.xmlrpc; + +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Date; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; +import java.util.Vector; +import javax.servlet.http.HttpServletRequest; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.xmlrpc.XmlRpcException; +import org.roller.RollerException; +import org.roller.model.Roller; +import org.roller.model.RollerFactory; +import org.roller.model.UserManager; +import org.roller.model.WeblogManager; +import org.roller.pojos.WeblogTemplate; +import org.roller.pojos.UserData; +import org.roller.pojos.WeblogEntryData; +import org.roller.pojos.WebsiteData; +import org.roller.presentation.RollerContext; +import org.roller.presentation.RollerRequest; + + +/** + * Roller XML-RPC Handler for the Blogger v1 API. + * + * Blogger API spec can be found at http://plant.blogger.com/api/index.html + * See also http://xmlrpc.free-conversant.com/docs/bloggerAPI + * + * @author David M Johnson + */ +public class BloggerAPIHandler extends BaseAPIHandler { + + static final long serialVersionUID = 2398898776655115019L; + + private static Log mLogger = LogFactory.getLog(BloggerAPIHandler.class); + + public BloggerAPIHandler() { + super(); + } + + + /** + * Delete a Post + * + * @param appkey Unique identifier/passcode of the application sending the post + * @param postid Unique identifier of the post to be changed + * @param userid Login for a Blogger user who has permission to post to the blog + * @param password Password for said username + * @param publish Ignored + * @throws XmlRpcException + * @return + */ + public boolean deletePost(String appkey, String postid, String userid, + String password, boolean publish) throws Exception { + + mLogger.debug("deletePost() Called =====[ SUPPORTED ]====="); + mLogger.debug(" Appkey: " + appkey); + mLogger.debug(" PostId: " + postid); + mLogger.debug(" UserId: " + userid); + + Roller roller = RollerFactory.getRoller(); + WeblogManager weblogMgr = roller.getWeblogManager(); + WeblogEntryData entry = weblogMgr.getWeblogEntry(postid); + + validate(entry.getWebsite().getHandle(), userid, password); + + try { + // delete the entry + weblogMgr.removeWeblogEntry(entry); + roller.flush(); + + // notify cache + flushPageCache(entry.getWebsite()); + } catch (Exception e) { + String msg = "ERROR in blogger.deletePost: "+e.getClass().getName(); + mLogger.error(msg,e); + throw new XmlRpcException(UNKNOWN_EXCEPTION, msg); + } + + return true; + } + + + /** + * Edits the main index template of a given blog. Roller only support + * updating the main template, the default template of your weblog. + * + * @param appkey Unique identifier/passcode of the application sending the post + * @param blogid Unique identifier of the blog the post will be added to + * @param userid Login for a Blogger user who has permission to post to the blog + * @param password Password for said username + * @param template The text for the new template (usually mostly HTML). + * @param templateType Determines which of the blog's templates is to be set. + * @throws XmlRpcException + * @return + */ + public boolean setTemplate(String appkey, String blogid, String userid, + String password, String templateData, + String templateType) throws Exception { + + mLogger.debug("setTemplate() Called =====[ SUPPORTED ]====="); + mLogger.debug(" Appkey: " + appkey); + mLogger.debug(" BlogId: " + blogid); + mLogger.debug(" UserId: " + userid); + mLogger.debug(" Template: " + templateData); + mLogger.debug(" Type: " + templateType); + + validate(blogid, userid, password); + + if (! templateType.equals("main")) { + throw new XmlRpcException( + UNKNOWN_EXCEPTION, "Roller only supports main template"); + } + + try { + Roller roller = RollerFactory.getRoller(); + UserManager userMgr = roller.getUserManager(); + + WeblogTemplate page = userMgr.getPage(templateType); + page.setContents(templateData); + userMgr.savePage(page); + flushPageCache(page.getWebsite()); + + return true; + } catch (RollerException e) { + String msg = "ERROR in BlooggerAPIHander.setTemplate"; + mLogger.error(msg,e); + throw new XmlRpcException(UNKNOWN_EXCEPTION,msg); + } + } + + + /** + * Returns the main or archive index template of a given blog + * + * @param appkey Unique identifier/passcode of the application sending the post + * @param blogid Unique identifier of the blog the post will be added to + * @param userid Login for a Blogger user who has permission to post to the blog + * @param password Password for said username + * @param templateType Determines which of the blog's templates will be returned. Currently, either "main" or "archiveIndex" + * @throws XmlRpcException + * @return + */ + public String getTemplate(String appkey, String blogid, String userid, + String password, String templateType) + throws Exception { + + mLogger.debug("getTemplate() Called =====[ SUPPORTED ]====="); + mLogger.debug(" Appkey: " + appkey); + mLogger.debug(" BlogId: " + blogid); + mLogger.debug(" UserId: " + userid); + mLogger.debug(" Type: " + templateType); + + validate(blogid, userid,password); + + try { + Roller roller = RollerFactory.getRoller(); + UserManager userMgr = roller.getUserManager(); + WeblogTemplate page = userMgr.getPage(templateType); + + if ( null == page ) { + throw new XmlRpcException(UNKNOWN_EXCEPTION,"Template not found"); + } else { + return page.getContents(); + } + } catch (Exception e) { + String msg = "ERROR in BlooggerAPIHander.getTemplate"; + mLogger.error(msg,e); + throw new XmlRpcException(UNKNOWN_EXCEPTION,msg); + } + } + + + /** + * Authenticates a user and returns basic user info (name, email, userid, etc.) + * + * @param appkey Unique identifier/passcode of the application sending the post + * @param userid Login for a Blogger user who has permission to post to the blog + * @param password Password for said username + * @throws XmlRpcException + * @return + */ + public Object getUserInfo(String appkey, String userid, String password) + throws Exception { + + mLogger.debug("getUserInfo() Called =====[ SUPPORTED ]====="); + mLogger.debug(" Appkey: " + appkey); + mLogger.debug(" UserId: " + userid); + + validateUser(userid, password); + + try { + Roller roller = RollerFactory.getRoller(); + UserManager userMgr = roller.getUserManager(); + UserData user = userMgr.getUserByUsername(userid); + + // parses full name into two strings, firstname and lastname + String firstname = "", lastname = ""; + StringTokenizer toker = new StringTokenizer(user.getFullName()); + + if (toker.hasMoreTokens()) { + firstname = toker.nextToken(); + } + + while (toker.hasMoreTokens()) { + if ( !lastname.equals("") ) { + lastname += " "; + } + lastname += toker.nextToken(); + } + + RollerRequest rreq = RollerRequest.getRollerRequest(); + HttpServletRequest req = rreq.getRequest(); + String contextUrl = + RollerContext.getRollerContext().getAbsoluteContextUrl(req); + + // populates user information to return as a result + Hashtable result = new Hashtable(); + result.put("nickname", user.getUserName()); + result.put("userid", user.getUserName()); + result.put("url", contextUrl+"/page/"+userid); + result.put("email", ""); + result.put("lastname", lastname); + result.put("firstname", firstname); + + return result; + } catch (RollerException e) { + String msg = "ERROR in BlooggerAPIHander.getInfo"; + mLogger.error(msg,e); + throw new XmlRpcException(UNKNOWN_EXCEPTION,msg); + } + } + + + /** + * Returns information on all the blogs a given user is a member of + * + * @param appkey Unique identifier/passcode of the application sending the post + * @param userid Login for a Blogger user who has permission to post to the blog + * @param password Password for said username + * @throws XmlRpcException + * @return + */ + public Object getUsersBlogs(String appkey, String userid, String password) + throws Exception { + + mLogger.debug("getUsersBlogs() Called ===[ SUPPORTED ]======="); + mLogger.debug(" Appkey: " + appkey); + mLogger.debug(" UserId: " + userid); + + Vector result = new Vector(); + if (validateUser(userid, password)) { + try { + RollerRequest rreq = RollerRequest.getRollerRequest(); + HttpServletRequest req = rreq.getRequest(); + String contextUrl = + RollerContext.getRollerContext().getAbsoluteContextUrl(req); + + UserManager umgr = RollerFactory.getRoller().getUserManager(); + UserData user = umgr.getUserByUsername(userid); + // get list of user's enabled websites + List websites = umgr.getWebsites(user, Boolean.TRUE, null); + Iterator iter = websites.iterator(); + while (iter.hasNext()) { + WebsiteData website = (WebsiteData)iter.next(); + Hashtable blog = new Hashtable(3); + blog.put("url", contextUrl+"/page/"+website.getHandle()); + blog.put("blogid", website.getHandle()); + blog.put("blogName", website.getName()); + result.add(blog); + } + } catch (Exception e) { + String msg = "ERROR in BlooggerAPIHander.getUsersBlogs"; + mLogger.error(msg,e); + throw new XmlRpcException(UNKNOWN_EXCEPTION, msg); + } + } + return result; + } + + + /** + * Edits a given post. Optionally, will publish the blog after making the edit + * + * @param appkey Unique identifier/passcode of the application sending the post + * @param postid Unique identifier of the post to be changed + * @param userid Login for a Blogger user who has permission to post to the blog + * @param password Password for said username + * @param content Contents of the post + * @param publish If true, the blog will be published immediately after the post is made + * @throws XmlRpcException + * @return + */ + public boolean editPost(String appkey, String postid, String userid, + String password, String content, boolean publish) + throws Exception { + + mLogger.debug("editPost() Called ========[ SUPPORTED ]====="); + mLogger.debug(" Appkey: " + appkey); + mLogger.debug(" PostId: " + postid); + mLogger.debug(" UserId: " + userid); + mLogger.debug(" Publish: " + publish); + mLogger.debug(" Content:\n " + content); + + if (validateUser(userid, password)) { + try { + Timestamp current = new Timestamp(System.currentTimeMillis()); + + Roller roller = RollerFactory.getRoller(); + WeblogManager weblogMgr = roller.getWeblogManager(); + WeblogEntryData entry = weblogMgr.getWeblogEntry(postid); + entry.setText(content); + entry.setUpdateTime(current); + if (Boolean.valueOf(publish).booleanValue()) { + entry.setStatus(WeblogEntryData.PUBLISHED); + } else { + entry.setStatus(WeblogEntryData.DRAFT); + } + + // save the entry + weblogMgr.saveWeblogEntry(entry); + roller.flush(); + + // notify cache + flushPageCache(entry.getWebsite()); + + return true; + } catch (Exception e) { + String msg = "ERROR in BlooggerAPIHander.editPost"; + mLogger.error(msg,e); + throw new XmlRpcException(UNKNOWN_EXCEPTION, msg); + } + } + return false; + } + + + /** + * Makes a new post to a designated blog. Optionally, will publish the blog after making the post + * + * @param appkey Unique identifier/passcode of the application sending the post + * @param blogid Unique identifier of the blog the post will be added to + * @param userid Login for a Blogger user who has permission to post to the blog + * @param password Password for said username + * @param content Contents of the post + * @param publish If true, the blog will be published immediately after the post is made + * @throws XmlRpcException + * @return + */ + public String newPost(String appkey, String blogid, String userid, + String password, String content, boolean publish) + throws Exception { + + mLogger.debug("newPost() Called ===========[ SUPPORTED ]====="); + mLogger.debug(" Appkey: " + appkey); + mLogger.debug(" BlogId: " + blogid); + mLogger.debug(" UserId: " + userid); + mLogger.debug(" Publish: " + publish); + mLogger.debug(" Content:\n " + content); + + WebsiteData website = validate(blogid, userid,password); + + // extract the title from the content + String title = ""; + + if (content.indexOf("<title>") != -1) { + title = + content.substring(content.indexOf("<title>") + 7, + content.indexOf("</title>")); + content = StringUtils.replace(content, "<title>"+title+"</title>", ""); + } + + try { + RollerRequest rreq = RollerRequest.getRollerRequest(); + Roller roller = RollerFactory.getRoller(); + WeblogManager weblogMgr = roller.getWeblogManager(); + + Timestamp current = new Timestamp(System.currentTimeMillis()); + + WeblogEntryData entry = new WeblogEntryData(); + entry.setTitle(title); + entry.setText(content); + entry.setPubTime(current); + entry.setUpdateTime(current); + // TODO BACKEND: fix from backend refactoring + //entry.setCreator(roller.getUser()); + entry.setWebsite(website); + entry.setCategory(website.getBloggerCategory()); + if (Boolean.valueOf(publish).booleanValue()) { + entry.setStatus(WeblogEntryData.PUBLISHED); + } else { + entry.setStatus(WeblogEntryData.DRAFT); + } + + // save the entry + weblogMgr.saveWeblogEntry(entry); + roller.flush(); + + // notify cache + flushPageCache(entry.getWebsite()); +/* + String blogUrl = Utilities.escapeHTML( + RollerContext.getRollerContext(req).getAbsoluteContextUrl(req) + + "/page/" + userid); + RollerXmlRpcClient.sendWeblogsPing( + blogUrl, + entry.getWebsite().getName()); + */ + return entry.getId(); + } catch (Exception e) { + String msg = "ERROR in BlooggerAPIHander.newPost"; + mLogger.error(msg,e); + throw new XmlRpcException(UNKNOWN_EXCEPTION, msg); + } + } + + + /** + * This method was added to the Blogger 1.0 API via an Email from Evan + * Williams to the Yahoo Group bloggerDev, see the email message for details - + * http://groups.yahoo.com/group/bloggerDev/message/225 + * + * @param appkey Unique identifier/passcode of the application sending the post + * @param blogid Unique identifier of the blog the post will be added to + * @param userid Login for a Blogger user who has permission to post to the blog + * @param password Password for said username + * @param numposts Number of Posts to Retrieve + * @throws XmlRpcException + * @return Vector of Hashtables, each containing dateCreated, userid, postid, content + */ + public Object getRecentPosts(String appkey, String blogid, String userid, + String password, int numposts) + throws Exception { + + mLogger.debug("getRecentPosts() Called ===========[ SUPPORTED ]====="); + mLogger.debug(" Appkey: " + appkey); + mLogger.debug(" BlogId: " + blogid); + mLogger.debug(" UserId: " + userid); + mLogger.debug(" Number: " + numposts); + + WebsiteData website = validate(blogid, userid,password); + + try { + Vector results = new Vector(); + + Roller roller = RollerFactory.getRoller(); + WeblogManager weblogMgr = roller.getWeblogManager(); + if (website != null) { + Map entries = weblogMgr.getWeblogEntryObjectMap( + website, // userName + null, // startDate + new Date(), // endDate + null, // catName + null, // status + new Integer(numposts)); // maxEntries + + Iterator iter = entries.values().iterator(); + while (iter.hasNext()) { + ArrayList list = (ArrayList) iter.next(); + Iterator i = list.iterator(); + while (i.hasNext()) { + WeblogEntryData entry = (WeblogEntryData) i.next(); + Hashtable result = new Hashtable(); + result.put("dateCreated", entry.getPubTime()); + result.put("userid", userid); + result.put("postid", entry.getId()); + result.put("content", entry.getText()); + results.add(result); + } + } + } + return results; + } catch (Exception e) { + String msg = "ERROR in BlooggerAPIHander.getRecentPosts"; + mLogger.error(msg,e); + throw new XmlRpcException(UNKNOWN_EXCEPTION, msg); + } + } + +} Added: incubator/roller/trunk/src/org/roller/presentation/webservices/xmlrpc/MetaWeblogAPIHandler.java URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/webservices/xmlrpc/MetaWeblogAPIHandler.java?rev=395633&view=auto ============================================================================== --- incubator/roller/trunk/src/org/roller/presentation/webservices/xmlrpc/MetaWeblogAPIHandler.java (added) +++ incubator/roller/trunk/src/org/roller/presentation/webservices/xmlrpc/MetaWeblogAPIHandler.java Thu Apr 20 09:22:49 2006 @@ -0,0 +1,467 @@ + +package org.roller.presentation.webservices.xmlrpc; + +import java.io.ByteArrayInputStream; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Date; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Vector; +import javax.servlet.http.HttpServletRequest; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.struts.util.RequestUtils; +import org.apache.xmlrpc.XmlRpcException; +import org.roller.RollerException; +import org.roller.model.FileManager; +import org.roller.model.Roller; +import org.roller.model.RollerFactory; +import org.roller.model.WeblogManager; +import org.roller.pojos.UserData; +import org.roller.pojos.WeblogCategoryData; +import org.roller.pojos.WeblogEntryData; +import org.roller.pojos.WebsiteData; +import org.roller.presentation.RollerContext; +import org.roller.presentation.RollerRequest; +import org.roller.util.RollerMessages; +import org.roller.util.Utilities; + + +/** + * Roller XML-RPC Handler for the MetaWeblog API. + * + * MetaWeblog API spec can be found at http://www.xmlrpc.com/metaWeblogApi + * + * @author David M Johnson + */ +public class MetaWeblogAPIHandler extends BloggerAPIHandler { + + static final long serialVersionUID = -1364456614935668629L; + + private static Log mLogger = LogFactory.getLog(MetaWeblogAPIHandler.class); + + public MetaWeblogAPIHandler() { + super(); + } + + + /** + * Authenticates a user and returns the categories available in the website + * + * @param blogid Dummy Value for Roller + * @param userid Login for a MetaWeblog user who has permission to post to the blog + * @param password Password for said username + * @throws Exception + * @return + */ + public Object getCategories(String blogid, String userid, String password) + throws Exception { + + mLogger.debug("getCategories() Called =====[ SUPPORTED ]====="); + mLogger.debug(" BlogId: " + blogid); + mLogger.debug(" UserId: " + userid); + + WebsiteData website = validate(blogid, userid,password); + RollerRequest rreq = RollerRequest.getRollerRequest(); + Roller roller = RollerFactory.getRoller(); + try { + Hashtable result = new Hashtable(); + WeblogManager weblogMgr = roller.getWeblogManager(); + List cats = weblogMgr.getWeblogCategories(website, false); + for (Iterator wbcItr = cats.iterator(); wbcItr.hasNext();) { + WeblogCategoryData category = (WeblogCategoryData) wbcItr.next(); + result.put(category.getPath(), + createCategoryStruct(category, userid)); + } + return result; + } catch (Exception e) { + String msg = "ERROR in MetaWeblogAPIHandler.getCategories"; + mLogger.error(msg,e); + throw new XmlRpcException(UNKNOWN_EXCEPTION, msg); + } + } + + + /** + * Edits a given post. Optionally, will publish the blog after making the edit + * + * @param postid Unique identifier of the post to be changed + * @param userid Login for a MetaWeblog user who has permission to post to the blog + * @param password Password for said username + * @param struct Contents of the post + * @param publish If true, the blog will be published immediately after the post is made + * @throws org.apache.xmlrpc.XmlRpcException + * @return + */ + public boolean editPost(String postid, String userid, String password, + Hashtable struct, int publish) throws Exception { + + return editPost(postid, userid, password, struct, publish > 0); + } + + + public boolean editPost(String postid, String userid, String password, + Hashtable struct, boolean publish) throws Exception { + + mLogger.debug("editPost() Called ========[ SUPPORTED ]====="); + mLogger.debug(" PostId: " + postid); + mLogger.debug(" UserId: " + userid); + mLogger.debug(" Publish: " + publish); + + Roller roller = RollerFactory.getRoller(); + WeblogManager weblogMgr = roller.getWeblogManager(); + WeblogEntryData entry = weblogMgr.getWeblogEntry(postid); + + validate(entry.getWebsite().getHandle(), userid,password); + + Hashtable postcontent = struct; + String description = (String)postcontent.get("description"); + String title = (String)postcontent.get("title"); + if (title == null) title = ""; + + Date dateCreated = (Date)postcontent.get("dateCreated"); + if (dateCreated == null) dateCreated = (Date)postcontent.get("pubDate"); + + String cat = null; + if ( postcontent.get("categories") != null ) { + Vector cats = (Vector)postcontent.get("categories"); + cat = (String)cats.elementAt(0); + } + mLogger.debug(" Title: " + title); + mLogger.debug(" Category: " + cat); + + try { + + Timestamp current = + new Timestamp(System.currentTimeMillis()); + + if ( !title.equals("") ) entry.setTitle(title); + entry.setText(description); + entry.setUpdateTime(current); + if (Boolean.valueOf(publish).booleanValue()) { + entry.setStatus(WeblogEntryData.PUBLISHED); + } else { + entry.setStatus(WeblogEntryData.DRAFT); + } + if (dateCreated != null) { + entry.setPubTime(new Timestamp(dateCreated.getTime())); + } + + if ( cat != null ) { + // Use first category specified by request + WeblogCategoryData cd = + weblogMgr.getWeblogCategoryByPath(entry.getWebsite(), cat); + entry.setCategory(cd); + } + + // save the entry + weblogMgr.saveWeblogEntry(entry); + roller.flush(); + + // notify cache + flushPageCache(entry.getWebsite()); + + // TODO: Roller timestamps need better than 1 second accuracy + // Until then, we can't allow more than one post per second + Thread.sleep(1000); + + return true; + } catch (Exception e) { + String msg = "ERROR in MetaWeblogAPIHandler.editPost"; + mLogger.error(msg,e); + throw new XmlRpcException(UNKNOWN_EXCEPTION, msg); + } + } + + + /** + * Makes a new post to a designated blog. Optionally, will publish the blog after making the post + * + * @param blogid Unique identifier of the blog the post will be added to + * @param userid Login for a MetaWeblog user who has permission to post to the blog + * @param password Password for said username + * @param struct Contents of the post + * @param publish If true, the blog will be published immediately after the post is made + * @throws org.apache.xmlrpc.XmlRpcException + * @return + */ + public String newPost(String blogid, String userid, String password, + Hashtable struct, int publish) throws Exception { + + return newPost(blogid, userid, password, struct, publish > 0); + } + + + public String newPost(String blogid, String userid, String password, + Hashtable struct, boolean publish) throws Exception { + + mLogger.debug("newPost() Called ===========[ SUPPORTED ]====="); + mLogger.debug(" BlogId: " + blogid); + mLogger.debug(" UserId: " + userid); + mLogger.debug(" Publish: " + publish); + + WebsiteData website = validate(blogid, userid, password); + + Hashtable postcontent = struct; + String description = (String)postcontent.get("description"); + String title = (String)postcontent.get("title"); + if (title == null) title = ""; + + Date dateCreated = (Date)postcontent.get("dateCreated"); + if (dateCreated == null) dateCreated = (Date)postcontent.get("pubDate"); + if (dateCreated == null) dateCreated = new Date(); + + String cat = null; + if ( postcontent.get("categories") != null ) { + Vector cats = (Vector)postcontent.get("categories"); + if (cats.size() > 0) { + // only use the first category passed in + cat = (String)cats.elementAt(0); + } + } + mLogger.debug(" Title: " + title); + mLogger.debug(" Category: " + cat); + + try { + Roller roller = RollerFactory.getRoller(); + WeblogManager weblogMgr = roller.getWeblogManager(); + UserData user = roller.getUserManager().getUserByUsername(userid); + Timestamp current = + new Timestamp(System.currentTimeMillis()); + + WeblogEntryData entry = new WeblogEntryData(); + entry.setTitle(title); + entry.setText(description); + entry.setPubTime(new Timestamp(dateCreated.getTime())); + entry.setUpdateTime(current); + entry.setWebsite(website); + entry.setCreator(user); + if (Boolean.valueOf(publish).booleanValue()) { + entry.setStatus(WeblogEntryData.PUBLISHED); + } else { + entry.setStatus(WeblogEntryData.DRAFT); + } + + + if ( cat != null ) { + // Use first category specified by request + WeblogCategoryData cd = + weblogMgr.getWeblogCategoryByPath(website, cat); + entry.setCategory(cd); + } else { + // Use Blogger API category from user's weblog config + entry.setCategory(website.getBloggerCategory()); + } + + // save the entry + weblogMgr.saveWeblogEntry(entry); + roller.flush(); + + // notify cache + flushPageCache(entry.getWebsite()); + + // TODO: Roller timestamps need better than 1 second accuracy + // Until then, we can't allow more than one post per second + Thread.sleep(1000); + + return entry.getId(); + } catch (Exception e) { + String msg = "ERROR in MetaWeblogAPIHandler.newPost"; + mLogger.error(msg,e); + throw new XmlRpcException(UNKNOWN_EXCEPTION, msg); + } + } + + + /** + * + * @param postid + * @param userid + * @param password + * @return + * @throws Exception + */ + public Object getPost(String postid, String userid, String password) + throws Exception { + + mLogger.debug("getPost() Called =========[ SUPPORTED ]====="); + mLogger.debug(" PostId: " + postid); + mLogger.debug(" UserId: " + userid); + + Roller roller = RollerFactory.getRoller(); + WeblogManager weblogMgr = roller.getWeblogManager(); + WeblogEntryData entry = weblogMgr.getWeblogEntry(postid); + + validate(entry.getWebsite().getHandle(), userid,password); + + try { + return createPostStruct(entry, userid); + } catch (Exception e) { + String msg = "ERROR in MetaWeblogAPIHandler.getPost"; + mLogger.error(msg,e); + throw new XmlRpcException(UNKNOWN_EXCEPTION, msg); + } + } + + + /** + * Allows user to post a binary object, a file, to Roller. If the file is + * allowed by the RollerConfig file-upload settings, then the file will be + * placed in the user's upload diretory. + */ + public Object newMediaObject(String blogid, String userid, String password, + Hashtable struct) throws Exception { + + mLogger.debug("newMediaObject() Called =[ SUPPORTED ]====="); + mLogger.debug(" BlogId: " + blogid); + mLogger.debug(" UserId: " + userid); + mLogger.debug(" Password: *********"); + + WebsiteData website = validate(blogid, userid, password); + try { + String name = (String) struct.get("name"); + name = name.replaceAll("/","_"); + String type = (String) struct.get("type"); + mLogger.debug("newMediaObject name: " + name); + mLogger.debug("newMediaObject type: " + type); + + byte[] bits = (byte[]) struct.get("bits"); + + Roller roller = RollerFactory.getRoller(); + FileManager fmgr = roller.getFileManager(); + RollerMessages msgs = new RollerMessages(); + + // If save is allowed by Roller system-wide policies + if (fmgr.canSave(website.getHandle(), name, bits.length, msgs)) { + // Then save the file + fmgr.saveFile( + website.getHandle(), name, bits.length, new ByteArrayInputStream(bits)); + + RollerRequest rreq = RollerRequest.getRollerRequest(); + HttpServletRequest request = rreq.getRequest(); + + // TODO: build URL to uploaded file should be done in FileManager + String uploadPath = RollerFactory.getRoller().getFileManager().getUploadUrl(); + uploadPath += "/" + website.getHandle() + "/" + name; + String fileLink = RequestUtils.printableURL( + RequestUtils.absoluteURL(request, uploadPath)); + + Hashtable returnStruct = new Hashtable(1); + returnStruct.put("url", fileLink); + return returnStruct; + } + throw new XmlRpcException(UPLOAD_DENIED_EXCEPTION, + "File upload denied because:" + msgs.toString()); + } catch (RollerException e) { + String msg = "ERROR in BlooggerAPIHander.newMediaObject"; + mLogger.error(msg,e); + throw new XmlRpcException(UNKNOWN_EXCEPTION, msg); + } + } + + + /** + * Get a list of recent posts for a category + * + * @param blogid Unique identifier of the blog the post will be added to + * @param userid Login for a Blogger user who has permission to post to the blog + * @param password Password for said username + * @param numposts Number of Posts to Retrieve + * @throws XmlRpcException + * @return + */ + public Object getRecentPosts(String blogid, String userid, String password, + int numposts) throws Exception { + + mLogger.debug("getRecentPosts() Called ===========[ SUPPORTED ]====="); + mLogger.debug(" BlogId: " + blogid); + mLogger.debug(" UserId: " + userid); + mLogger.debug(" Number: " + numposts); + + WebsiteData website = validate(blogid, userid,password); + + try { + Vector results = new Vector(); + + Roller roller = RollerFactory.getRoller(); + WeblogManager weblogMgr = roller.getWeblogManager(); + if (website != null) { + List entries = weblogMgr.getWeblogEntries( + website, // website + null, // startDate + null, // endDate + null, // catName + null, // status + "updateTime", // sortby + new Integer(numposts)); // maxEntries + + Iterator iter = entries.iterator(); + while (iter.hasNext()) { + WeblogEntryData entry = (WeblogEntryData)iter.next(); + results.addElement(createPostStruct(entry, userid)); + } + } + return results; + + } catch (Exception e) { + String msg = "ERROR in BlooggerAPIHander.getRecentPosts"; + mLogger.error(msg,e); + throw new XmlRpcException(UNKNOWN_EXCEPTION, msg); + } + } + + + private Hashtable createPostStruct(WeblogEntryData entry, String userid) { + + RollerRequest rreq = RollerRequest.getRollerRequest(); + HttpServletRequest request = rreq.getRequest(); + RollerContext rollerCtx = RollerContext.getRollerContext(); + String permalink = + rollerCtx.getAbsoluteContextUrl(request) + entry.getPermaLink(); + + Hashtable struct = new Hashtable(); + struct.put("title", entry.getTitle()); + if (entry.getLink() != null) { + struct.put("link", Utilities.escapeHTML(entry.getLink())); + } + + struct.put("description", entry.getText()); + struct.put("pubDate", entry.getPubTime()); + struct.put("dateCreated", entry.getPubTime()); + struct.put("guid", Utilities.escapeHTML(permalink)); + struct.put("permaLink", Utilities.escapeHTML(permalink)); + struct.put("postid", entry.getId()); + struct.put("userid", userid); + + Vector catArray = new Vector(); + catArray.addElement(entry.getCategory().getPath()); + struct.put("categories", catArray); + + return struct; + } + + + private Hashtable createCategoryStruct(WeblogCategoryData category, String userid) { + + RollerRequest rreq = RollerRequest.getRollerRequest(); + HttpServletRequest req = rreq.getRequest(); + String contextUrl = RollerContext.getRollerContext().getAbsoluteContextUrl(req); + + Hashtable struct = new Hashtable(); + struct.put("description", category.getPath()); + + String catUrl = contextUrl+"/page/"+userid+"?catname="+category.getPath(); + catUrl = Utilities.stringReplace(catUrl," ","%20"); + struct.put("htmlUrl", catUrl); + + String rssUrl = contextUrl+"/rss/"+userid+"?catname="+category.getPath(); + rssUrl = Utilities.stringReplace(catUrl," ","%20"); + struct.put("rssUrl",rssUrl); + + return struct; + } + +} Added: incubator/roller/trunk/src/org/roller/presentation/webservices/xmlrpc/RollerXMLRPCServlet.java URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/webservices/xmlrpc/RollerXMLRPCServlet.java?rev=395633&view=auto ============================================================================== --- incubator/roller/trunk/src/org/roller/presentation/webservices/xmlrpc/RollerXMLRPCServlet.java (added) +++ incubator/roller/trunk/src/org/roller/presentation/webservices/xmlrpc/RollerXMLRPCServlet.java Thu Apr 20 09:22:49 2006 @@ -0,0 +1,98 @@ +/* + * RollerXMLRPCServlet.java + */ + +package org.roller.presentation.webservices.xmlrpc; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.StringBufferInputStream; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.xmlrpc.XmlRpcServer; + +/** + * Roller's XML RPC Servlet sets up XmlRpcHandler for Blogger/ API. + * + * @author David M Johnson + * + * @web.servlet name="RollerXMLRPCServlet" + * @web.servlet-mapping url-pattern="/xmlrpc" + */ +public class RollerXMLRPCServlet extends HttpServlet +{ + static final long serialVersionUID = -4424719615968330852L; + + private static Log mLogger = + LogFactory.getFactory().getInstance(RollerXMLRPCServlet.class); + + private transient XmlRpcServer mXmlRpcServer = new XmlRpcServer(); + private BloggerAPIHandler mBloggerHandler = null; + private MetaWeblogAPIHandler mMetaWeblogHandler = null; + + //------------------------------------------------------------------------ + + /** + * Initializes the servlet. + */ + public void init(ServletConfig config) throws ServletException + { + super.init(config); + try + { + mBloggerHandler = new BloggerAPIHandler(); + mXmlRpcServer.addHandler("blogger", mBloggerHandler); + + mMetaWeblogHandler = new MetaWeblogAPIHandler(); + mXmlRpcServer.addHandler("metaWeblog", mMetaWeblogHandler); + } + catch (Exception e) + { + mLogger.error("Initialization of XML-RPC servlet failed", e); + } + } + + //------------------------------------------------------------------------ + + protected void service(HttpServletRequest request, + HttpServletResponse response) + throws ServletException, java.io.IOException + { + InputStream is = request.getInputStream(); + + if (mLogger.isDebugEnabled()) { + BufferedReader br = new BufferedReader(new InputStreamReader(is)); + String line = null; + StringBuffer sb = new StringBuffer(); + while ((line = br.readLine()) != null) { + sb.append(line); + sb.append("\n"); + } + mLogger.debug(sb.toString()); + is = new StringBufferInputStream(sb.toString()); + } + + // execute XML-RPC request + byte[] result = mXmlRpcServer.execute(is); + + if (mLogger.isDebugEnabled()) { + String output = new String(result); + mLogger.debug(output); + } + + response.setContentType("text/xml"); + response.setContentLength(result.length); + OutputStream output = response.getOutputStream(); + output.write(result); + output.flush(); + } +} Added: incubator/roller/trunk/src/org/roller/presentation/webservices/xmlrpc/package.html URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/webservices/xmlrpc/package.html?rev=395633&view=auto ============================================================================== --- incubator/roller/trunk/src/org/roller/presentation/webservices/xmlrpc/package.html (added) +++ incubator/roller/trunk/src/org/roller/presentation/webservices/xmlrpc/package.html Thu Apr 20 09:22:49 2006 @@ -0,0 +1,16 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html> +<head> + <title></title> +</head> +<body> +Roller implementation of Blogger and MetaWeblog APIs. + +<p>Blogger API spec can be found at http://plant.blogger.com/api/index.html</p> + +<p>See also http://xmlrpc.free-conversant.com/docs/bloggerAPI</p> + +<p>MetaWeblog API spec can be found at http://www.xmlrpc.com/metaWeblogApi</p> + +</body> +</html> Modified: incubator/roller/trunk/tests/org/roller/business/WeblogManagerTest.java URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/tests/org/roller/business/WeblogManagerTest.java?rev=395633&r1=395632&r2=395633&view=diff ============================================================================== --- incubator/roller/trunk/tests/org/roller/business/WeblogManagerTest.java (original) +++ incubator/roller/trunk/tests/org/roller/business/WeblogManagerTest.java Thu Apr 20 09:22:49 2006 @@ -1,4 +1,4 @@ -package org.roller.business; + package org.roller.business; import java.sql.Timestamp; import java.util.Date; Modified: incubator/roller/trunk/web/WEB-INF/classes/roller.properties URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/web/WEB-INF/classes/roller.properties?rev=395633&r1=395632&r2=395633&view=diff ============================================================================== --- incubator/roller/trunk/web/WEB-INF/classes/roller.properties (original) +++ incubator/roller/trunk/web/WEB-INF/classes/roller.properties Thu Apr 20 09:22:49 2006 @@ -346,6 +346,13 @@ # Set to true to allow only default topic tag site (and avoid costly bookmark queries) org.roller.presentation.velocity.plugins.topictag.TopicTagPlugin.ignoreBookmarks=true +#--------------------------------- +# Experimental settings + +# Atom Publishing Protocol (APP) - this is an incomplete and untested +# implementation of an unfinished IETF specification. Intended only for +# interoperability testing. DO NOT ENABLE IN PRODUCTION! +webservices.atomprotocol.enable=false #---------------------------------- # legacy settings (thing that should be deprecated Modified: incubator/roller/trunk/web/WEB-INF/classes/rome.properties URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/web/WEB-INF/classes/rome.properties?rev=395633&r1=395632&r2=395633&view=diff ============================================================================== --- incubator/roller/trunk/web/WEB-INF/classes/rome.properties (original) +++ incubator/roller/trunk/web/WEB-INF/classes/rome.properties Thu Apr 20 09:22:49 2006 @@ -36,6 +36,12 @@ rss_1.0.item.ModuleGenerator.classes =com.sun.syndication.io.impl.DCModuleGenerator \ org.roller.util.rome.ContentModuleGenerator +# Handle Atom Pub Protocol pubcontrol extension + +atom_1.0.item.ModuleParser.classes =org.roller.presentation.atomapi.PubControlModuleParser + +atom_1.0.item.ModuleGenerator.classes =org.roller.presentation.atomapi.PubControlModuleGenerator +
