001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. The ASF licenses this file to You
004: * under the Apache License, Version 2.0 (the "License"); you may not
005: * use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License. For additional information regarding
015: * copyright in this work, please see the NOTICE file in the top level
016: * directory of this distribution.
017: */
018:
019: package org.apache.roller.webservices.xmlrpc;
020:
021: import java.io.ByteArrayInputStream;
022: import java.sql.Timestamp;
023: import java.util.Date;
024: import java.util.Hashtable;
025: import java.util.Iterator;
026: import java.util.List;
027: import java.util.Vector;
028:
029: import javax.servlet.http.HttpServletRequest;
030:
031: import org.apache.commons.lang.StringUtils;
032: import org.apache.commons.logging.Log;
033: import org.apache.commons.logging.LogFactory;
034: import org.apache.roller.RollerException;
035: import org.apache.roller.config.RollerRuntimeConfig;
036: import org.apache.roller.business.FileManager;
037: import org.apache.roller.business.Roller;
038: import org.apache.roller.business.RollerFactory;
039: import org.apache.roller.business.WeblogManager;
040: import org.apache.roller.pojos.UserData;
041: import org.apache.roller.pojos.WeblogCategoryData;
042: import org.apache.roller.pojos.WeblogEntryData;
043: import org.apache.roller.pojos.WebsiteData;
044: import org.apache.roller.ui.core.RollerContext;
045: import org.apache.roller.util.RollerMessages;
046: import org.apache.roller.util.URLUtilities;
047: import org.apache.roller.util.Utilities;
048: import org.apache.struts.util.RequestUtils;
049: import org.apache.xmlrpc.XmlRpcException;
050:
051: /**
052: * Roller XML-RPC Handler for the MetaWeblog API.
053: *
054: * MetaWeblog API spec can be found at http://www.xmlrpc.com/metaWeblogApi
055: *
056: * @author David M Johnson
057: */
058: public class MetaWeblogAPIHandler extends BloggerAPIHandler {
059:
060: static final long serialVersionUID = -1364456614935668629L;
061:
062: private static Log mLogger = LogFactory
063: .getLog(MetaWeblogAPIHandler.class);
064:
065: public MetaWeblogAPIHandler() {
066: super ();
067: }
068:
069: /**
070: * Authenticates a user and returns the categories available in the website
071: *
072: * @param blogid Dummy Value for Roller
073: * @param userid Login for a MetaWeblog user who has permission to post to the blog
074: * @param password Password for said username
075: * @throws Exception
076: * @return
077: */
078: public Object getCategories(String blogid, String userid,
079: String password) throws Exception {
080:
081: mLogger.debug("getCategories() Called =====[ SUPPORTED ]=====");
082: mLogger.debug(" BlogId: " + blogid);
083: mLogger.debug(" UserId: " + userid);
084:
085: WebsiteData website = validate(blogid, userid, password);
086: Roller roller = RollerFactory.getRoller();
087: try {
088: Hashtable result = new Hashtable();
089: WeblogManager weblogMgr = roller.getWeblogManager();
090: List cats = weblogMgr.getWeblogCategories(website, false);
091: for (Iterator wbcItr = cats.iterator(); wbcItr.hasNext();) {
092: WeblogCategoryData category = (WeblogCategoryData) wbcItr
093: .next();
094: result.put(category.getPath(), createCategoryStruct(
095: category, userid));
096: }
097: return result;
098: } catch (Exception e) {
099: String msg = "ERROR in MetaWeblogAPIHandler.getCategories";
100: mLogger.error(msg, e);
101: throw new XmlRpcException(UNKNOWN_EXCEPTION, msg);
102: }
103: }
104:
105: /**
106: * Edits a given post. Optionally, will publish the blog after making the edit
107: *
108: * @param postid Unique identifier of the post to be changed
109: * @param userid Login for a MetaWeblog user who has permission to post to the blog
110: * @param password Password for said username
111: * @param struct Contents of the post
112: * @param publish If true, the blog will be published immediately after the post is made
113: * @throws org.apache.xmlrpc.XmlRpcException
114: * @return
115: */
116: public boolean editPost(String postid, String userid,
117: String password, Hashtable struct, int publish)
118: throws Exception {
119:
120: return editPost(postid, userid, password, struct, publish > 0);
121: }
122:
123: public boolean editPost(String postid, String userid,
124: String password, Hashtable struct, boolean publish)
125: throws Exception {
126:
127: mLogger.debug("editPost() Called ========[ SUPPORTED ]=====");
128: mLogger.debug(" PostId: " + postid);
129: mLogger.debug(" UserId: " + userid);
130: mLogger.debug(" Publish: " + publish);
131:
132: Roller roller = RollerFactory.getRoller();
133: WeblogManager weblogMgr = roller.getWeblogManager();
134: WeblogEntryData entry = weblogMgr.getWeblogEntry(postid);
135:
136: validate(entry.getWebsite().getHandle(), userid, password);
137:
138: Hashtable postcontent = struct;
139: String description = (String) postcontent.get("description");
140: String title = (String) postcontent.get("title");
141: if (title == null)
142: title = "";
143:
144: Date dateCreated = (Date) postcontent.get("dateCreated");
145: if (dateCreated == null)
146: dateCreated = (Date) postcontent.get("pubDate");
147:
148: String cat = null;
149: if (postcontent.get("categories") != null) {
150: Vector cats = (Vector) postcontent.get("categories");
151: cat = (String) cats.elementAt(0);
152: }
153: mLogger.debug(" Title: " + title);
154: mLogger.debug(" Category: " + cat);
155:
156: try {
157:
158: Timestamp current = new Timestamp(System
159: .currentTimeMillis());
160:
161: if (!title.equals(""))
162: entry.setTitle(title);
163: entry.setText(description);
164: entry.setUpdateTime(current);
165: if (Boolean.valueOf(publish).booleanValue()) {
166: entry.setStatus(WeblogEntryData.PUBLISHED);
167: } else {
168: entry.setStatus(WeblogEntryData.DRAFT);
169: }
170: if (dateCreated != null) {
171: entry.setPubTime(new Timestamp(dateCreated.getTime()));
172: }
173:
174: if (cat != null) {
175: // Use first category specified by request
176: WeblogCategoryData cd = weblogMgr
177: .getWeblogCategoryByPath(entry.getWebsite(),
178: cat);
179: entry.setCategory(cd);
180: }
181:
182: // save the entry
183: weblogMgr.saveWeblogEntry(entry);
184: roller.flush();
185:
186: // notify cache
187: flushPageCache(entry.getWebsite());
188:
189: // TODO: Roller timestamps need better than 1 second accuracy
190: // Until then, we can't allow more than one post per second
191: Thread.sleep(1000);
192:
193: return true;
194: } catch (Exception e) {
195: String msg = "ERROR in MetaWeblogAPIHandler.editPost";
196: mLogger.error(msg, e);
197: throw new XmlRpcException(UNKNOWN_EXCEPTION, msg);
198: }
199: }
200:
201: /**
202: * Makes a new post to a designated blog. Optionally, will publish the blog after making the post
203: *
204: * @param blogid Unique identifier of the blog the post will be added to
205: * @param userid Login for a MetaWeblog user who has permission to post to the blog
206: * @param password Password for said username
207: * @param struct Contents of the post
208: * @param publish If true, the blog will be published immediately after the post is made
209: * @throws org.apache.xmlrpc.XmlRpcException
210: * @return
211: */
212: public String newPost(String blogid, String userid,
213: String password, Hashtable struct, int publish)
214: throws Exception {
215:
216: return newPost(blogid, userid, password, struct, publish > 0);
217: }
218:
219: public String newPost(String blogid, String userid,
220: String password, Hashtable struct, boolean publish)
221: throws Exception {
222:
223: mLogger.debug("newPost() Called ===========[ SUPPORTED ]=====");
224: mLogger.debug(" BlogId: " + blogid);
225: mLogger.debug(" UserId: " + userid);
226: mLogger.debug(" Publish: " + publish);
227:
228: WebsiteData website = validate(blogid, userid, password);
229:
230: Hashtable postcontent = struct;
231: String description = (String) postcontent.get("description");
232: String title = (String) postcontent.get("title");
233: if (StringUtils.isEmpty(title)
234: && StringUtils.isEmpty(description)) {
235: throw new XmlRpcException(BLOGGERAPI_INCOMPLETE_POST,
236: "Must specify title or description");
237: }
238: if (StringUtils.isEmpty(title)) {
239: title = Utilities
240: .truncateNicely(description, 15, 15, "...");
241: }
242:
243: Date dateCreated = (Date) postcontent.get("dateCreated");
244: if (dateCreated == null)
245: dateCreated = (Date) postcontent.get("pubDate");
246: if (dateCreated == null)
247: dateCreated = new Date();
248: mLogger.debug(" Title: " + title);
249:
250: try {
251: Roller roller = RollerFactory.getRoller();
252: WeblogManager weblogMgr = roller.getWeblogManager();
253: UserData user = roller.getUserManager().getUserByUserName(
254: userid);
255: Timestamp current = new Timestamp(System
256: .currentTimeMillis());
257:
258: WeblogEntryData entry = new WeblogEntryData();
259: entry.setTitle(title);
260: entry.setText(description);
261: entry.setPubTime(new Timestamp(dateCreated.getTime()));
262: entry.setUpdateTime(current);
263: entry.setWebsite(website);
264: entry.setCreator(user);
265: if (Boolean.valueOf(publish).booleanValue()) {
266: entry.setStatus(WeblogEntryData.PUBLISHED);
267: } else {
268: entry.setStatus(WeblogEntryData.DRAFT);
269: }
270:
271: // MetaWeblog supports multiple cats, Roller supports one/entry
272: // so here we take accept the first category that exists
273: WeblogCategoryData rollerCat = null;
274: if (postcontent.get("categories") != null) {
275: Vector cats = (Vector) postcontent.get("categories");
276: if (cats != null && cats.size() > 0) {
277: for (int i = 0; i < cats.size(); i++) {
278: String cat = (String) cats.get(i);
279: rollerCat = weblogMgr.getWeblogCategoryByPath(
280: website, cat);
281: if (rollerCat != null) {
282: entry.setCategory(rollerCat);
283: break;
284: }
285: }
286: }
287: }
288: if (rollerCat == null) {
289: // or we fall back to the default Blogger API category
290: entry.setCategory(website.getBloggerCategory());
291: }
292:
293: // save the entry
294: weblogMgr.saveWeblogEntry(entry);
295: roller.flush();
296:
297: // notify cache
298: flushPageCache(entry.getWebsite());
299:
300: // TODO: Roller timestamps need better than 1 second accuracy
301: // Until then, we can't allow more than one post per second
302: Thread.sleep(1000);
303:
304: return entry.getId();
305: } catch (Exception e) {
306: String msg = "ERROR in MetaWeblogAPIHandler.newPost";
307: mLogger.error(msg, e);
308: throw new XmlRpcException(UNKNOWN_EXCEPTION, msg);
309: }
310: }
311:
312: /**
313: *
314: * @param postid
315: * @param userid
316: * @param password
317: * @return
318: * @throws Exception
319: */
320: public Object getPost(String postid, String userid, String password)
321: throws Exception {
322:
323: mLogger.debug("getPost() Called =========[ SUPPORTED ]=====");
324: mLogger.debug(" PostId: " + postid);
325: mLogger.debug(" UserId: " + userid);
326:
327: Roller roller = RollerFactory.getRoller();
328: WeblogManager weblogMgr = roller.getWeblogManager();
329: WeblogEntryData entry = weblogMgr.getWeblogEntry(postid);
330:
331: validate(entry.getWebsite().getHandle(), userid, password);
332:
333: try {
334: return createPostStruct(entry, userid);
335: } catch (Exception e) {
336: String msg = "ERROR in MetaWeblogAPIHandler.getPost";
337: mLogger.error(msg, e);
338: throw new XmlRpcException(UNKNOWN_EXCEPTION, msg);
339: }
340: }
341:
342: /**
343: * Allows user to post a binary object, a file, to Roller. If the file is
344: * allowed by the RollerConfig file-upload settings, then the file will be
345: * placed in the user's upload diretory.
346: */
347: public Object newMediaObject(String blogid, String userid,
348: String password, Hashtable struct) throws Exception {
349:
350: mLogger.debug("newMediaObject() Called =[ SUPPORTED ]=====");
351: mLogger.debug(" BlogId: " + blogid);
352: mLogger.debug(" UserId: " + userid);
353: mLogger.debug(" Password: *********");
354:
355: WebsiteData website = validate(blogid, userid, password);
356: try {
357: String name = (String) struct.get("name");
358: name = name.replaceAll("/", "_");
359: String type = (String) struct.get("type");
360: mLogger.debug("newMediaObject name: " + name);
361: mLogger.debug("newMediaObject type: " + type);
362:
363: byte[] bits = (byte[]) struct.get("bits");
364:
365: Roller roller = RollerFactory.getRoller();
366: FileManager fmgr = roller.getFileManager();
367: RollerMessages msgs = new RollerMessages();
368:
369: // Try to save file
370: fmgr.saveFile(website, name, type, bits.length,
371: new ByteArrayInputStream(bits));
372:
373: String fileLink = URLUtilities.getWeblogResourceURL(
374: website, name, true);
375:
376: Hashtable returnStruct = new Hashtable(1);
377: returnStruct.put("url", fileLink);
378: return returnStruct;
379:
380: } catch (RollerException e) {
381: String msg = "ERROR in MetaWeblogAPIHandler.newMediaObject";
382: mLogger.error(msg, e);
383: throw new XmlRpcException(UNKNOWN_EXCEPTION, msg);
384: }
385: }
386:
387: /**
388: * Get a list of recent posts for a category
389: *
390: * @param blogid Unique identifier of the blog the post will be added to
391: * @param userid Login for a Blogger user who has permission to post to the blog
392: * @param password Password for said username
393: * @param numposts Number of Posts to Retrieve
394: * @throws XmlRpcException
395: * @return
396: */
397: public Object getRecentPosts(String blogid, String userid,
398: String password, int numposts) throws Exception {
399:
400: mLogger
401: .debug("getRecentPosts() Called ===========[ SUPPORTED ]=====");
402: mLogger.debug(" BlogId: " + blogid);
403: mLogger.debug(" UserId: " + userid);
404: mLogger.debug(" Number: " + numposts);
405:
406: WebsiteData website = validate(blogid, userid, password);
407:
408: try {
409: Vector results = new Vector();
410:
411: Roller roller = RollerFactory.getRoller();
412: WeblogManager weblogMgr = roller.getWeblogManager();
413: if (website != null) {
414: List entries = weblogMgr.getWeblogEntries(website, // website
415: null, null, // startDate
416: null, // endDate
417: null, // catName
418: null, // tags
419: null, // status
420: "updateTime", // sortby
421: null, 0, numposts);
422:
423: Iterator iter = entries.iterator();
424: while (iter.hasNext()) {
425: WeblogEntryData entry = (WeblogEntryData) iter
426: .next();
427: results.addElement(createPostStruct(entry, userid));
428: }
429: }
430: return results;
431:
432: } catch (Exception e) {
433: String msg = "ERROR in MetaWeblogAPIHandler.getRecentPosts";
434: mLogger.error(msg, e);
435: throw new XmlRpcException(UNKNOWN_EXCEPTION, msg);
436: }
437: }
438:
439: private Hashtable createPostStruct(WeblogEntryData entry,
440: String userid) {
441:
442: String permalink = RollerRuntimeConfig.getAbsoluteContextURL()
443: + entry.getPermaLink();
444:
445: Hashtable struct = new Hashtable();
446: struct.put("title", entry.getTitle());
447: if (entry.getLink() != null) {
448: struct.put("link", Utilities.escapeHTML(entry.getLink()));
449: }
450: struct.put("description", entry.getText());
451: if (entry.getPubTime() != null) {
452: struct.put("pubDate", entry.getPubTime());
453: struct.put("dateCreated", entry.getPubTime());
454: }
455: struct.put("guid", Utilities.escapeHTML(permalink));
456: struct.put("permaLink", Utilities.escapeHTML(permalink));
457: struct.put("postid", entry.getId());
458:
459: struct.put("userid", entry.getCreator().getUserName());
460: struct.put("author", entry.getCreator().getEmailAddress());
461:
462: Vector catArray = new Vector();
463: catArray.addElement(entry.getCategory().getPath());
464: struct.put("categories", catArray);
465:
466: return struct;
467: }
468:
469: private Hashtable createCategoryStruct(WeblogCategoryData category,
470: String userid) {
471:
472: String contextUrl = RollerRuntimeConfig.getAbsoluteContextURL();
473:
474: Hashtable struct = new Hashtable();
475: struct.put("description", category.getPath());
476:
477: String catUrl = contextUrl + "/page/" + userid + "?catname="
478: + category.getPath();
479: catUrl = StringUtils.replace(catUrl, " ", "%20");
480: struct.put("htmlUrl", catUrl);
481:
482: String rssUrl = contextUrl + "/rss/" + userid + "?catname="
483: + category.getPath();
484: rssUrl = StringUtils.replace(catUrl, " ", "%20");
485: struct.put("rssUrl", rssUrl);
486:
487: return struct;
488: }
489:
490: }
|