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 java.util.Calendar;
022import java.util.Collection;
023import java.util.Date;
024import java.util.Hashtable;
025import java.util.Iterator;
026import java.util.Vector;
027
028import org.apache.wiki.LinkCollector;
029import org.apache.wiki.WikiContext;
030import org.apache.wiki.WikiEngine;
031import org.apache.wiki.WikiPage;
032import org.apache.wiki.attachment.Attachment;
033import org.apache.wiki.auth.permissions.PagePermission;
034import org.apache.wiki.auth.permissions.PermissionFactory;
035import org.apache.xmlrpc.XmlRpcException;
036
037/**
038 *  Provides handlers for all RPC routines.  These routines are used by
039 *  the UTF-8 interface.
040 *
041 *  @since 1.6.13
042 */
043
044public class RPCHandlerUTF8
045    extends AbstractRPCHandler
046{
047    public String getApplicationName()
048    {
049        checkPermission( PagePermission.VIEW );
050        
051        return m_engine.getApplicationName();
052    }
053
054    public Vector getAllPages()
055    {
056        checkPermission( PagePermission.VIEW );
057        
058        Collection pages = m_engine.getRecentChanges();
059        Vector<String> result = new Vector<String>();
060
061        for( Iterator i = pages.iterator(); i.hasNext(); )
062        {
063            WikiPage p = (WikiPage) i.next();
064
065            if( !(p instanceof Attachment) )
066            {
067                result.add( p.getName() );
068            }
069        }
070
071        return result;
072    }
073
074    /**
075     *  Encodes a single wiki page info into a Hashtable.
076     */
077    protected Hashtable<String, Object> encodeWikiPage( WikiPage page )
078    {
079        Hashtable<String, Object> ht = new Hashtable<String, Object>();
080
081        ht.put( "name", page.getName() );
082
083        Date d = page.getLastModified();
084
085        //
086        //  Here we reset the DST and TIMEZONE offsets of the
087        //  calendar.  Unfortunately, I haven't thought of a better
088        //  way to ensure that we're getting the proper date
089        //  from the XML-RPC thingy, except to manually adjust the date.
090        //
091
092        Calendar cal = Calendar.getInstance();
093        cal.setTime( d );
094        cal.add( Calendar.MILLISECOND, 
095                 - (cal.get( Calendar.ZONE_OFFSET ) + 
096                    (cal.getTimeZone().inDaylightTime( d ) ? cal.get( Calendar.DST_OFFSET ) : 0 )) );
097
098        ht.put( "lastModified", cal.getTime() );
099        ht.put( "version", page.getVersion() );
100
101        if( page.getAuthor() != null )
102        {
103            ht.put( "author", page.getAuthor() );
104        }
105
106        return ht;
107    }
108
109    public Vector getRecentChanges( Date since )
110    {
111        checkPermission( PagePermission.VIEW );
112        
113        Collection pages = m_engine.getRecentChanges();
114        Vector<Hashtable<String, Object>> result = new Vector<Hashtable<String, Object>>();
115
116        Calendar cal = Calendar.getInstance();
117        cal.setTime( since );
118
119        //
120        //  Convert UTC to our time.
121        //
122        cal.add( Calendar.MILLISECOND,
123                 (cal.get( Calendar.ZONE_OFFSET ) +
124                  (cal.getTimeZone().inDaylightTime(since) ? cal.get( Calendar.DST_OFFSET ) : 0 ) ) );
125        since = cal.getTime();
126
127        for( Iterator i = pages.iterator(); i.hasNext(); )
128        {
129            WikiPage page = (WikiPage)i.next();
130
131            if( page.getLastModified().after( since ) && !(page instanceof Attachment) )
132            {
133                result.add( encodeWikiPage( page ) );
134            }
135        }
136
137        return result;
138    }
139
140    /**
141     *  Simple helper method, turns the incoming page name into
142     *  normal Java string, then checks page condition.
143     *
144     *  @param pagename Page Name as an RPC string (URL-encoded UTF-8)
145     *  @return Real page name, as Java string.
146     *  @throws XmlRpcException, if there is something wrong with the page.
147     */
148    private String parsePageCheckCondition( String pagename )
149        throws XmlRpcException
150    {
151        if( !m_engine.pageExists(pagename) )
152        {
153            throw new XmlRpcException( ERR_NOPAGE, "No such page '"+pagename+"' found, o master." );
154        }
155
156        WikiPage p = m_engine.getPage( pagename );
157        
158        checkPermission( PermissionFactory.getPagePermission( p, PagePermission.VIEW_ACTION ) );
159        return pagename;
160    }
161
162    public Hashtable getPageInfo( String pagename )
163        throws XmlRpcException
164    {
165        pagename = parsePageCheckCondition( pagename );
166        
167        return encodeWikiPage( m_engine.getPage(pagename) );
168    }
169
170    public Hashtable getPageInfoVersion( String pagename, int version )
171        throws XmlRpcException
172    {
173        pagename = parsePageCheckCondition( pagename );
174
175        return encodeWikiPage( m_engine.getPage( pagename, version ) );
176    }
177
178    public String getPage( String pagename )
179        throws XmlRpcException
180    {
181        pagename = parsePageCheckCondition( pagename );
182
183        String text = m_engine.getPureText( pagename, -1 );
184
185        return text;
186    }
187
188    public String getPageVersion( String pagename, int version )
189        throws XmlRpcException
190    {
191        pagename = parsePageCheckCondition( pagename );
192
193        return m_engine.getPureText( pagename, version );
194    }
195
196    public String getPageHTML( String pagename )
197        throws XmlRpcException    
198    {
199        pagename = parsePageCheckCondition( pagename );
200
201        return m_engine.getHTML( pagename );
202    }
203
204    public String getPageHTMLVersion( String pagename, int version )
205        throws XmlRpcException
206    {
207        pagename = parsePageCheckCondition( pagename );
208
209        return m_engine.getHTML( pagename, version );
210    }
211
212    public Vector listLinks( String pagename )
213        throws XmlRpcException
214    {
215        pagename = parsePageCheckCondition( pagename );
216
217        WikiPage page = m_engine.getPage( pagename );
218        String pagedata = m_engine.getPureText( page );
219
220        LinkCollector localCollector = new LinkCollector();
221        LinkCollector extCollector   = new LinkCollector();
222        LinkCollector attCollector   = new LinkCollector();
223
224        WikiContext context = new WikiContext( m_engine, page );
225        context.setVariable( WikiEngine.PROP_REFSTYLE, "absolute" );
226
227        m_engine.textToHTML( context,
228                             pagedata,
229                             localCollector,
230                             extCollector,
231                             attCollector );
232
233        Vector<Hashtable<String, String>> result = new Vector<Hashtable<String, String>>();
234
235        // FIXME: Contains far too much common with RPCHandler.  Refactor!
236
237        //
238        //  Add local links.
239        //
240        for( Iterator< String > i = localCollector.getLinks().iterator(); i.hasNext(); )
241        {
242            String link = i.next();
243            Hashtable< String, String > ht = new Hashtable< String, String >();
244            ht.put( "page", link );
245            ht.put( "type", LINK_LOCAL );
246
247            if( m_engine.pageExists(link) )
248            {
249                ht.put( "href", context.getViewURL(link) );
250            }
251            else
252            {
253                ht.put( "href", context.getURL(WikiContext.EDIT,link) );
254            }
255
256            result.add( ht );
257        }
258
259        //
260        // Add links to inline attachments
261        //
262        for( Iterator i = attCollector.getLinks().iterator(); i.hasNext(); )
263        {
264            String link = (String) i.next();
265
266            Hashtable<String, String> ht = new Hashtable<String, String>();
267
268            ht.put( "page", link );
269            ht.put( "type", LINK_LOCAL );
270            ht.put( "href", context.getURL(WikiContext.ATTACH,link) );
271
272            result.add( ht );
273        }
274
275        //
276        // External links don't need to be changed into XML-RPC strings,
277        // simply because URLs are by definition ASCII.
278        //
279
280        for( Iterator i = extCollector.getLinks().iterator(); i.hasNext(); )
281        {
282            String link = (String) i.next();
283
284            Hashtable<String, String> ht = new Hashtable<String, String>();
285
286            ht.put( "page", link );
287            ht.put( "type", LINK_EXTERNAL );
288            ht.put( "href", link );
289
290            result.add( ht );
291        }
292
293        return result;
294    }
295}