You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jspwiki.apache.org by aj...@apache.org on 2008/02/13 06:54:24 UTC

svn commit: r627255 [31/41] - in /incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src: ./ com/ com/ecyrd/ com/ecyrd/jspwiki/ com/ecyrd/jspwiki/action/ com/ecyrd/jspwiki/attachment/ com/ecyrd/jspwiki/auth/ com/ecyrd/jspwiki/auth/acl/ com/ecyrd/jspwiki...

Added: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/rpc/atom/AtomAPIServlet.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/rpc/atom/AtomAPIServlet.java?rev=627255&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/rpc/atom/AtomAPIServlet.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/rpc/atom/AtomAPIServlet.java Tue Feb 12 21:53:55 2008
@@ -0,0 +1,332 @@
+/*
+    JSPWiki - a JSP-based WikiWiki clone.
+
+    Copyright (C) 2001-2004 Janne Jalkanen (Janne.Jalkanen@iki.fi)
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published by
+    the Free Software Foundation; either version 2.1 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+package com.ecyrd.jspwiki.rpc.atom;
+
+import java.io.*;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import java.util.Date;
+import java.util.Collection;
+import java.util.Iterator;
+import org.apache.log4j.Logger;
+import org.intabulas.sandler.*;
+import org.intabulas.sandler.exceptions.*;
+import org.intabulas.sandler.elements.*;
+
+import com.ecyrd.jspwiki.*;
+import com.ecyrd.jspwiki.util.*;
+
+import com.ecyrd.jspwiki.plugin.WeblogEntryPlugin;
+import com.ecyrd.jspwiki.plugin.WeblogPlugin;
+import com.ecyrd.jspwiki.providers.ProviderException;
+
+/**
+ *  Handles incoming requests for the Atom API.  This class uses the
+ *  "sandler" Atom API implementation.
+ *
+ *  @author Janne Jalkanen
+ *  @since 2.1.97
+ */
+public class AtomAPIServlet extends HttpServlet
+{
+    static final Logger log = Logger.getLogger( AtomAPIServlet.class );
+
+    private static final long serialVersionUID = 0L;
+
+    private WikiEngine       m_engine;
+
+    /**
+     *  Initializes the servlet.
+     */
+    public void init( ServletConfig config )
+        throws ServletException
+    {
+        m_engine = WikiEngine.getInstance( config );
+    }
+
+    /**
+     *  Takes the name of the page from the request URI.
+     *  The initial slash is also removed.  If there is no page,
+     *  returns null.
+     */
+    private String getPageName( HttpServletRequest request )
+    {
+        String name = request.getPathInfo();
+
+        if( name == null || name.length() <= 1 )
+        {
+            return null;
+        }
+        else if( name.charAt(0) == '/' )
+        {
+            name = name.substring(1);
+        }
+
+        name = TextUtil.urlDecodeUTF8( name );
+
+        return name;
+    }
+
+    /**
+     *  Implements the PostURI of the Atom spec.
+     *  <p>
+     *  Implementation notes:
+     *  <ul>
+     *   <li>Only fetches the first content.  All other contents are ignored.
+     *   <li>Assumes that incoming code is plain text or WikiMarkup, not html.
+     *  </ul>
+     */
+    public void doPost( HttpServletRequest request, HttpServletResponse response )
+        throws ServletException
+    {
+        log.debug("Received POST to AtomAPIServlet");
+
+        try
+        {
+            String blogid = getPageName( request );
+
+            WikiPage page    = m_engine.getPage( blogid );
+
+            if( page == null )
+            {
+                throw new ServletException("Page "+blogid+" does not exist, cannot add blog post.");
+            }
+
+            //FIXME: Do authentication here
+            Entry entry = Sandler.unmarshallEntry( request.getInputStream() );
+
+            //
+            //  Fetch the obligatory parts of the content.
+            //
+            Content title   = entry.getTitle();
+            Content content = entry.getContent(0);
+
+            Person  author  = entry.getAuthor();
+
+            //FIXME: Sandler 0.5 does not support generator
+
+            //
+            //  Generate new blog entry.
+            //
+            WeblogEntryPlugin plugin = new WeblogEntryPlugin();
+
+            String pageName = plugin.getNewEntryPage( m_engine, blogid );
+            String username = author.getName();
+
+            WikiPage entryPage = new WikiPage( m_engine, pageName );
+            entryPage.setAuthor( username );
+
+            WikiContext context = m_engine.getWikiActionBeanFactory().newViewActionBean( request, response, entryPage );
+
+            StringBuffer text = new StringBuffer();
+            text.append( "!"+title.getBody() );
+            text.append( "\n\n" );
+            text.append( content.getBody() );
+
+            log.debug("Writing entry: "+text);
+
+            m_engine.saveText( context, text.toString() );
+
+        }
+        catch( FeedMarshallException e )
+        {
+            log.error("Received faulty Atom entry",e);
+            throw new ServletException("Faulty Atom entry",e);
+        }
+        catch( IOException e )
+        {
+            log.error("I/O exception",e);
+            throw new ServletException("Could not get body of request",e);
+        }
+        catch( WikiException e )
+        {
+            log.error("Provider exception while posting",e);
+            throw new ServletException("JSPWiki cannot save the entry",e);
+        }
+    }
+
+    /**
+     *  Handles HTTP GET.  However, we do not respond to GET requests,
+     *  other than to show an explanatory text.
+     */
+    public void doGet( HttpServletRequest request, HttpServletResponse response )
+        throws ServletException
+    {
+        log.debug("Received HTTP GET to AtomAPIServlet");
+
+        String blogid = getPageName( request );
+
+        log.debug("Requested page "+blogid);
+
+        try
+        {
+            if( blogid == null )
+            {
+                Feed feed = listBlogs();
+
+                response.setContentType("application/x.atom+xml; charset=UTF-8");
+                response.getWriter().println( Sandler.marshallFeed(feed) );
+
+                response.getWriter().flush();
+            }
+            else
+            {
+                Entry entry = getBlogEntry( blogid );
+
+                response.setContentType("application/x.atom+xml; charset=UTF-8");
+                response.getWriter().println( Sandler.marshallEntry(entry) );
+
+                response.getWriter().flush();
+            }
+        }
+        catch( Exception e )
+        {
+            log.error("Unable to generate response",e);
+            throw new ServletException("Internal problem - whack Janne on the head to get a better error report",e);
+        }
+
+    }
+
+    private Entry getBlogEntry( String entryid )
+        throws ProviderException
+    {
+        WikiPage page = m_engine.getPage( entryid );
+        WikiPage firstVersion = m_engine.getPage( entryid, 1 );
+
+        Entry entry = SyndicationFactory.newSyndicationEntry();
+
+        String pageText = m_engine.getText(page.getName());
+        String title = "";
+        int firstLine = pageText.indexOf('\n');
+
+        if( firstLine > 0 )
+        {
+            title = pageText.substring( 0, firstLine );
+        }
+
+        if( title.trim().length() == 0 ) title = page.getName();
+
+        // Remove wiki formatting
+        while( title.startsWith("!") ) title = title.substring(1);
+
+        entry.setTitle( title );
+        entry.setCreated( firstVersion.getLastModified() );
+        entry.setModified( page.getLastModified() );
+        entry.setAuthor( SyndicationFactory.createPerson( page.getAuthor(),
+                                                          null,
+                                                          null ) );
+
+        entry.addContent( SyndicationFactory.createEscapedContent(pageText) );
+
+        return entry;
+    }
+
+    /**
+     *  Creates and outputs a full list of all available blogs
+     */
+    private Feed listBlogs()
+        throws ProviderException,
+               IOException
+    {
+        Collection pages = m_engine.getPageManager().getAllPages();
+
+        Feed feed = SyndicationFactory.newSyndicationFeed();
+        feed.setTitle("List of blogs at this site");
+        feed.setModified( new Date() );
+
+        for( Iterator i = pages.iterator(); i.hasNext(); )
+        {
+            WikiPage p = (WikiPage) i.next();
+
+            //
+            //  List only weblogs
+            //  FIXME: Unfortunately, a weblog is not known until it has
+            //         been executed once, because plugins are off during
+            //         the initial startup phase.
+            //
+
+            log.debug( p.getName()+" = "+p.getAttribute(WeblogPlugin.ATTR_ISWEBLOG)) ;
+
+            if( !("true".equals(p.getAttribute(WeblogPlugin.ATTR_ISWEBLOG)) ) )
+                continue;
+
+            String encodedName = TextUtil.urlEncodeUTF8( p.getName() );
+
+            WikiContext context = m_engine.getWikiActionBeanFactory().newViewActionBean( p );
+
+            String title = TextUtil.replaceEntities(BlogUtil.getSiteName(context));
+
+            Link postlink = createLink( "service.post",
+                                        m_engine.getBaseURL()+"atom/"+encodedName,
+                                        title );
+
+            Link editlink = createLink( "service.edit",
+                                        m_engine.getBaseURL()+"atom/"+encodedName,
+                                        title );
+
+            Link feedlink = createLink( "service.feed",
+                                        m_engine.getBaseURL()+"atom.jsp?page="+encodedName,
+                                        title );
+
+
+            feed.addLink( postlink );
+            feed.addLink( feedlink );
+            feed.addLink( editlink );
+        }
+
+        return feed;
+    }
+
+    private Link createLink( String rel,
+                             String href,
+                             String title )
+    {
+        org.intabulas.sandler.elements.impl.LinkImpl link = new org.intabulas.sandler.elements.impl.LinkImpl();
+
+        link.setRelationship( rel );
+        link.setTitle( title );
+        link.setType( "application/x.atom+xml" );
+        link.setHref( href );
+
+        return link;
+    }
+
+    /**
+     *
+     */
+    public void doDelete( HttpServletRequest request, HttpServletResponse response )
+        throws ServletException
+    {
+        log.debug("Received HTTP DELETE");
+    }
+
+    /**
+     *
+     */
+    public void doPut( HttpServletRequest request, HttpServletResponse response )
+        throws ServletException
+    {
+        log.debug("Received HTTP PUT");
+    }
+}

