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    }