001/* 002 Licensed to the Apache Software Foundation (ASF) under one 003 or more contributor license agreements. See the NOTICE file 004 distributed with this work for additional information 005 regarding copyright ownership. The ASF licenses this file 006 to you under the Apache License, Version 2.0 (the 007 "License"); you may not use this file except in compliance 008 with the License. You may obtain a copy of the License at 009 010 http://www.apache.org/licenses/LICENSE-2.0 011 012 Unless required by applicable law or agreed to in writing, 013 software distributed under the License is distributed on an 014 "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 KIND, either express or implied. See the License for the 016 specific language governing permissions and limitations 017 under the License. 018 */ 019package org.apache.wiki.xmlrpc; 020 021import org.apache.log4j.Logger; 022import org.apache.wiki.api.core.Attachment; 023import org.apache.wiki.api.core.Context; 024import org.apache.wiki.api.core.ContextEnum; 025import org.apache.wiki.api.core.Engine; 026import org.apache.wiki.api.core.Page; 027import org.apache.wiki.api.spi.Wiki; 028import org.apache.wiki.attachment.AttachmentManager; 029import org.apache.wiki.auth.AuthenticationManager; 030import org.apache.wiki.auth.AuthorizationManager; 031import org.apache.wiki.auth.WikiSecurityException; 032import org.apache.wiki.auth.permissions.PermissionFactory; 033import org.apache.wiki.pages.PageManager; 034import org.apache.wiki.pages.PageTimeComparator; 035import org.apache.wiki.plugin.WeblogEntryPlugin; 036import org.apache.wiki.plugin.WeblogPlugin; 037import org.apache.xmlrpc.XmlRpcException; 038 039import java.io.ByteArrayInputStream; 040import java.util.Date; 041import java.util.Hashtable; 042import java.util.Iterator; 043import java.util.List; 044 045/** 046 * Provides handlers for all RPC routines of the MetaWeblog API. 047 * <P> 048 * JSPWiki does not support categories, and therefore we always return 049 * an empty list for getCategories(). Note also that this API is not 050 * suitable for general Wiki editing, since JSPWiki formats the entries 051 * in a wiki-compatible manner. And you cannot choose your page names 052 * either. Since 2.1.94 the entire MetaWeblog API is supported. 053 * 054 * @since 2.1.7 055 */ 056 057public class MetaWeblogHandler implements WikiRPCHandler { 058 059 private static final Logger log = Logger.getLogger( MetaWeblogHandler.class ); 060 061 private Context m_context; 062 063 /** 064 * {@inheritDoc} 065 */ 066 @Override 067 public void initialize( final Context context ) 068 { 069 m_context = context; 070 } 071 072 /** 073 * Does a quick check against the current user 074 * and does he have permissions to do the stuff 075 * that he really wants to. 076 * <p> 077 * If there is no authentication enabled, returns normally. 078 * 079 * @throws XmlRpcException with the correct error message, if auth fails. 080 */ 081 private void checkPermissions( final Page page, 082 final String username, 083 final String password, 084 final String permission ) throws XmlRpcException { 085 try { 086 final AuthenticationManager amm = m_context.getEngine().getManager( AuthenticationManager.class ); 087 final AuthorizationManager mgr = m_context.getEngine().getManager( AuthorizationManager.class ); 088 089 if( amm.login( m_context.getWikiSession(), m_context.getHttpRequest(), username, password ) ) { 090 if( !mgr.checkPermission( m_context.getWikiSession(), PermissionFactory.getPagePermission( page, permission ) ) ) { 091 throw new XmlRpcException( 1, "No permission" ); 092 } 093 } else { 094 throw new XmlRpcException( 1, "Unknown login" ); 095 } 096 } catch( final WikiSecurityException e ) { 097 throw new XmlRpcException( 1, e.getMessage(), e ); 098 } 099 } 100 101 /** 102 * JSPWiki does not support categories, therefore JSPWiki always returns an empty list for categories. 103 * 104 * @param blogid The id of the blog. 105 * @param username The username to use 106 * @param password The password 107 * @throws XmlRpcException If something goes wrong 108 * @return An empty hashtable. 109 */ 110 public Hashtable< Object, Object > getCategories( final String blogid, final String username, final String password ) throws XmlRpcException { 111 final Page page = m_context.getEngine().getManager( PageManager.class ).getPage( blogid ); 112 checkPermissions( page, username, password, "view" ); 113 return new Hashtable<>(); 114 } 115 116 private String getURL( final String page ) { 117 return m_context.getEngine().getURL( ContextEnum.PAGE_VIEW.getRequestContext(), page,null ); 118 } 119 120 /** 121 * Takes a wiki page, and creates a metaWeblog struct out of it. 122 * 123 * @param page The actual entry page 124 * @return A metaWeblog entry struct. 125 */ 126 private Hashtable< String,Object > makeEntry( final Page page ) { 127 final Page firstVersion = m_context.getEngine().getManager( PageManager.class ).getPage( page.getName(), 1 ); 128 final Hashtable< String, Object > ht = new Hashtable<>(); 129 ht.put( "dateCreated", firstVersion.getLastModified() ); 130 ht.put( "link", getURL(page.getName() ) ); 131 ht.put( "permaLink", getURL(page.getName() ) ); 132 ht.put( "postid", page.getName() ); 133 ht.put( "userid", page.getAuthor() ); 134 135 final String pageText = m_context.getEngine().getManager( PageManager.class ).getText(page.getName()); 136 final int firstLine = pageText.indexOf('\n'); 137 138 String title = ""; 139 if( firstLine > 0 ) { 140 title = pageText.substring( 0, firstLine ); 141 } 142 143 if( title.trim().length() == 0 ) { 144 title = page.getName(); 145 } 146 147 // Remove wiki formatting 148 while( title.startsWith("!") ) { 149 title = title.substring(1); 150 } 151 152 ht.put("title", title); 153 ht.put("description", pageText); 154 155 return ht; 156 } 157 158 /** 159 * Returns a list of the recent posts to this weblog. 160 * 161 * @param blogid The id of the blog. 162 * @param username The username to use 163 * @param password The password 164 * @param numberOfPosts How many posts to find 165 * @throws XmlRpcException If something goes wrong 166 * @return As per MetaweblogAPI specification 167 */ 168 // FIXME: The implementation is suboptimal, as it goes through all of the blog entries. 169 public Hashtable getRecentPosts( final String blogid, final String username, final String password, final int numberOfPosts ) throws XmlRpcException { 170 final Hashtable<String, Hashtable<String, Object>> result = new Hashtable<>(); 171 log.info( "metaWeblog.getRecentPosts() called"); 172 final Page page = m_context.getEngine().getManager( PageManager.class ).getPage( blogid ); 173 checkPermissions( page, username, password, "view" ); 174 175 final WeblogPlugin plugin = new WeblogPlugin(); 176 final List< Page > changed = plugin.findBlogEntries( m_context.getEngine(), blogid, new Date( 0L ), new Date() ); 177 changed.sort( new PageTimeComparator() ); 178 179 int items = 0; 180 for( final Iterator< Page > i = changed.iterator(); i.hasNext() && items < numberOfPosts; items++ ) { 181 final Page p = i.next(); 182 result.put( "entry", makeEntry( p ) ); 183 } 184 185 return result; 186 } 187 188 /** 189 * Adds a new post to the blog. 190 * 191 * @param blogid The id of the blog. 192 * @param username The username to use 193 * @param password The password 194 * @param content As per Metaweblogapi contract 195 * @param publish This parameter is ignored for JSPWiki. 196 * @return Returns an empty string 197 * @throws XmlRpcException If something goes wrong 198 */ 199 public String newPost( final String blogid, 200 final String username, 201 final String password, 202 final Hashtable< String, Object > content, 203 final boolean publish ) throws XmlRpcException { 204 log.info("metaWeblog.newPost() called"); 205 final Engine engine = m_context.getEngine(); 206 final Page page = engine.getManager( PageManager.class ).getPage( blogid ); 207 checkPermissions( page, username, password, "createPages" ); 208 209 try { 210 final WeblogEntryPlugin plugin = new WeblogEntryPlugin(); 211 final String pageName = plugin.getNewEntryPage( engine, blogid ); 212 final Page entryPage = Wiki.contents().page( engine, pageName ); 213 entryPage.setAuthor( username ); 214 215 final Context context = Wiki.context().create( engine, entryPage ); 216 final StringBuilder text = new StringBuilder(); 217 text.append( "!" ).append( content.get( "title" ) ); 218 text.append( "\n\n" ); 219 text.append( content.get("description") ); 220 221 log.debug("Writing entry: "+text); 222 223 engine.getManager( PageManager.class ).saveText( context, text.toString() ); 224 } catch( final Exception e ) { 225 log.error("Failed to create weblog entry",e); 226 throw new XmlRpcException( 0, "Failed to create weblog entry: "+e.getMessage() ); 227 } 228 229 return ""; // FIXME: 230 } 231 232 /** 233 * Creates an attachment and adds it to the blog. The attachment 234 * is created into the main blog page, not the actual post page, 235 * because we do not know it at this point. 236 * 237 * @param blogid The id of the blog. 238 * @param username The username to use 239 * @param password The password 240 * @param content As per the MetaweblogAPI contract 241 * @return As per the MetaweblogAPI contract 242 * @throws XmlRpcException If something goes wrong 243 */ 244 public Hashtable< String, Object > newMediaObject( final String blogid, 245 final String username, 246 final String password, 247 final Hashtable< String, Object > content ) throws XmlRpcException { 248 final Engine engine = m_context.getEngine(); 249 final String url; 250 251 log.info( "metaWeblog.newMediaObject() called" ); 252 253 final Page page = engine.getManager( PageManager.class ).getPage( blogid ); 254 checkPermissions( page, username, password, "upload" ); 255 256 final String name = (String) content.get( "name" ); 257 final byte[] data = (byte[]) content.get( "bits" ); 258 259 final AttachmentManager attmgr = engine.getManager( AttachmentManager.class ); 260 261 try { 262 final Attachment att = Wiki.contents().attachment( engine, blogid, name ); 263 att.setAuthor( username ); 264 attmgr.storeAttachment( att, new ByteArrayInputStream( data ) ); 265 266 url = engine.getURL( ContextEnum.PAGE_ATTACH.getRequestContext(), att.getName(), null ); 267 } catch( final Exception e ) { 268 log.error( "Failed to upload attachment", e ); 269 throw new XmlRpcException( 0, "Failed to upload media object: "+e.getMessage() ); 270 } 271 272 final Hashtable< String, Object > result = new Hashtable<>(); 273 result.put("url", url); 274 275 return result; 276 } 277 278 279 /** 280 * Allows the user to edit a post. It does not allow general editability of wiki pages, because of the limitations of the metaWeblog API. 281 */ 282 boolean editPost( final String postid, 283 final String username, 284 final String password, 285 final Hashtable< String,Object > content, 286 final boolean publish ) throws XmlRpcException { 287 final Engine engine = m_context.getEngine(); 288 log.info("metaWeblog.editPost("+postid+") called"); 289 290 // FIXME: Is postid correct? Should we determine it from the page name? 291 final Page page = engine.getManager( PageManager.class ).getPage( postid ); 292 checkPermissions( page, username, password, "edit" ); 293 294 try { 295 final Page entryPage = page.clone(); 296 entryPage.setAuthor( username ); 297 298 final Context context = Wiki.context().create( engine, entryPage ); 299 300 final StringBuilder text = new StringBuilder(); 301 text.append( "!" ).append( content.get( "title" ) ); 302 text.append( "\n\n" ); 303 text.append( content.get("description") ); 304 305 log.debug("Updating entry: "+text); 306 307 engine.getManager( PageManager.class ).saveText( context, text.toString() ); 308 } catch( final Exception e ) { 309 log.error("Failed to create weblog entry",e); 310 throw new XmlRpcException( 0, "Failed to update weblog entry: "+e.getMessage() ); 311 } 312 313 return true; 314 } 315 316 /** 317 * Gets the text of any page. The title of the page is parsed 318 * (if any is provided). 319 */ 320 Hashtable< String, Object > getPost( final String postid, final String username, final String password ) throws XmlRpcException { 321 final String wikiname = "FIXME"; 322 final Page page = m_context.getEngine().getManager( PageManager.class ).getPage( wikiname ); 323 checkPermissions( page, username, password, "view" ); 324 return makeEntry( page ); 325 } 326 327}