Added: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/rpc/json/JSONRPCManager.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/rpc/json/JSONRPCManager.java?rev=627255&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/rpc/json/JSONRPCManager.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/rpc/json/JSONRPCManager.java Tue Feb 12 21:53:55 2008
@@ -0,0 +1,309 @@
+/*
+    JSPWiki - a JSP-based WikiWiki clone.
+
+    Copyright (C) 2001-2007 Janne Jalkanen (Janne.Jalkanen@iki.fi)
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published by
+    the Free Software Foundation; either version 2.1 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+package com.ecyrd.jspwiki.rpc.json;
+
+import java.lang.reflect.Method;
+import java.security.Permission;
+import java.util.HashMap;
+import java.util.Iterator;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+import org.apache.log4j.Logger;
+
+import com.ecyrd.jspwiki.WikiContext;
+import com.ecyrd.jspwiki.WikiEngine;
+import com.ecyrd.jspwiki.WikiSession;
+import com.ecyrd.jspwiki.action.NoneActionBean;
+import com.ecyrd.jspwiki.auth.WikiSecurityException;
+import com.ecyrd.jspwiki.auth.permissions.PagePermission;
+import com.ecyrd.jspwiki.rpc.RPCCallable;
+import com.ecyrd.jspwiki.rpc.RPCManager;
+import com.ecyrd.jspwiki.ui.TemplateManager;
+import com.metaparadigm.jsonrpc.InvocationCallback;
+import com.metaparadigm.jsonrpc.JSONRPCBridge;
+
+/**
+ *  Provides an easy-to-use interface for different modules to AJAX-enable
+ *  themselves.  This class is a static class, so it cannot be instantiated,
+ *  but it easily available from anywhere (including JSP pages).
+ *  <p>
+ *  Any object which wants to expose its methods through JSON calls, needs
+ *  to implement the RPCCallable interface.  JSONRPCManager will expose
+ *  <i>all</i> methods, so be careful which you want to expose.
+ *  <p>
+ *  Due to some limitations of the JSON-RPC library, we do not use the
+ *  Global bridge object. 
+ *  @see com.ecyrd.jspwiki.rpc.RPCCallable
+ *  @author Janne Jalkanen
+ *  @since 2.5.4
+ */
+// FIXME: Must be mootool-ified.
+public final class JSONRPCManager extends RPCManager
+{
+    private static final String JSONRPCBRIDGE = "JSONRPCBridge";
+    private static HashMap c_globalObjects = new HashMap();
+    
+    /** Prevent instantiation */
+    private JSONRPCManager()
+    {
+        super();
+    }
+    
+    /**
+     *  Emits JavaScript to do a JSON RPC Call.  You would use this method e.g.
+     *  in your plugin generation code to embed an AJAX call to your object.
+     *  
+     *  @param context The Wiki Context
+     *  @param c An RPCCallable object
+     *  @param function Name of the method to call
+     *  @param params Parameters to pass to the method
+     *  @return generated JavasSript code snippet that calls the method
+     */
+    public static String emitJSONCall( WikiContext context, RPCCallable c, String function, String params )
+    {
+        StringBuffer sb = new StringBuffer();
+        sb.append("<script>");
+        sb.append("var result = jsonrpc."+getId(c)+"."+function+"("+params+");\r\n");
+        sb.append("document.write(result);\r\n");
+        sb.append("</script>");
+        
+        return sb.toString();        
+    }
+    
+    /**
+     *  Finds this user's personal RPC Bridge.  If it does not exist, will
+     *  create one and put it in the context.  If there is no HTTP Request included,
+     *  returns the global bridge.
+     *  
+     *  @param context WikiContext to find the bridge in
+     *  @return A JSON RPC Bridge
+     */
+    // FIXME: Is returning the global bridge a potential security threat?
+    private static JSONRPCBridge getBridge( WikiContext context )
+    {
+        JSONRPCBridge bridge = null;
+        HttpServletRequest req = context.getHttpRequest();
+        
+        if( req != null )
+        {
+            HttpSession hs = req.getSession();
+            
+            if( hs != null )
+            {
+                bridge = (JSONRPCBridge)hs.getAttribute(JSONRPCBRIDGE);
+                
+                if( bridge == null )
+                {
+                    bridge = new JSONRPCBridge();
+                
+                    hs.setAttribute(JSONRPCBRIDGE, bridge);
+                }
+            }
+        }
+        
+        if( bridge == null) bridge = JSONRPCBridge.getGlobalBridge();
+        bridge.setDebug(false);
+        
+        return bridge;
+    }
+    
+    /**
+     *  Registers a callable to JSON global bridge and requests JSON libraries to be added
+     *  to the page.  
+     *  
+     *  @param context The WikiContext.
+     *  @param c The RPCCallable to register
+     *  @return the ID of the registered callable object
+     */
+    public static String registerJSONObject( WikiContext context, RPCCallable c )
+    {
+        String id = getId(c);
+        getBridge(context).registerObject( id, c );
+
+        requestJSON( context );
+        return id;
+    }
+    
+    /**
+     *  Requests the JSON Javascript and object to be generated in the HTML.
+     *  @param context The WikiContext.
+     */
+    public static void requestJSON( WikiContext context )
+    {
+        TemplateManager.addResourceRequest(context, 
+                                           TemplateManager.RESOURCE_SCRIPT, 
+                                           context.getContext().getURL(NoneActionBean.class,"scripts/json-rpc/jsonrpc.js"));        
+        
+        String jsonurl = context.getContext().getURL( NoneActionBean.class, "JSON-RPC" );
+        TemplateManager.addResourceRequest(context, 
+                                           TemplateManager.RESOURCE_JSFUNCTION, 
+                                           "jsonrpc = new JSONRpcClient(\""+jsonurl+"\");");
+        
+        getBridge(context).registerCallback(new WikiJSONAccessor(), HttpServletRequest.class);
+    }
+    
+    /**
+     *  Provides access control to the JSON calls.  Rather private.
+     *  Unfortunately we have to check the permission every single time, because
+     *  the user can log in and we would need to reset the permissions at that time.
+     *  Note that this is an obvious optimization piece if this becomes
+     *  a bottleneck.
+     *  
+     *  @author Janne Jalkanen
+     */
+    static class WikiJSONAccessor implements InvocationCallback
+    {
+        private static final long serialVersionUID = 1L;
+        private static final Logger log = Logger.getLogger( WikiJSONAccessor.class );
+        
+        /**
+         *  Create an accessor.
+         */
+        public WikiJSONAccessor()
+        {}
+        
+        /**
+         *  Does not do anything.
+         * 
+         *  {@inheritDoc}
+         */
+        public void postInvoke(Object context, Object instance, Method method, Object result) throws Exception
+        {
+        }
+
+        /**
+         *  Checks access against the permission given.
+         *  
+         *  {@inheritDoc}
+         */
+        public void preInvoke(Object context, Object instance, Method method, Object[] arguments) throws Exception
+        {
+            if( context instanceof HttpServletRequest )
+            {
+                boolean canDo = false;
+                HttpServletRequest req = (HttpServletRequest) context;
+                
+                WikiEngine e = WikiEngine.getInstance( req.getSession().getServletContext(), null );
+               
+                for( Iterator i = c_globalObjects.values().iterator(); i.hasNext(); )
+                {
+                    CallbackContainer cc = (CallbackContainer) i.next();
+                    
+                    if( cc.m_object == instance )
+                    {
+                        canDo = e.getAuthorizationManager().checkPermission( WikiSession.getWikiSession(e, req), 
+                                                                             cc.m_permission );
+
+                        break;
+                    }
+                }
+                                    
+                if( canDo )
+                {
+                    return;
+                }
+            }
+
+            log.debug("Failed JSON permission check: "+instance);
+            throw new WikiSecurityException("No permission to access this AJAX method!");
+        }
+        
+    }
+
+    /**
+     *  Registers a global object (i.e. something which can be called by any
+     *  JSP page).  Typical examples is e.g. "search".  By default, the RPCCallable
+     *  shall need a "view" permission to access.
+     *  
+     *  @param id     The name under which this shall be registered (e.g. "search")
+     *  @param object The RPCCallable which shall be associated to this id.
+     */
+    public static void registerGlobalObject(String id, RPCCallable object)
+    {
+        registerGlobalObject(id, object, PagePermission.VIEW);    
+    }
+    
+    /**
+     *  Registers a global object (i.e. something which can be called by any
+     *  JSP page) with a specific permission.  
+     *  
+     *  @param id     The name under which this shall be registered (e.g. "search")
+     *  @param object The RPCCallable which shall be associated to this id.
+     *  @param perm   The permission which is required to access this object.
+     */
+    public static void registerGlobalObject(String id, RPCCallable object, Permission perm )
+    {
+        CallbackContainer cc = new CallbackContainer();
+        cc.m_permission = perm;
+        cc.m_id = id;
+        cc.m_object = object;
+        
+        c_globalObjects.put( id, cc );
+    }
+
+    /**
+     *  Is called whenever a session is created.  This method creates a new JSONRPCBridge
+     *  and adds it to the user session.  This is done because the global JSONRPCBridge
+     *  InvocationCallbacks are not called; only session locals.  This may be a bug
+     *  in JSON-RPC, or it may be a design feature...
+     *  <p>
+     *  The JSONRPCBridge object will go away once the session expires.
+     *  
+     *  @param session The HttpSession which was created.
+     */
+    public static void sessionCreated( HttpSession session )
+    {
+        JSONRPCBridge bridge = (JSONRPCBridge)session.getAttribute(JSONRPCBRIDGE);
+        
+        if( bridge == null )
+        {
+            bridge = new JSONRPCBridge();
+        
+            session.setAttribute( JSONRPCBRIDGE, bridge );
+        }
+
+        WikiJSONAccessor acc = new WikiJSONAccessor();
+        
+        bridge.registerCallback( acc, HttpServletRequest.class );
+        
+        for( Iterator i = c_globalObjects.values().iterator(); i.hasNext(); )
+        {
+            CallbackContainer cc = (CallbackContainer) i.next();
+       
+            bridge.registerObject( cc.m_id, cc.m_object );
+        }
+
+    }
+     
+    /**
+     *  Just stores the registered global method.
+     *  
+     *  @author Janne Jalkanen
+     *
+     */
+    private static class CallbackContainer
+    {
+        String m_id;
+        RPCCallable m_object;
+        Permission m_permission;
+    }
+}

