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 */
019 package org.apache.wiki.xmlrpc;
020
021 import java.io.ByteArrayInputStream;
022 import java.util.Collections;
023 import java.util.Date;
024 import java.util.Hashtable;
025 import java.util.Iterator;
026 import java.util.List;
027
028 import org.apache.log4j.Logger;
029 import org.apache.wiki.WikiContext;
030 import org.apache.wiki.WikiEngine;
031 import org.apache.wiki.WikiPage;
032 import org.apache.wiki.api.exceptions.ProviderException;
033 import org.apache.wiki.attachment.Attachment;
034 import org.apache.wiki.attachment.AttachmentManager;
035 import org.apache.wiki.auth.AuthenticationManager;
036 import org.apache.wiki.auth.AuthorizationManager;
037 import org.apache.wiki.auth.WikiSecurityException;
038 import org.apache.wiki.auth.permissions.PermissionFactory;
039 import org.apache.wiki.plugin.WeblogEntryPlugin;
040 import org.apache.wiki.plugin.WeblogPlugin;
041 import org.apache.wiki.util.comparators.PageTimeComparator;
042 import org.apache.xmlrpc.XmlRpcException;
043
044 /**
045 * Provides handlers for all RPC routines of the MetaWeblog API.
046 * <P>
047 * JSPWiki does not support categories, and therefore we always return
048 * an empty list for getCategories(). Note also that this API is not
049 * suitable for general Wiki editing, since JSPWiki formats the entries
050 * in a wiki-compatible manner. And you cannot choose your page names
051 * either. Since 2.1.94 the entire MetaWeblog API is supported.
052 *
053 * @since 2.1.7
054 */
055
056 public class MetaWeblogHandler
057 implements WikiRPCHandler
058 {
059 private static Logger log = Logger.getLogger( MetaWeblogHandler.class );
060
061 private WikiContext m_context;
062
063 /**
064 * {@inheritDoc}
065 */
066 public void initialize( WikiContext context )
067 {
068 m_context = context;
069 }
070
071 /**
072 * Does a quick check against the current user
073 * and does he have permissions to do the stuff
074 * that he really wants to.
075 * <p>
076 * If there is no authentication enabled, returns normally.
077 *
078 * @throw XmlRpcException with the correct error message, if auth fails.
079 */
080 private void checkPermissions( WikiPage page,
081 String username,
082 String password,
083 String permission )
084 throws XmlRpcException
085 {
086 try
087 {
088 AuthenticationManager amm = m_context.getEngine().getAuthenticationManager();
089 AuthorizationManager mgr = m_context.getEngine().getAuthorizationManager();
090
091 if( amm.login( m_context.getWikiSession(), m_context.getHttpRequest(), username, password ) )
092 {
093 if( !mgr.checkPermission( m_context.getWikiSession(), PermissionFactory.getPagePermission( page, permission ) ))
094 {
095 throw new XmlRpcException( 1, "No permission" );
096 }
097 }
098 else
099 {
100 throw new XmlRpcException( 1, "Unknown login" );
101 }
102 }
103 catch( WikiSecurityException e )
104 {
105 throw new XmlRpcException( 1, e.getMessage(), e );
106 }
107 return;
108 }
109
110 /**
111 * JSPWiki does not support categories, therefore JSPWiki
112 * always returns an empty list for categories.
113 *
114 * @param blogid The id of the blog.
115 * @param username The username to use
116 * @param password The password
117 * @throws XmlRpcException If something goes wrong
118 * @return An empty hashtable.
119 */
120 public Hashtable getCategories( String blogid,
121 String username,
122 String password )
123 throws XmlRpcException
124 {
125 WikiPage page = m_context.getEngine().getPage( blogid );
126
127 checkPermissions( page, username, password, "view" );
128
129 Hashtable ht = new Hashtable();
130
131 return ht;
132 }
133
134 private String getURL( String page )
135 {
136 return m_context.getEngine().getURL( WikiContext.VIEW,
137 page,
138 null,
139 true ); // Force absolute urls
140 }
141
142 /**
143 * Takes a wiki page, and creates a metaWeblog struct
144 * out of it.
145 * @param page The actual entry page
146 * @return A metaWeblog entry struct.
147 */
148 private Hashtable<String,Object> makeEntry( WikiPage page )
149 {
150 Hashtable<String, Object> ht = new Hashtable<String, Object>();
151
152 WikiPage firstVersion = m_context.getEngine().getPage( page.getName(), 1 );
153
154 ht.put("dateCreated", firstVersion.getLastModified());
155 ht.put("link", getURL(page.getName()));
156 ht.put("permaLink", getURL(page.getName()));
157 ht.put("postid", page.getName());
158 ht.put("userid", page.getAuthor());
159
160 String pageText = m_context.getEngine().getText(page.getName());
161 String title = "";
162 int firstLine = pageText.indexOf('\n');
163
164 if( firstLine > 0 )
165 {
166 title = pageText.substring( 0, firstLine );
167 }
168
169 if( title.trim().length() == 0 ) title = page.getName();
170
171 // Remove wiki formatting
172 while( title.startsWith("!") ) title = title.substring(1);
173
174 ht.put("title", title);
175 ht.put("description", pageText);
176
177 return ht;
178 }
179
180 /**
181 * Returns a list of the recent posts to this weblog.
182 *
183 * @param blogid The id of the blog.
184 * @param username The username to use
185 * @param password The password
186 * @param numberOfPosts How many posts to find
187 * @throws XmlRpcException If something goes wrong
188 * @return As per MetaweblogAPI specification
189 */
190
191 // FIXME: The implementation is suboptimal, as it
192 // goes through all of the blog entries.
193
194 @SuppressWarnings("unchecked")
195 public Hashtable getRecentPosts( String blogid,
196 String username,
197 String password,
198 int numberOfPosts)
199 throws XmlRpcException
200 {
201 Hashtable<String, Hashtable<String, Object>> result = new Hashtable<String, Hashtable<String, Object>>();
202
203 log.info( "metaWeblog.getRecentPosts() called");
204
205 WikiPage page = m_context.getEngine().getPage( blogid );
206
207 checkPermissions( page, username, password, "view" );
208
209 try
210 {
211 WeblogPlugin plugin = new WeblogPlugin();
212
213 List<WikiPage> changed = plugin.findBlogEntries(m_context.getEngine().getPageManager(),
214 blogid,
215 new Date(0L),
216 new Date());
217
218 Collections.sort( changed, new PageTimeComparator() );
219
220 int items = 0;
221 for( Iterator i = changed.iterator(); i.hasNext() && items < numberOfPosts; items++ )
222 {
223 WikiPage p = (WikiPage) i.next();
224
225 result.put( "entry", makeEntry( p ) );
226 }
227
228 }
229 catch( ProviderException e )
230 {
231 log.error( "Failed to list recent posts", e );
232
233 throw new XmlRpcException( 0, e.getMessage() );
234 }
235
236 return result;
237 }
238
239 /**
240 * Adds a new post to the blog.
241 *
242 * @param blogid The id of the blog.
243 * @param username The username to use
244 * @param password The password
245 * @param content As per Metaweblogapi contract
246 * @param publish This parameter is ignored for JSPWiki.
247 * @return Returns an empty string
248 * @throws XmlRpcException If something goes wrong
249 */
250 public String newPost( String blogid,
251 String username,
252 String password,
253 Hashtable content,
254 boolean publish )
255 throws XmlRpcException
256 {
257 log.info("metaWeblog.newPost() called");
258 WikiEngine engine = m_context.getEngine();
259
260 WikiPage page = engine.getPage( blogid );
261 checkPermissions( page, username, password, "createPages" );
262
263 try
264 {
265 WeblogEntryPlugin plugin = new WeblogEntryPlugin();
266
267 String pageName = plugin.getNewEntryPage( engine, blogid );
268
269 WikiPage entryPage = new WikiPage( engine, pageName );
270 entryPage.setAuthor( username );
271
272 WikiContext context = new WikiContext( engine, entryPage );
273
274 StringBuffer text = new StringBuffer();
275 text.append( "!"+content.get("title") );
276 text.append( "\n\n" );
277 text.append( content.get("description") );
278
279 log.debug("Writing entry: "+text);
280
281 engine.saveText( context, text.toString() );
282 }
283 catch( Exception e )
284 {
285 log.error("Failed to create weblog entry",e);
286 throw new XmlRpcException( 0, "Failed to create weblog entry: "+e.getMessage() );
287 }
288
289 return ""; // FIXME:
290 }
291
292 /**
293 * Creates an attachment and adds it to the blog. The attachment
294 * is created into the main blog page, not the actual post page,
295 * because we do not know it at this point.
296 *
297 * @param blogid The id of the blog.
298 * @param username The username to use
299 * @param password The password
300 * @param content As per the MetaweblogAPI contract
301 * @return As per the MetaweblogAPI contract
302 * @throws XmlRpcException If something goes wrong
303 *
304 */
305 public Hashtable newMediaObject( String blogid,
306 String username,
307 String password,
308 Hashtable content )
309 throws XmlRpcException
310 {
311 WikiEngine engine = m_context.getEngine();
312 String url = "";
313
314 log.info("metaWeblog.newMediaObject() called");
315
316 WikiPage page = engine.getPage( blogid );
317 checkPermissions( page, username, password, "upload" );
318
319 String name = (String) content.get( "name" );
320 byte[] data = (byte[]) content.get( "bits" );
321
322 AttachmentManager attmgr = engine.getAttachmentManager();
323
324 try
325 {
326 Attachment att = new Attachment( engine, blogid, name );
327 att.setAuthor( username );
328 attmgr.storeAttachment( att, new ByteArrayInputStream( data ) );
329
330 url = engine.getURL( WikiContext.ATTACH, att.getName(), null, true );
331 }
332 catch( Exception e )
333 {
334 log.error( "Failed to upload attachment", e );
335 throw new XmlRpcException( 0, "Failed to upload media object: "+e.getMessage() );
336 }
337
338 Hashtable<String, Object> result = new Hashtable<String, Object>();
339 result.put("url", url);
340
341 return result;
342 }
343
344
345 /**
346 * Allows the user to edit a post. It does not allow general
347 * editability of wiki pages, because of the limitations of the
348 * metaWeblog API.
349 */
350 boolean editPost( String postid,
351 String username,
352 String password,
353 Hashtable content,
354 boolean publish )
355 throws XmlRpcException
356 {
357 WikiEngine engine = m_context.getEngine();
358 log.info("metaWeblog.editPost("+postid+") called");
359
360 // FIXME: Is postid correct? Should we determine it from the page name?
361 WikiPage page = engine.getPage( postid );
362 checkPermissions( page, username, password, "edit" );
363
364 try
365 {
366 WikiPage entryPage = (WikiPage)page.clone();
367 entryPage.setAuthor( username );
368
369 WikiContext context = new WikiContext( engine, entryPage );
370
371 StringBuffer text = new StringBuffer();
372 text.append( "!"+content.get("title") );
373 text.append( "\n\n" );
374 text.append( content.get("description") );
375
376 log.debug("Updating entry: "+text);
377
378 engine.saveText( context, text.toString() );
379 }
380 catch( Exception e )
381 {
382 log.error("Failed to create weblog entry",e);
383 throw new XmlRpcException( 0, "Failed to update weblog entry: "+e.getMessage() );
384 }
385
386 return true;
387 }
388
389 /**
390 * Gets the text of any page. The title of the page is parsed
391 * (if any is provided).
392 */
393 Hashtable getPost( String postid,
394 String username,
395 String password )
396 throws XmlRpcException
397 {
398 String wikiname = "FIXME";
399
400 WikiPage page = m_context.getEngine().getPage( wikiname );
401
402 checkPermissions( page, username, password, "view" );
403
404 return makeEntry( page );
405 }
406 }