Added: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/rpc/json/package.html
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/rpc/json/package.html?rev=627255&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/rpc/json/package.html (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/rpc/json/package.html Tue Feb 12 21:53:55 2008
@@ -0,0 +1,20 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<title>Insert title here</title>
+</head>
+<body>
+Provides utility classes for using the JSON-RPC AJAX library.
+<h3>Package Specification</h3>
+
+<p>This package contains utility classes for using the JSON-RPC AJAX library.
+The JSON-RPC library is available from 
+<a href="http://oss.metaparadigm.com/jsonrpc/">http://oss.metaparadigm.com/jsonrpc/</a>.
+
+<h3>Related Documentation</h3>
+
+<a href="http://oss.metaparadigm.com/jsonrpc/">JSON-RPC</a>.
+
+</body>
+</html>
\ No newline at end of file

Added: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/rss/AtomFeed.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/rss/AtomFeed.java?rev=627255&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/rss/AtomFeed.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/rss/AtomFeed.java Tue Feb 12 21:53:55 2008
@@ -0,0 +1,217 @@
+/*
+    JSPWiki - a JSP-based WikiWiki clone.
+
+    Copyright (C) 2001-2007 Janne Jalkanen (Janne.Jalkanen@iki.fi)
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published by
+    the Free Software Foundation; either version 2.1 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+package com.ecyrd.jspwiki.rss;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.*;
+
+import javax.servlet.ServletContext;
+
+import org.apache.commons.lang.time.DateFormatUtils;
+import org.jdom.Element;
+import org.jdom.Namespace;
+import org.jdom.output.Format;
+import org.jdom.output.XMLOutputter;
+
+import com.ecyrd.jspwiki.Release;
+import com.ecyrd.jspwiki.WikiContext;
+import com.ecyrd.jspwiki.WikiEngine;
+import com.ecyrd.jspwiki.WikiPage;
+import com.ecyrd.jspwiki.action.AttachActionBean;
+import com.ecyrd.jspwiki.action.RSSActionBean;
+import com.ecyrd.jspwiki.attachment.Attachment;
+import com.ecyrd.jspwiki.providers.ProviderException;
+
+/**
+ *  Provides an Atom 1.0 standard feed, with enclosures.
+ *
+ * @author jalkanen
+ *
+ */
+public class AtomFeed extends Feed
+{
+    private Namespace m_atomNameSpace = Namespace.getNamespace("http://www.w3.org/2005/Atom");
+
+    public static final String RFC3339FORMAT = "yyyy-MM-dd'T'HH:mm:ssZZ";
+
+    public AtomFeed( WikiContext c )
+    {
+        super(c);
+    }
+
+    /**
+     *   This is a bit complicated right now, as there is no proper metadata
+     *   store in JSPWiki.
+     *
+     *   @return
+     */
+    private String getFeedID()
+    {
+        return m_wikiContext.getEngine().getBaseURL(); // FIXME: This is not a feed id
+    }
+
+    private String getEntryID( Entry e )
+    {
+        return e.getURL(); // FIXME: Not really a feed id!
+    }
+
+    private Collection getItems()
+    {
+        ArrayList list = new ArrayList();
+
+        WikiEngine engine = m_wikiContext.getEngine();
+        ServletContext servletContext = null;
+
+        if( m_wikiContext.getHttpRequest() != null )
+            servletContext = m_wikiContext.getHttpRequest().getSession().getServletContext();
+
+        for( Iterator i = m_entries.iterator(); i.hasNext(); )
+        {
+            Entry e = (Entry)i.next();
+
+            WikiPage p = e.getPage();
+
+            Element entryEl = getElement("entry");
+
+            //
+            //  Mandatory elements
+            //
+
+            entryEl.addContent( getElement("id").setText( getEntryID(e)) );
+            entryEl.addContent( getElement("title").setAttribute("type","html").setText( e.getTitle() ));
+            entryEl.addContent( getElement("updated").setText( DateFormatUtils.formatUTC(p.getLastModified(),
+                                                                                         RFC3339FORMAT )));
+            //
+            //  Optional elements
+            //
+
+            entryEl.addContent( getElement("author").addContent( getElement("name").setText( e.getAuthor() )));
+            entryEl.addContent( getElement("link").setAttribute("rel","alternate").setAttribute("href",e.getURL()));
+            entryEl.addContent( getElement("content").setAttribute("type","html").setText( e.getContent() ));
+
+            //
+            //  Check for enclosures
+            //
+
+            if( engine.getAttachmentManager().hasAttachments(p) && servletContext != null )
+            {
+                try
+                {
+                    Collection c = engine.getAttachmentManager().listAttachments(p);
+
+                    for( Iterator a = c.iterator(); a.hasNext(); )
+                    {
+                        Attachment att = (Attachment) a.next();
+
+                        Element attEl = getElement("link");
+                        attEl.setAttribute( "rel","enclosure" );
+                        attEl.setAttribute( "href", m_wikiContext.getContext().getURL(AttachActionBean.class, att.getName(), null, true ) );
+                        attEl.setAttribute( "length", Long.toString(att.getSize()) );
+                        attEl.setAttribute( "type", getMimeType( servletContext, att.getFileName() ) );
+
+                        entryEl.addContent( attEl );
+                    }
+                }
+                catch( ProviderException ex )
+                {
+                    // FIXME: log.info("Can't get attachment data",ex);
+                }
+            }
+
+
+            list.add( entryEl );
+        }
+
+        return list;
+    }
+
+    public String getString()
+    {
+        Element root = getElement("feed");
+        WikiEngine engine = m_wikiContext.getEngine();
+
+        Date lastModified = new Date(0L);
+
+        for( Iterator i = m_entries.iterator(); i.hasNext(); )
+        {
+            Entry e = (Entry)i.next();
+
+            if( e.getPage().getLastModified().after(lastModified) )
+                lastModified = e.getPage().getLastModified();
+        }
+
+        //
+        //  Mandatory parts
+        //
+        root.addContent( getElement("title").setText( getChannelTitle() ) );
+        root.addContent( getElement("id").setText(getFeedID()) );
+        root.addContent( getElement("updated").setText(DateFormatUtils.formatUTC( lastModified,
+                                                                                  RFC3339FORMAT ) ));
+
+        //
+        //  Optional
+        //
+        // root.addContent( getElement("author").addContent(getElement("name").setText(format())))
+        root.addContent( getElement("link").setAttribute("href",engine.getBaseURL()));
+        root.addContent( getElement("generator").setText("JSPWiki "+Release.VERSTR));
+        
+        Map<String,String> rssParams = new HashMap<String,String>();
+        rssParams.put("mode", m_mode);
+        rssParams.put("type", "atom");
+        String rssFeedURL  = m_wikiContext.getContext().getURL(RSSActionBean.class, 
+                                           "page="+engine.encodeName(m_wikiContext.getPage().getName()),
+                                           rssParams,
+                                           true );
+        Element self = getElement("link").setAttribute("rel","self");
+        self.setAttribute("href",rssFeedURL);
+        root.addContent(self);
+
+        //
+        //  Items
+        //
+
+        root.addContent( getItems() );
+
+        //
+        //  aaand output
+        //
+        XMLOutputter output = new XMLOutputter();
+
+        output.setFormat( Format.getPrettyFormat() );
+
+        try
+        {
+            StringWriter res = new StringWriter();
+            output.output( root, res );
+
+            return res.toString();
+        }
+        catch( IOException e )
+        {
+            return null;
+        }
+    }
+
+    private final Element getElement( String name )
+    {
+        return new Element( name, m_atomNameSpace );
+    }
+}

Added: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/rss/Entry.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/rss/Entry.java?rev=627255&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/rss/Entry.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/rss/Entry.java Tue Feb 12 21:53:55 2008
@@ -0,0 +1,86 @@
+/*
+    JSPWiki - a JSP-based WikiWiki clone.
+
+    Copyright (C) 2001-2007 Janne Jalkanen (Janne.Jalkanen@iki.fi)
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published by
+    the Free Software Foundation; either version 2.1 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+package com.ecyrd.jspwiki.rss;
+
+import com.ecyrd.jspwiki.WikiPage;
+
+/**
+ *  @author jalkanen
+ *
+ *  @since
+ */
+public class Entry
+{
+    private String m_content;
+    private String m_URL;
+    private String m_title;
+    private WikiPage m_page;
+    private String m_author;
+
+    public void setAuthor( String author )
+    {
+        m_author = author;
+    }
+
+    public String getAuthor()
+    {
+        return m_author;
+    }
+
+    public WikiPage getPage()
+    {
+        return m_page;
+    }
+
+    public void setPage( WikiPage p )
+    {
+        m_page = p;
+    }
+
+    public void setTitle( String title )
+    {
+        m_title = title;
+    }
+
+    public String getTitle()
+    {
+        return m_title;
+    }
+
+    public void setURL( String url )
+    {
+        m_URL = url;
+    }
+
+    public String getURL()
+    {
+        return m_URL;
+    }
+
+    public void setContent( String content )
+    {
+        m_content = content;
+    }
+
+    public String getContent()
+    {
+        return m_content;
+    }
+}

Added: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/rss/Feed.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/rss/Feed.java?rev=627255&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/rss/Feed.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/rss/Feed.java Tue Feb 12 21:53:55 2008
@@ -0,0 +1,145 @@
+/*
+    JSPWiki - a JSP-based WikiWiki clone.
+
+    Copyright (C) 2001-2007 Janne Jalkanen (Janne.Jalkanen@iki.fi)
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published by
+    the Free Software Foundation; either version 2.1 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+package com.ecyrd.jspwiki.rss;
+
+import java.util.*;
+
+import javax.servlet.ServletContext;
+
+import com.ecyrd.jspwiki.TextUtil;
+import com.ecyrd.jspwiki.WikiContext;
+
+/**
+ *  @author jalkanen
+ *
+ *  @since
+ */
+public abstract class Feed
+{
+    protected List m_entries = new ArrayList();
+
+    protected String m_feedURL;
+    protected String m_channelTitle;
+    protected String m_channelDescription;
+    protected String m_channelLanguage;
+
+    protected WikiContext m_wikiContext;
+
+    protected String m_mode = RSSGenerator.MODE_WIKI;
+
+    public Feed( WikiContext context )
+    {
+        m_wikiContext = context;
+    }
+
+    public void setMode( String mode )
+    {
+        m_mode = mode;
+    }
+
+    public void addEntry( Entry e )
+    {
+        m_entries.add( e );
+    }
+
+    public abstract String getString();
+    /**
+     * @return Returns the m_channelDescription.
+     */
+    public String getChannelDescription()
+    {
+        return m_channelDescription;
+    }
+    /**
+     * @param description The m_channelDescription to set.
+     */
+    public void setChannelDescription( String description )
+    {
+        m_channelDescription = description;
+    }
+    /**
+     * @return Returns the m_channelLanguage.
+     */
+    public String getChannelLanguage()
+    {
+        return m_channelLanguage;
+    }
+    /**
+     * @param language The m_channelLanguage to set.
+     */
+    public void setChannelLanguage( String language )
+    {
+        m_channelLanguage = language;
+    }
+    /**
+     * @return Returns the m_channelTitle.
+     */
+    public String getChannelTitle()
+    {
+        return m_channelTitle;
+    }
+    /**
+     * @param title The m_channelTitle to set.
+     */
+    public void setChannelTitle( String title )
+    {
+        m_channelTitle = title;
+    }
+
+    /**
+     * @return Returns the m_feedURL.
+     */
+    public String getFeedURL()
+    {
+        return m_feedURL;
+    }
+    /**
+     * @param feedurl The m_feedURL to set.
+     */
+    public void setFeedURL( String feedurl )
+    {
+        m_feedURL = feedurl;
+    }
+
+    protected String getMimeType(ServletContext c, String name)
+    {
+        String type = c.getMimeType(name);
+
+        if( type == null ) type = "application/octet-stream";
+
+        return type;
+    }
+
+    /**
+     *  Does the required formatting and entity replacement for XML.
+     */
+    public static String format( String s )
+    {
+        if( s != null )
+        {
+            s = TextUtil.replaceString( s, "&", "&amp;" );
+            s = TextUtil.replaceString( s, "<", "&lt;" );
+            s = TextUtil.replaceString( s, ">", "&gt;" );
+
+            return s.trim();
+        }
+        return null;
+    }
+}

Added: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/rss/RSS10Feed.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/rss/RSS10Feed.java?rev=627255&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/rss/RSS10Feed.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/rss/RSS10Feed.java Tue Feb 12 21:53:55 2008
@@ -0,0 +1,211 @@
+/* 
+    JSPWiki - a JSP-based WikiWiki clone.
+
+    Copyright (C) 2001-2002 Janne Jalkanen (Janne.Jalkanen@iki.fi)
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published by
+    the Free Software Foundation; either version 2.1 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+package com.ecyrd.jspwiki.rss;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.ecs.xml.XML;
+
+import com.ecyrd.jspwiki.WikiContext;
+import com.ecyrd.jspwiki.WikiEngine;
+import com.ecyrd.jspwiki.WikiPage;
+import com.ecyrd.jspwiki.action.DiffActionBean;
+import com.ecyrd.jspwiki.action.PageInfoActionBean;
+
+/**
+ *  @author jalkanen
+ *
+ *  @since 
+ */
+public class RSS10Feed extends Feed
+{
+    public RSS10Feed( WikiContext context )
+    {
+        super(context);
+    }
+    
+    private XML getRDFItems()
+    {
+        XML items = new XML( "items" );
+        
+        XML rdfseq = new XML( "rdf:Seq" );
+        
+        for( Iterator i = m_entries.iterator(); i.hasNext(); )
+        {
+            Entry e = (Entry)i.next();
+
+            String url = e.getURL();
+
+            rdfseq.addElement( new XML("rdf:li").addAttribute( "rdf:resource", url ) );
+        }
+        
+        items.addElement( rdfseq );
+        
+        return items;
+    }
+    
+    private void addItemList( XML root )
+    {
+        SimpleDateFormat iso8601fmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
+        
+        WikiEngine engine = m_wikiContext.getEngine();
+        
+        for( Iterator i = m_entries.iterator(); i.hasNext(); )
+        {
+            Entry e = (Entry)i.next();
+            
+            String url = e.getURL();
+            
+            XML item = new XML( "item" );
+            item.addAttribute( "rdf:about", url );
+            
+            item.addElement( new XML("title").addElement( format(e.getTitle()) ) );
+
+            item.addElement( new XML("link").addElement( url ) );
+
+            XML content = new XML("description");
+            
+            // TODO: Add a size limiter here
+            content.addElement( format(e.getContent()) );
+
+            item.addElement( content );
+
+            WikiPage p = e.getPage();
+            
+            if( p.getVersion() != -1 )
+            {
+                item.addElement( new XML("wiki:version").addElement( Integer.toString(p.getVersion()) ) );
+            }
+
+            if( p.getVersion() > 1 )
+            {
+                Map<String,String> rssParams = new HashMap<String,String>();
+                rssParams.put("r1", "-1");
+                item.addElement( new XML("wiki:diff").addElement( m_wikiContext.getContext().getURL( DiffActionBean.class,
+                                                                                 p.getName(),
+                                                                                 rssParams,
+                                                                                 true) ) );
+            }
+
+           
+            //
+            //  Modification date.
+            //
+            Calendar cal = Calendar.getInstance();
+            cal.setTime( p.getLastModified() );
+            cal.add( Calendar.MILLISECOND, 
+                     - (cal.get( Calendar.ZONE_OFFSET ) + 
+                        (cal.getTimeZone().inDaylightTime( p.getLastModified() ) ? cal.get( Calendar.DST_OFFSET ) : 0 )) );
+
+            item.addElement( new XML("dc:date").addElement( iso8601fmt.format( cal.getTime() )));
+           
+            //
+            //  Author
+            String author = e.getAuthor();
+            if( author == null ) author = "unknown";
+
+            XML contributor = new XML("dc:creator");
+            
+            item.addElement( contributor );
+
+            /*
+            XML description = new XML("rdf:Description");
+            if( m_wikiContext.getEngine().pageExists(author) )
+            {
+                description.addAttribute( "link", engine.getURL( WikiContext.VIEW, 
+                                                                 author,
+                                                                 null,
+                                                                 true ) );
+            }
+            
+            description.addElement( new XML("value").addElement( format(author) ) );
+            contributor.addElement( description );
+           */
+            
+            // Not too many aggregators seem to like this.  Therefore we're
+            // just adding the name here.
+            
+            contributor.addElement( format(author) );
+            
+            //  PageHistory
+
+            item.addElement( new XML("wiki:history").addElement( m_wikiContext.getContext().getURL( PageInfoActionBean.class,
+                                                                                p.getName(),
+                                                                                null,
+                                                                                true ) ) );
+            
+            //
+            //  Add to root
+            //
+            
+            root.addElement( item );
+        }
+    }
+    
+    private XML getChannelElement()
+    {
+        XML channel = new XML( "channel" );
+
+        channel.addAttribute("rdf:about", m_feedURL );
+        
+        channel.addElement( new XML("link").addElement( m_feedURL ) );
+        
+        if( m_channelTitle != null )
+            channel.addElement( new XML("title").addElement( format(m_channelTitle)) );
+        
+        if( m_channelDescription != null )
+            channel.addElement( new XML("description").addElement( format(m_channelDescription)) );
+        
+        if( m_channelLanguage != null )
+            channel.addElement( new XML("dc:language").addElement(m_channelLanguage) );
+
+        channel.setPrettyPrint( true );
+        
+        channel.addElement( getRDFItems() );
+        
+        return channel;
+    }
+    
+    /* (non-Javadoc)
+     * @see com.ecyrd.jspwiki.rss.Feed#getString()
+     */
+    public String getString()
+    {
+        XML root = new XML("rdf:RDF");
+        
+        root.addAttribute( "xmlns:rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#" );
+        root.addAttribute( "xmlns", "http://purl.org/rss/1.0/" );
+        root.addAttribute( "xmlns:dc", "http://purl.org/dc/elements/1.1/" );
+        root.addAttribute( "xmlns:wiki", "http://purl.org/rss/1.0/modules/wiki/" );
+        
+        root.addElement( getChannelElement() );
+        
+        addItemList( root );
+        
+        root.setPrettyPrint( true );
+                
+        return root.toString();
+    }
+
+}

Added: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/rss/RSS20Feed.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/rss/RSS20Feed.java?rev=627255&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/rss/RSS20Feed.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/rss/RSS20Feed.java Tue Feb 12 21:53:55 2008
@@ -0,0 +1,186 @@
+/*
+  JSPWiki - a JSP-based WikiWiki clone.
+
+  Copyright (C) 2001-2002 Janne Jalkanen (Janne.Jalkanen@iki.fi)
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+package com.ecyrd.jspwiki.rss;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+import javax.servlet.ServletContext;
+
+import org.jdom.Element;
+import org.jdom.output.Format;
+import org.jdom.output.XMLOutputter;
+
+import com.ecyrd.jspwiki.Release;
+import com.ecyrd.jspwiki.WikiContext;
+import com.ecyrd.jspwiki.WikiEngine;
+import com.ecyrd.jspwiki.WikiPage;
+import com.ecyrd.jspwiki.action.AttachActionBean;
+import com.ecyrd.jspwiki.attachment.Attachment;
+import com.ecyrd.jspwiki.providers.ProviderException;
+
+/**
+ *  Represents an RSS 2.0 feed (with enclosures).
+ *
+ *  @author jalkanen
+ *
+ *  @since 2.2.27
+ */
+public class RSS20Feed extends Feed
+{
+
+    public RSS20Feed( WikiContext context )
+    {
+        super( context );
+    }
+
+    private List getItems()
+    {
+        ArrayList list = new ArrayList();
+        SimpleDateFormat fmt = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss 'GMT'");
+
+        WikiEngine engine = m_wikiContext.getEngine();
+        ServletContext servletContext = null;
+
+        if( m_wikiContext.getHttpRequest() != null )
+            servletContext = m_wikiContext.getHttpRequest().getSession().getServletContext();
+
+        for( Iterator i = m_entries.iterator(); i.hasNext(); )
+        {
+            Entry e = (Entry)i.next();
+            WikiPage p = e.getPage();
+
+            String url = e.getURL();
+
+            Element item = new Element("item");
+
+            item.addContent( new Element("link").setText(url) );
+
+            item.addContent( new Element("title").setText( e.getTitle()) );
+
+            item.addContent( new Element("description").setText( e.getContent()) );
+
+            //
+            //  Attachments for enclosures
+            //
+
+            if( engine.getAttachmentManager().hasAttachments(p) && servletContext != null )
+            {
+                try
+                {
+                    Collection c = engine.getAttachmentManager().listAttachments(p);
+
+                    for( Iterator a = c.iterator(); a.hasNext(); )
+                    {
+                        Attachment att = (Attachment) a.next();
+
+                        Element attEl = new Element("enclosure");
+                        attEl.setAttribute( "url", m_wikiContext.getContext().getURL(AttachActionBean.class, att.getName(), null, true ) );
+                        attEl.setAttribute( "length", Long.toString(att.getSize()) );
+                        attEl.setAttribute( "type", getMimeType( servletContext, att.getFileName() ) );
+
+                        item.addContent( attEl );
+                    }
+                }
+                catch( ProviderException ex )
+                {
+                    // FIXME: log.info("Can't get attachment data",ex);
+                }
+            }
+
+            //
+            //  Modification date.
+            //
+            Calendar cal = Calendar.getInstance();
+            cal.setTime( p.getLastModified() );
+            cal.add( Calendar.MILLISECOND,
+                     - (cal.get( Calendar.ZONE_OFFSET ) +
+                        (cal.getTimeZone().inDaylightTime( p.getLastModified() ) ? cal.get( Calendar.DST_OFFSET ) : 0 )) );
+
+            item.addContent( new Element("pubDate").setText(fmt.format(cal.getTime())) );
+
+            list.add( item );
+        }
+
+        return list;
+    }
+
+    public String getString()
+    {
+        WikiEngine engine = m_wikiContext.getEngine();
+        Element root = new Element("rss");
+        root.setAttribute("version","2.0");
+
+        Element channel = new Element("channel");
+        root.addContent( channel );
+
+        //
+        //  Mandatory parts
+        //
+        channel.addContent( new Element("title").setText( getChannelTitle() ) );
+        channel.addContent( new Element("link").setText(engine.getBaseURL()));
+        channel.addContent( new Element("description").setText( getChannelDescription() ));
+
+        //
+        //  Optional
+        //
+        channel.addContent( new Element("language").setText(getChannelLanguage()));
+        channel.addContent( new Element("generator").setText("JSPWiki "+Release.VERSTR));
+
+        String mail = engine.getVariable(m_wikiContext,RSSGenerator.PROP_RSS_AUTHOREMAIL);
+        if( mail != null )
+        {
+            String editor = engine.getVariable( m_wikiContext,RSSGenerator.PROP_RSS_AUTHOR );
+
+            if( editor != null )
+                mail = mail + " ("+editor+")";
+
+            channel.addContent( new Element("managingEditor").setText(mail) );
+        }
+
+        //
+        //  Items
+        //
+
+        channel.addContent( getItems() );
+
+        //
+        //  aaand output
+        //
+        XMLOutputter output = new XMLOutputter();
+
+        output.setFormat( Format.getPrettyFormat() );
+
+        try
+        {
+            StringWriter res = new StringWriter();
+            output.output( root, res );
+
+            return res.toString();
+        }
+        catch( IOException e )
+        {
+            return null;
+        }
+    }
+
+}

Added: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/rss/RSSGenerator.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/rss/RSSGenerator.java?rev=627255&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/rss/RSSGenerator.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/rss/RSSGenerator.java Tue Feb 12 21:53:55 2008
@@ -0,0 +1,590 @@
+/*
+    JSPWiki - a JSP-based WikiWiki clone.
+
+    Copyright (C) 2001-2002 Janne Jalkanen (Janne.Jalkanen@iki.fi)
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published by
+    the Free Software Foundation; either version 2.1 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+package com.ecyrd.jspwiki.rss;
+
+import java.util.*;
+
+import org.apache.log4j.Logger;
+
+import com.ecyrd.jspwiki.*;
+import com.ecyrd.jspwiki.action.AttachActionBean;
+import com.ecyrd.jspwiki.action.PageInfoActionBean;
+import com.ecyrd.jspwiki.action.RSSActionBean;
+import com.ecyrd.jspwiki.action.ViewActionBean;
+import com.ecyrd.jspwiki.attachment.Attachment;
+import com.ecyrd.jspwiki.auth.permissions.PagePermission;
+import com.ecyrd.jspwiki.providers.ProviderException;
+
+/**
+ *  Generates an RSS feed from the recent changes.
+ *  <P>
+ *  We use the 1.0 spec, including the wiki-specific extensions.  Wiki extensions
+ *  have been defined in <A HREF="http://usemod.com/cgi-bin/mb.pl?ModWiki">UseMod:ModWiki</A>.
+ *
+ *  @author Janne Jalkanen
+ *  @since  1.7.5.
+ */
+// FIXME: Limit diff and page content size.
+public class RSSGenerator
+{
+    static Logger              log = Logger.getLogger( RSSGenerator.class );
+    private WikiEngine         m_engine;
+
+    private String             m_channelDescription = "";
+    private String             m_channelLanguage    = "en-us";
+    private boolean            m_enabled = true;
+
+    public static final String RSS10 = "rss10";
+    public static final String RSS20 = "rss20";
+    public static final String ATOM  = "atom";
+
+    public static final String MODE_BLOG = "blog";
+    public static final String MODE_WIKI = "wiki";
+    public static final String MODE_FULL = "full";
+
+    /**
+     *  Defines the property name for the RSS channel description.  Default value for the
+     *  channel description is an empty string.
+     *  @since 1.7.6.
+     */
+    public static final String PROP_CHANNEL_DESCRIPTION = "jspwiki.rss.channelDescription";
+
+    /**
+     *  Defines the property name for the RSS channel language.  Default value for the
+     *  language is "en-us".
+     *  @since 1.7.6.
+     */
+    public static final String PROP_CHANNEL_LANGUAGE    = "jspwiki.rss.channelLanguage";
+
+    public static final String PROP_CHANNEL_TITLE       = "jspwiki.rss.channelTitle";
+
+    /**
+     *  Defines the property name for the RSS generator main switch.
+     *  @since 1.7.6.
+     */
+    public static final String PROP_GENERATE_RSS        = "jspwiki.rss.generate";
+
+    /**
+     *  Defines the property name for the RSS file that the wiki should generate.
+     *  @since 1.7.6.
+     */
+    public static final String PROP_RSSFILE             = "jspwiki.rss.fileName";
+
+    public static final String PROP_RSSAUTHOR           = "jspwiki.rss.author";
+    public static final String PROP_RSSAUTHOREMAIL      = "jspwiki.rss.author.email";
+
+    /**
+     *  Defines the property name for the RSS generation interval in seconds.
+     *  @since 1.7.6.
+     */
+    public static final String PROP_INTERVAL            = "jspwiki.rss.interval";
+
+    public static final String PROP_RSS_AUTHOR          = "jspwiki.rss.author";
+    public static final String PROP_RSS_AUTHOREMAIL     = "jspwiki.rss.author.email";
+    public static final String PROP_RSS_COPYRIGHT       = "jspwiki.rss.copyright";
+
+    private static final int MAX_CHARACTERS             = Integer.MAX_VALUE-1;
+
+    /**
+     *  Initialize the RSS generator.
+     */
+    public RSSGenerator( WikiEngine engine, Properties properties )
+        throws NoRequiredPropertyException
+    {
+        m_engine = engine;
+
+        // FIXME: This assumes a bit too much.
+        if( engine.getBaseURL() == null || engine.getBaseURL().length() == 0 )
+        {
+            throw new NoRequiredPropertyException( "RSS requires jspwiki.baseURL to be set!",
+                                                   WikiEngine.PROP_BASEURL );
+        }
+
+        m_channelDescription = properties.getProperty( PROP_CHANNEL_DESCRIPTION,
+                                                       m_channelDescription );
+        m_channelLanguage    = properties.getProperty( PROP_CHANNEL_LANGUAGE,
+                                                       m_channelLanguage );
+    }
+
+    /**
+     *  Does the required formatting and entity replacement for XML.
+     */
+    public static String format( String s )
+    {
+        s = TextUtil.replaceString( s, "&", "&amp;" );
+        s = TextUtil.replaceString( s, "<", "&lt;" );
+        s = TextUtil.replaceString( s, "]]>", "]]&gt;" );
+
+        return s.trim();
+    }
+
+    private String getAuthor( WikiPage page )
+    {
+        String author = page.getAuthor();
+
+        if( author == null ) author = "An unknown author";
+
+        return author;
+    }
+
+    private String getAttachmentDescription( WikiContext wikiContext, Attachment att )
+    {
+        String author = getAuthor(att);
+        StringBuffer sb = new StringBuffer();
+
+        if( att.getVersion() != 1 )
+        {
+            sb.append(author+" uploaded a new version of this attachment on "+att.getLastModified() );
+        }
+        else
+        {
+            sb.append(author+" created this attachment on "+att.getLastModified() );
+        }
+
+        sb.append("<br /><hr /><br />");
+        sb.append( "Parent page: <a href=\""+
+                   wikiContext.getContext().getURL( ViewActionBean.class, att.getParentName(), null, true ) +
+                   "\">"+att.getParentName()+"</a><br />" );
+        sb.append( "Info page: <a href=\""+
+                   wikiContext.getContext().getURL( PageInfoActionBean.class, att.getName(), null, true ) +
+                   "\">"+att.getName()+"</a>" );
+
+        return sb.toString();
+    }
+
+    private String getPageDescription( WikiPage page )
+    {
+        StringBuffer buf = new StringBuffer();
+        String author = getAuthor(page);
+
+        WikiContext ctx = m_engine.getWikiActionBeanFactory().newViewActionBean( page );
+        if( page.getVersion() > 1 )
+        {
+            String diff = m_engine.getDiff( ctx,
+                                            page.getVersion()-1, // FIXME: Will fail when non-contiguous versions
+                                            page.getVersion() );
+
+            buf.append(author+" changed this page on "+page.getLastModified()+":<br /><hr /><br />" );
+            buf.append(diff);
+        }
+        else
+        {
+            buf.append(author+" created this page on "+page.getLastModified()+":<br /><hr /><br />" );
+            buf.append(m_engine.getHTML( page.getName() ));
+        }
+
+        return buf.toString();
+    }
+
+    private String getEntryDescription( WikiContext context, WikiPage page )
+    {
+        String res;
+
+        if( page instanceof Attachment )
+        {
+            res = getAttachmentDescription( context, (Attachment)page );
+        }
+        else
+        {
+            res = getPageDescription( page );
+        }
+
+        return res;
+    }
+
+    // FIXME: This should probably return something more intelligent
+    private String getEntryTitle( WikiPage page )
+    {
+        return page.getName()+", version "+page.getVersion();
+    }
+
+    /**
+     *  Generates the RSS resource.  You probably want to output this
+     *  result into a file or something, or serve as output from a servlet.
+     */
+    public String generate() throws WikiException
+    {
+        // FIXME: This will absolutely, positively not work. We need to do something else
+        WikiContext context = (WikiContext)m_engine.getWikiActionBeanFactory().newActionBean(null,null,RSSActionBean.class);
+        context.setPage( new WikiPage( m_engine, "__DUMMY" ) );
+        Feed feed = new RSS10Feed( context );
+
+        String result = generateFullWikiRSS( context, feed );
+
+        result = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + result;
+
+        return result;
+    }
+
+    /**
+     * Returns the content type of this RSS feed.
+     *  @since 2.3.15
+     * @param mode the RSS mode: {@link #RSS10}, {@link #RSS20} or {@link #ATOM}.
+     * @return the content type
+     */
+    public static String getContentType( String mode )
+    {
+        if( mode.equals( RSS10 )||mode.equals(RSS20) )
+        {
+            return "application/rss+xml";
+        }
+        else if( mode.equals(ATOM) )
+        {
+            return "application/atom+xml";
+        }
+
+        return "application/octet-stream"; // Unknown type
+    }
+
+    /**
+     *  Generates a feed based on a context and list of changes.
+     * @param wikiContext The WikiContext
+     * @param changed A list of Entry objects
+     * @param mode The mode (wiki/blog)
+     * @param type The type (RSS10, RSS20, ATOM).  Default is RSS 1.0
+     * @return Fully formed XML.
+     *
+     * @throws ProviderException If the underlying provider failed.
+     * @throws IllegalArgumentException If an illegal mode is given.
+     */
+    public String generateFeed( WikiContext wikiContext, List changed, String mode, String type )
+        throws ProviderException
+    {
+        Feed feed = null;
+        String res = null;
+
+        if( ATOM.equals(type) )
+        {
+            feed = new AtomFeed( wikiContext );
+        }
+        else if( RSS20.equals( type ) )
+        {
+            feed = new RSS20Feed( wikiContext );
+        }
+        else
+        {
+            feed = new RSS10Feed( wikiContext );
+        }
+
+        feed.setMode( mode );
+
+        if( MODE_BLOG.equals( mode ) )
+        {
+            res = generateBlogRSS( wikiContext, changed, feed );
+        }
+        else if( MODE_FULL.equals(mode) )
+        {
+            res = generateFullWikiRSS( wikiContext, feed );
+        }
+        else if( MODE_WIKI.equals(mode) )
+        {
+            res = generateWikiPageRSS( wikiContext, changed, feed );
+        }
+        else
+        {
+            throw new IllegalArgumentException( "Invalid value for feed mode: "+mode );
+        }
+
+        return res;
+    }
+
+    /**
+     * Returns <code>true</code> if RSS generation is enabled.
+     * @return whether RSS generation is currently enabled
+     */
+    public boolean isEnabled()
+    {
+        return m_enabled;
+    }
+
+    /**
+     * Turns RSS generation on or off. This setting is used to set
+     * the "enabled" flag only for use by callers, and does not
+     * actually affect whether the {@link #generate()} or
+     * {@link #generateFeed(WikiContext, List, String, String)}
+     * methods output anything.
+     * @param enabled whether RSS generation is considered enabled.
+     */
+    public synchronized void setEnabled( boolean enabled )
+    {
+        m_enabled = enabled;
+    }
+
+    /**
+     *  Generates an RSS feed for the entire wiki.  Each item should be an instance of the RSSItem class.
+     */
+    protected String generateFullWikiRSS( WikiContext wikiContext, Feed feed )
+    {
+        feed.setChannelTitle( m_engine.getApplicationName() );
+        feed.setFeedURL( m_engine.getBaseURL() );
+        feed.setChannelLanguage( m_channelLanguage );
+        feed.setChannelDescription( m_channelDescription );
+
+        Collection changed = m_engine.getRecentChanges();
+
+        WikiSession session = WikiSession.guestSession( m_engine );
+        int items = 0;
+        for( Iterator i = changed.iterator(); i.hasNext() && items < 15; items++ )
+        {
+            WikiPage page = (WikiPage) i.next();
+
+            //
+            //  Check if the anonymous user has view access to this page.
+            //
+
+            if( !m_engine.getAuthorizationManager().checkPermission(session,
+                                                                    new PagePermission(page,PagePermission.VIEW_ACTION) ) )
+            {
+                // No permission, skip to the next one.
+                continue;
+            }
+
+            Entry e = new Entry();
+
+            e.setPage( page );
+
+            String url;
+
+            if( page instanceof Attachment )
+            {
+                url = wikiContext.getContext().getURL( AttachActionBean.class, 
+                                       page.getName(),
+                                       null,
+                                       true );
+            }
+            else
+            {
+                url = wikiContext.getContext().getURL( ViewActionBean.class, 
+                                       page.getName(),
+                                       null,
+                                       true );
+            }
+
+            e.setURL( url );
+            e.setTitle( page.getName() );
+            e.setContent( getEntryDescription(wikiContext, page) );
+            e.setAuthor( getAuthor(page) );
+
+            feed.addEntry( e );
+        }
+
+        return feed.getString();
+    }
+
+    /**
+     *  Create RSS/Atom as if this page was a wikipage (in contrast to Blog mode).
+     *
+     * @param wikiContext
+     * @param changed
+     * @param feed
+     * @return
+     */
+    protected String generateWikiPageRSS( WikiContext wikiContext, List changed, Feed feed )
+    {
+        feed.setChannelTitle( m_engine.getApplicationName()+": "+wikiContext.getPage().getName() );
+        feed.setFeedURL( wikiContext.getViewURL( wikiContext.getPage().getName() ) );
+        String language = m_engine.getVariable( wikiContext, PROP_CHANNEL_LANGUAGE );
+
+        if( language != null )
+            feed.setChannelLanguage( language );
+        else
+            feed.setChannelLanguage( m_channelLanguage );
+
+        String channelDescription = m_engine.getVariable( wikiContext, PROP_CHANNEL_DESCRIPTION );
+
+        if( channelDescription != null )
+        {
+            feed.setChannelDescription( channelDescription );
+        }
+
+        Collections.sort( changed, new PageTimeComparator() );
+
+        int items = 0;
+        for( Iterator i = changed.iterator(); i.hasNext() && items < 15; items++ )
+        {
+            WikiPage page = (WikiPage) i.next();
+
+            Entry e = new Entry();
+
+            e.setPage( page );
+
+            String url;
+
+            Map<String,String> rssParams = new HashMap<String,String>();
+            rssParams.put("version", String.valueOf(page.getVersion()));
+            if( page instanceof Attachment )
+            {
+                url = wikiContext.getContext().getURL( AttachActionBean.class, 
+                                       page.getName(),
+                                       rssParams,
+                                       true );
+            }
+            else
+            {
+                url = wikiContext.getContext().getURL( ViewActionBean.class, 
+                                       page.getName(),
+                                       rssParams,
+                                       true );
+            }
+
+            // Unfortunately, this is needed because the code will again go through
+            // replacement conversion
+
+            url = TextUtil.replaceString( url, "&amp;", "&" );
+
+            e.setURL( url );
+            e.setTitle( getEntryTitle(page) );
+            e.setContent( getEntryDescription(wikiContext, page) );
+            e.setAuthor( getAuthor(page) );
+
+            feed.addEntry( e );
+        }
+
+        return feed.getString();
+    }
+
+
+    /**
+     *  Creates RSS from modifications as if this page was a blog (using the WeblogPlugin).
+     *
+     *  @param wikiContext The WikiContext, as usual.
+     *  @param changed A list of the changed pages.
+     *  @param feed A valid Feed object.  The feed will be used to create the RSS/Atom, depending
+     *              on which kind of an object you want to put in it.
+     *  @return A String of valid RSS or Atom.
+     *  @throws ProviderException If reading of pages was not possible.
+     */
+    protected String generateBlogRSS( WikiContext wikiContext, List changed, Feed feed )
+        throws ProviderException
+    {
+        if( log.isDebugEnabled() ) log.debug("Generating RSS for blog, size="+changed.size());
+
+        String ctitle = m_engine.getVariable( wikiContext, PROP_CHANNEL_TITLE );
+
+        if( ctitle != null )
+            feed.setChannelTitle( ctitle );
+        else
+            feed.setChannelTitle( m_engine.getApplicationName()+":"+wikiContext.getPage().getName() );
+
+        feed.setFeedURL( wikiContext.getViewURL( wikiContext.getPage().getName() ) );
+
+        String language = m_engine.getVariable( wikiContext, PROP_CHANNEL_LANGUAGE );
+
+        if( language != null )
+            feed.setChannelLanguage( language );
+        else
+            feed.setChannelLanguage( m_channelLanguage );
+
+        String channelDescription = m_engine.getVariable( wikiContext, PROP_CHANNEL_DESCRIPTION );
+
+        if( channelDescription != null )
+        {
+            feed.setChannelDescription( channelDescription );
+        }
+
+        Collections.sort( changed, new PageTimeComparator() );
+
+        int items = 0;
+        for( Iterator i = changed.iterator(); i.hasNext() && items < 15; items++ )
+        {
+            WikiPage page = (WikiPage) i.next();
+
+            Entry e = new Entry();
+
+            e.setPage( page );
+
+            String url;
+
+            if( page instanceof Attachment )
+            {
+                url = wikiContext.getContext().getURL( AttachActionBean.class, 
+                                       page.getName(),
+                                       null,
+                                       true );
+            }
+            else
+            {
+                url = wikiContext.getContext().getURL( ViewActionBean.class, 
+                                       page.getName(),
+                                       null,
+                                       true );
+            }
+
+            e.setURL( url );
+
+            //
+            //  Title
+            //
+
+            String pageText = m_engine.getPureText(page.getName(), WikiProvider.LATEST_VERSION );
+
+            String title = "";
+            int firstLine = pageText.indexOf('\n');
+
+            if( firstLine > 0 )
+            {
+                title = pageText.substring( 0, firstLine ).trim();
+            }
+
+            if( title.length() == 0 ) title = page.getName();
+
+            // Remove wiki formatting
+            while( title.startsWith("!") ) title = title.substring(1);
+
+            e.setTitle( title );
+
+            //
+            //  Description
+            //
+
+            if( firstLine > 0 )
+            {
+                int maxlen = pageText.length();
+                if( maxlen > MAX_CHARACTERS ) maxlen = MAX_CHARACTERS;
+
+                if( maxlen > 0 )
+                {
+                    pageText = m_engine.textToHTML( wikiContext,
+                                                    pageText.substring( firstLine+1,
+                                                                        maxlen ).trim() );
+
+                    if( maxlen == MAX_CHARACTERS ) pageText += "...";
+
+                    e.setContent( pageText );
+                }
+                else
+                {
+                    e.setContent( title );
+                }
+            }
+            else
+            {
+                e.setContent( title );
+            }
+
+            e.setAuthor( getAuthor(page) );
+
+            feed.addEntry( e );
+        }
+
+        return feed.getString();
+    }
+
+}

Added: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/rss/RSSThread.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/rss/RSSThread.java?rev=627255&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/rss/RSSThread.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/rss/RSSThread.java Tue Feb 12 21:53:55 2008
@@ -0,0 +1,125 @@
+/*
+   JSPWiki - a JSP-based WikiWiki clone.
+
+   Copyright (C) 2005-2007 Janne Jalkanen (Janne.Jalkanen@iki.fi)
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as published by
+   the Free Software Foundation; either version 2.1 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+package com.ecyrd.jspwiki.rss;
+
+
+import java.io.BufferedWriter;
+import java.io.File;
+
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.Writer;
+
+import org.apache.log4j.Logger;
+
+import com.ecyrd.jspwiki.FileUtil;
+import com.ecyrd.jspwiki.WikiEngine;
+import com.ecyrd.jspwiki.util.WatchDog;
+import com.ecyrd.jspwiki.util.WikiBackgroundThread;
+
+/**
+ *  Runs the RSS generation thread.
+ *  FIXME: MUST be somewhere else, this is not a good place.
+ */
+public class RSSThread extends WikiBackgroundThread
+{
+    static Logger              log = Logger.getLogger( RSSThread.class );
+        
+    private final File m_rssFile;
+    private final int m_rssInterval;
+    private final RSSGenerator m_generator;
+        
+    private WatchDog m_watchdog;
+    
+    public RSSThread( WikiEngine engine, File rssFile, int rssInterval )
+    {
+        super( engine, rssInterval );
+        m_generator = engine.getRSSGenerator();
+        m_rssFile = rssFile;
+        m_rssInterval = rssInterval;
+        setName("JSPWiki RSS Generator");
+        log.debug( "RSS file will be at "+m_rssFile.getAbsolutePath() );
+        log.debug( "RSS refresh interval (seconds): "+m_rssInterval );
+    }
+    
+    public void startupTask() throws Exception
+    {
+        m_watchdog = getEngine().getCurrentWatchDog();
+    }
+    
+    /**
+     * Runs the RSS generator thread.
+     * If a previous RSS generation operation encountered a 
+     * file I/O or other error, this method will turn off generation.
+     * <code>false</code>.
+     * @see java.lang.Thread#run()
+     */
+    public void backgroundTask() throws Exception
+    {
+        if ( m_generator.isEnabled() )
+        {
+            Writer out = null;
+            Reader in  = null;
+
+            m_watchdog.enterState( "Generating RSS feed", 60 );
+            
+            try
+            {
+                //
+                //  Generate RSS file, output it to
+                //  default "rss.rdf".
+                //
+                log.debug("Regenerating RSS feed to "+m_rssFile);
+
+                String feed = m_generator.generate();
+
+                in  = new StringReader(feed);
+                out = new BufferedWriter( new OutputStreamWriter( new FileOutputStream( m_rssFile ), "UTF-8") );
+
+                FileUtil.copyContents( in, out );
+            }
+            catch( IOException e )
+            {
+                log.error("Cannot generate RSS feed to "+m_rssFile.getAbsolutePath(), e );
+                m_generator.setEnabled( false );
+            }
+            finally
+            {
+                try
+                {
+                    if( in != null )  in.close();
+                    if( out != null ) out.close();
+                }
+                catch( IOException e )
+                {
+                    log.fatal("Could not close I/O for RSS", e );
+                    m_generator.setEnabled( false );
+                }
+                m_watchdog.exitState();
+            }
+
+        }
+    }
+        
+}

Added: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/search/BasicSearchProvider.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/search/BasicSearchProvider.java?rev=627255&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/search/BasicSearchProvider.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/search/BasicSearchProvider.java Tue Feb 12 21:53:55 2008
@@ -0,0 +1,200 @@
+/*
+JSPWiki - a JSP-based WikiWiki clone.
+
+Copyright (C) 2005 Janne Jalkanen (Janne.Jalkanen@iki.fi)
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+package com.ecyrd.jspwiki.search;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Properties;
+import java.util.StringTokenizer;
+import java.util.TreeSet;
+
+import org.apache.log4j.Logger;
+
+import com.ecyrd.jspwiki.NoRequiredPropertyException;
+import com.ecyrd.jspwiki.QueryItem;
+import com.ecyrd.jspwiki.SearchMatcher;
+import com.ecyrd.jspwiki.SearchResult;
+import com.ecyrd.jspwiki.SearchResultComparator;
+import com.ecyrd.jspwiki.WikiEngine;
+import com.ecyrd.jspwiki.WikiPage;
+import com.ecyrd.jspwiki.attachment.Attachment;
+import com.ecyrd.jspwiki.providers.ProviderException;
+import com.ecyrd.jspwiki.providers.WikiPageProvider;
+
+/**
+ *  Interface for the search providers that handle searching the Wiki
+ *
+ *  @author Arent-Jan Banck
+ *  @since 2.2.21.
+ */
+public class BasicSearchProvider implements SearchProvider
+{
+    private static final Logger log = Logger.getLogger(BasicSearchProvider.class);
+
+    private WikiEngine m_engine;
+
+    public void initialize(WikiEngine engine, Properties props)
+            throws NoRequiredPropertyException, IOException
+    {
+        m_engine = engine;
+    }
+
+    public void pageRemoved(WikiPage page) {}
+
+    public void reindexPage(WikiPage page) {}
+
+    public  QueryItem[] parseQuery(String query)
+    {
+        StringTokenizer st = new StringTokenizer( query, " \t," );
+
+        QueryItem[] items = new QueryItem[st.countTokens()];
+        int word = 0;
+
+        log.debug("Expecting "+items.length+" items");
+
+        //
+        //  Parse incoming search string
+        //
+
+        while( st.hasMoreTokens() )
+        {
+            log.debug("Item "+word);
+            String token = st.nextToken().toLowerCase();
+
+            items[word] = new QueryItem();
+
+            switch( token.charAt(0) )
+            {
+              case '+':
+                items[word].type = QueryItem.REQUIRED;
+                token = token.substring(1);
+                log.debug("Required word: "+token);
+                break;
+
+              case '-':
+                items[word].type = QueryItem.FORBIDDEN;
+                token = token.substring(1);
+                log.debug("Forbidden word: "+token);
+                break;
+
+              default:
+                items[word].type = QueryItem.REQUESTED;
+                log.debug("Requested word: "+token);
+                break;
+            }
+
+            items[word++].word = token;
+        }
+
+        return items;
+    }
+
+    private String attachmentNames(WikiPage page, String separator)
+    {
+        if(m_engine.getAttachmentManager().hasAttachments(page))
+        {
+            Collection<Attachment> attachments;
+            try
+            {
+                attachments = m_engine.getAttachmentManager().listAttachments(page);
+            }
+            catch (ProviderException e)
+            {
+                log.error("Unable to get attachments for page", e);
+                return "";
+            }
+
+            StringBuffer attachmentNames = new StringBuffer();
+            for( Iterator<Attachment> it = attachments.iterator(); it.hasNext(); )
+            {
+                Attachment att = it.next();
+                attachmentNames.append(att.getName());
+                if(it.hasNext())
+                    attachmentNames.append(separator);
+            }
+            return attachmentNames.toString();
+        }
+
+        return "";
+    }
+
+    private Collection<SearchResult> findPages( QueryItem[] query )
+    {
+        TreeSet<SearchResult> res = new TreeSet<SearchResult>( new SearchResultComparator() );
+        SearchMatcher matcher = new SearchMatcher( m_engine, query );
+
+        Collection<WikiPage> allPages = null;
+        try
+        {
+            allPages = m_engine.getPageManager().getAllPages();
+        }
+        catch( ProviderException pe )
+        {
+            log.error( "Unable to retrieve page list", pe );
+            return null;
+        }
+
+        Iterator<WikiPage> it = allPages.iterator();
+        while( it.hasNext() )
+        {
+            try
+            {
+                WikiPage page = it.next();
+                if (page != null)
+                {
+                    String pageName = page.getName();
+                    String pageContent = m_engine.getPageManager().getPageText(pageName, WikiPageProvider.LATEST_VERSION) +
+                                         attachmentNames(page, " ");
+                    SearchResult comparison = matcher.matchPageContent( pageName, pageContent );
+
+                    if( comparison != null )
+                    {
+                        res.add( comparison );
+                    }
+                }
+            }
+            catch( ProviderException pe )
+            {
+                log.error( "Unable to retrieve page from cache", pe );
+            }
+            catch( IOException ioe )
+            {
+                log.error( "Failed to search page", ioe );
+            }
+        }
+
+        return res;
+    }
+
+    public Collection<SearchResult> findPages(String query) 
+    {
+        return findPages(parseQuery(query));
+    }
+
+    /**
+     * @see com.ecyrd.jspwiki.WikiProvider#getProviderInfo()
+     */
+    public String getProviderInfo()
+    {
+        return "BasicSearchProvider";
+    }
+
+}