You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@roller.apache.org by sn...@apache.org on 2006/05/02 00:23:34 UTC

svn commit: r398712 [22/32] - in /incubator/roller/trunk/src/org/apache: ./ roller/ roller/business/ roller/business/hibernate/ roller/business/referrers/ roller/business/runnable/ roller/business/search/ roller/business/search/operations/ roller/busin...

Added: incubator/roller/trunk/src/org/apache/roller/presentation/velocity/PageServlet.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/apache/roller/presentation/velocity/PageServlet.java?rev=398712&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/apache/roller/presentation/velocity/PageServlet.java (added)
+++ incubator/roller/trunk/src/org/apache/roller/presentation/velocity/PageServlet.java Mon May  1 15:23:02 2006
@@ -0,0 +1,304 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+*  contributor license agreements.  The ASF licenses this file to You
+* under the Apache License, Version 2.0 (the "License"); you may not
+* use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+package org.apache.roller.presentation.velocity;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.net.SocketException;
+import java.util.Date;
+import java.util.Map;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.jsp.JspFactory;
+import javax.servlet.jsp.PageContext;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.context.Context;
+import org.apache.velocity.exception.ResourceNotFoundException;
+import org.apache.velocity.servlet.VelocityServlet;
+import org.apache.roller.RollerException;
+import org.apache.roller.pojos.WeblogTemplate;
+import org.apache.roller.pojos.WebsiteData;
+import org.apache.roller.presentation.InvalidRequestException;
+import org.apache.roller.presentation.RollerRequest;
+
+
+/**
+ * The PageServlet handles all requests for weblog pages at /page/*
+ * 
+ * @web.servlet name="PageServlet" load-on-startup="0"
+ * @web.servlet-init-param name="org.apache.velocity.properties" 
+ * 		                  value="/WEB-INF/velocity.properties"
+ *  
+ * //web.servlet-mapping url-pattern="/page/*"
+ *
+ * NOTE: the webdoclet task allows some elements to be inherited when generating
+ *   the web.xml file.  for this reason we can't put the servlet mapping here
+ *   because it will be inherited by PreviewServlet.  instead we manually do 
+ *   the servlet mapping for PageServlet in the servlet-mappings.xml
+ *   file under metadata/xdoclet.
+ *
+ */ 
+public class PageServlet extends VelocityServlet {
+    
+    private static Log mLogger = LogFactory.getLog(PageServlet.class);
+    
+    
+    /**
+     * Sets servletContext for WebappResourceLoader.
+     */
+    public void init( ServletConfig config )
+        throws ServletException {
+        
+        super.init( config );
+        WebappResourceLoader.setServletContext( getServletContext() );
+    }
+    
+    
+    /**
+     * Process a request for a Weblog page.
+     */
+    public Template handleRequest(HttpServletRequest request,
+                                HttpServletResponse response, 
+                                Context ctx) 
+        throws Exception {
+        
+        Template outty = null;
+        RollerRequest rreq = null;
+        WebsiteData website = null;
+        
+        PageContext pageContext =
+            JspFactory.getDefaultFactory().getPageContext(
+            this, request, response,"", true, 8192, true);
+
+        // first off lets parse the incoming request and validate it
+        try {
+            rreq = RollerRequest.getRollerRequest(pageContext);
+        } catch (Throwable t) {
+            // NOTE: indicates real problem, not just a "not found" error
+            throw new Exception("ERROR: creating RollerRequest");
+        }
+
+        // All pages exist within website, so website MUST be specified
+        website = rreq.getWebsite();
+        if (website == null) {
+            response.sendError(HttpServletResponse.SC_NOT_FOUND);
+            return null;
+        }
+               
+        // request appears to be valid, lets render
+        try {
+            org.apache.roller.pojos.Template page = null;
+            
+            // If this is a popup request, then deal with it specially
+            if(request.getParameter("popup") != null) {
+                try {
+                    // Does user have a popupcomments page?
+                    page = website.getPageByName("_popupcomments");
+                } catch(Exception e ) {
+                    // ignored ... considered page not found
+                }
+                
+                // User doesn't have one so return the default
+                if(page == null) {
+                    page = new WeblogTemplate("/popupcomments.vm", website, 
+                            "Comments", "Comments", "dummy_link", 
+                            "dummy_template", new Date());
+                }
+                
+                rreq.setPage(page);
+                
+            // If request specified the page, then go with that
+            } else if (rreq.getPage() != null) {
+                page = rreq.getPage();
+                
+            // If page not available from request, then use website's default
+            } else {
+                page = website.getDefaultPage();
+                rreq.setPage(page);
+            }
+            
+            // Still no page ID, then we have a problem
+            if ( page == null ) {
+                throw new ResourceNotFoundException("Page not found");
+            }
+
+            // this sets up the page we want to render
+            outty = prepareForPageExecution(ctx, rreq, response, page);
+            
+            // if there is a decorator template then apply it
+            if (website != null) {
+                // parse/merge Page template
+                StringWriter sw = new StringWriter();
+                outty.merge(ctx, sw);
+                ctx.put("decorator_body", sw.toString());
+                
+                // replace outty with decorator Template
+                outty = findDecorator(website, (String) ctx.get("decorator"));
+            }
+            
+        } catch (ResourceNotFoundException rnfe ) {            
+            response.sendError(HttpServletResponse.SC_NOT_FOUND);
+            request.setAttribute("DisplayException", rnfe);
+            mLogger.warn("ResourceNotFound: "+ request.getRequestURL());
+            mLogger.debug(rnfe);
+            
+        } catch (Throwable e) {            
+            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+            request.setAttribute("DisplayException", e);
+            mLogger.error("ERROR preparing for page execution", e);
+        }
+        
+        return outty;
+    }
+    
+    
+    /**
+     * Prepare the requested page for execution by setting content type
+     * and populating velocity context.
+     */
+    protected Template prepareForPageExecution(Context ctx,
+                                            RollerRequest rreq,
+                                            HttpServletResponse response,
+                                            org.apache.roller.pojos.Template page) 
+            
+         throws ResourceNotFoundException, RollerException {
+        
+        Template outty = null;
+        
+        // if page has an extension - use that to set the contentType
+        String pageLink = page.getLink();
+        String mimeType = getServletConfig().getServletContext().getMimeType(pageLink);
+        if(mimeType != null) {
+            // we found a match ... set the content type
+            response.setContentType(mimeType);
+        }
+        
+        // Made it this far, populate the Context
+        ContextLoader.setupContext( ctx, rreq, response );
+        
+        try {
+            outty = getTemplate(page.getId(), "UTF-8");
+        } catch(ResourceNotFoundException ex) {
+            // just rethrow
+            throw ex;
+        } catch(Exception ex) {
+            // wrap this as a roller exception
+            throw new RollerException("Error getting velocity template", ex);
+        }
+        
+        return outty;
+    }
+    
+    
+    /**
+     * Load the decorator template and apply it.  If there is no user specified
+     * decorator then the default decorator is applied.
+     */
+    protected Template findDecorator(WebsiteData website, String decorator_name)
+            throws ResourceNotFoundException, RollerException {
+        
+        Template decorator = null;
+        org.apache.roller.pojos.Template decorator_template = null;
+        
+        // check for user-specified decorator
+        if (decorator_name != null) {
+            decorator_template = website.getPageByName(decorator_name);
+        }
+        
+        // if no user-specified decorator try default page-name
+        if (decorator_template == null) {
+            decorator_template = website.getPageByName("_decorator");
+        }
+        
+        // try loading Template
+        if (decorator_template != null) {
+            try {
+                decorator = getTemplate(decorator_template.getId(), "UTF-8");
+            } catch (Exception e) {
+                // it may not exist, so this is okay
+            }
+        }
+        
+        // couldn't find Template, load default "no-op" decorator
+        if (decorator == null) {
+            try {
+                decorator = getTemplate("/themes/noop_decorator.vm", "UTF-8");
+            } catch (ResourceNotFoundException ex) {
+                // just rethrow
+                throw ex;
+            } catch (Exception ex) {
+                // wrap as a RollerException
+                throw new RollerException("error getting no-op decorator", ex);
+            }
+        }
+        
+        return decorator;
+    }
+    
+    
+    /**
+     * Handle error in Velocity processing.
+     */
+    protected void error( HttpServletRequest req, HttpServletResponse res,
+            Exception e) throws ServletException, IOException {
+        
+        // this means there was an exception outside of the handleRequest()
+        // method which seems to always be some variant of SocketException
+        // so we just ignore it
+        
+        // make sure anyone downstream knows about the exception
+        req.setAttribute("DisplayException", e);
+    }
+    
+    
+    /**
+     * Override to prevent Velocity from putting "req" and "res" into the context.
+     * Allowing users access to the underlying Servlet objects is a security risk.
+     * If need access to request parameters, use $requestParameters.
+     */
+    protected Context createContext(
+            HttpServletRequest req,
+            HttpServletResponse res) {
+        
+        VelocityContext context = new VelocityContext();
+        context.put(REQUEST, new RequestWrapper(req.getParameterMap()));
+        return context;
+        
+    }
+    
+    /** Provide access to request params only, not actual request */
+    public static class RequestWrapper {
+        Map params = null;
+        public RequestWrapper(Map params) {
+            this.params = params;
+        }
+        public String getParameter(String key) {
+            String ret = null;
+            String[] array = (String[])params.get(key);
+            if (array != null && array.length > 0) {
+                ret = array[0];
+            }
+            return ret;
+        }
+    }
+}

Added: incubator/roller/trunk/src/org/apache/roller/presentation/velocity/PlanetFeedServlet.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/apache/roller/presentation/velocity/PlanetFeedServlet.java?rev=398712&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/apache/roller/presentation/velocity/PlanetFeedServlet.java (added)
+++ incubator/roller/trunk/src/org/apache/roller/presentation/velocity/PlanetFeedServlet.java Mon May  1 15:23:02 2006
@@ -0,0 +1,130 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+*  contributor license agreements.  The ASF licenses this file to You
+* under the Apache License, Version 2.0 (the "License"); you may not
+* use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+package org.apache.roller.presentation.velocity;
+
+import java.io.IOException;
+import java.util.Date;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.velocity.Template;
+import org.apache.velocity.context.Context;
+import org.apache.velocity.servlet.VelocityServlet;
+import org.apache.roller.RollerException;
+import org.apache.roller.config.RollerRuntimeConfig;
+import org.apache.roller.model.PlanetManager;
+import org.apache.roller.model.RollerFactory;
+import org.apache.roller.presentation.RollerRequest;
+import org.apache.roller.util.Utilities;
+
+/////////////////////////////////////////////////////////////////////////////
+/**
+ * Planet Roller (i.e. NOT for Planet Tool) RSS feed. 
+ * @author David M Johnson
+ * @web.servlet name="PlanetFeedServlet"
+ * @web.servlet-mapping url-pattern="/planetrss/*"
+ */
+public class PlanetFeedServlet extends VelocityServlet
+{
+    private static Log mLogger = 
+        LogFactory.getFactory().getInstance(RollerRequest.class);
+    
+    public Template handleRequest(HttpServletRequest request,
+                                  HttpServletResponse response, Context context)
+    {
+        RollerRequest rreq = null;
+        try
+        {
+            rreq = RollerRequest.getRollerRequest(request, getServletContext());
+        }
+        catch (RollerException e)
+        {
+            // An error initializing the request is considered to be a 404
+            if (mLogger.isDebugEnabled())
+            {
+                mLogger.debug("RollerRequest threw Exception", e);
+            }
+            try
+            {
+                response.sendError(HttpServletResponse.SC_NOT_FOUND);
+            }
+            catch (IOException e1)
+            {
+                if (mLogger.isDebugEnabled())
+                {
+                    mLogger.debug("IOException sending error", e);
+                }
+            }
+            return null;
+        }
+        try
+        {
+            response.setContentType("application/rss+xml;charset=utf-8");
+            PlanetManager planet = 
+                RollerFactory.getRoller().getPlanetManager();
+            if (request.getParameter("group") != null) 
+            {
+                context.put("group", 
+                        planet.getGroup(request.getParameter("group")));
+            }
+            context.put("planet", planet);
+            context.put("date", new Date());
+            context.put("utilities", new Utilities());
+            
+            int entryCount = 
+                RollerRuntimeConfig.getIntProperty("site.newsfeeds.defaultEntries");
+            int maxEntries = 
+                RollerRuntimeConfig.getIntProperty("site.newsfeeds.maxEntries");
+            String sCount = request.getParameter("count");
+            if (sCount!=null)
+            {
+                try
+                {
+                    entryCount = Integer.parseInt(sCount);
+                }
+                catch (NumberFormatException e)
+                {
+                    mLogger.warn("Improperly formatted count parameter");
+                }
+                if ( entryCount > maxEntries ) entryCount = maxEntries;
+                if ( entryCount < 0 ) entryCount = 0;
+            }
+            context.put("entryCount", new Integer(entryCount));            
+            
+            return getTemplate("planetrss.vm");
+        }
+        catch (Exception e)
+        {
+            mLogger.error("ERROR in PlanetFeedServlet", e);
+        }
+        return null;
+    }
+
+    //------------------------------------------------------------------------
+    /**
+     * Handle error in Velocity processing.
+     */
+    protected void error( HttpServletRequest req, HttpServletResponse res,
+        Exception e) throws ServletException, IOException
+    {
+        mLogger.warn("ERROR in PlanetFeedServlet",e);
+    }
+}
+

Added: incubator/roller/trunk/src/org/apache/roller/presentation/velocity/PlanetPageModel.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/apache/roller/presentation/velocity/PlanetPageModel.java?rev=398712&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/apache/roller/presentation/velocity/PlanetPageModel.java (added)
+++ incubator/roller/trunk/src/org/apache/roller/presentation/velocity/PlanetPageModel.java Mon May  1 15:23:02 2006
@@ -0,0 +1,86 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+*  contributor license agreements.  The ASF licenses this file to You
+* under the Apache License, Version 2.0 (the "License"); you may not
+* use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+
+package org.apache.roller.presentation.velocity;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.roller.model.PlanetManager;
+import org.apache.roller.model.RollerFactory;
+import org.apache.roller.pojos.PlanetGroupData;
+import org.apache.roller.pojos.PlanetSubscriptionData;
+import org.apache.roller.presentation.RollerRequest;
+
+/**
+ * Allow Roller page templates to get the main Planet aggregation (the 'all'
+ * and 'external' group), custom aggregations, specified by handle, and 
+ * subscription entries (specified by feedUrl). 
+ * @author Dave Johnson
+ */
+public class PlanetPageModel extends PageModel 
+{
+    PlanetManager planetManager = null;
+    public void init(RollerRequest rreq)
+    {
+        super.init(rreq);
+        try 
+        {
+            planetManager = RollerFactory.getRoller().getPlanetManager();
+        }
+        catch (Exception e)
+        {
+            mLogger.error("ERROR initializing page model",e);
+        }
+    }
+    /** 
+     * Get main aggregation (of 'all' and 'external' groups) 
+     * @returns List of PlanetEntryData objects
+     */
+    public List getPlanetAggregation(int max) throws Exception
+    {
+        return planetManager.getAggregation(max);
+    }
+    /** 
+     * Get aggregation by group handle 
+     * @returns List of PlanetEntryData objects
+     */
+    public List getPlanetAggregation(String groupHandle, int max) throws Exception
+    {
+        List list = new ArrayList();
+        PlanetGroupData group = planetManager.getGroup(groupHandle);
+        if (group != null) 
+        {
+            list = planetManager.getAggregation(group, max);
+        }
+        return list;
+    }
+    /** 
+     * Get entries in a subscription specified by feedUrl.
+     * @returns List of PlanetEntryData objects
+     */
+    public List getPlanetSubscriptionEntries(String feedUrl, int max) throws Exception 
+    {
+        List list = new ArrayList();
+        PlanetSubscriptionData sub = planetManager.getSubscription(feedUrl);
+        if (sub != null)
+        {
+            list = sub.getEntries();
+        }
+        return list;
+    }
+}

Added: incubator/roller/trunk/src/org/apache/roller/presentation/velocity/PreviewServlet.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/apache/roller/presentation/velocity/PreviewServlet.java?rev=398712&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/apache/roller/presentation/velocity/PreviewServlet.java (added)
+++ incubator/roller/trunk/src/org/apache/roller/presentation/velocity/PreviewServlet.java Mon May  1 15:23:02 2006
@@ -0,0 +1,182 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+*  contributor license agreements.  The ASF licenses this file to You
+* under the Apache License, Version 2.0 (the "License"); you may not
+* use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+package org.apache.roller.presentation.velocity;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import org.apache.velocity.Template;
+import org.apache.velocity.context.Context;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.jsp.JspFactory;
+import javax.servlet.jsp.PageContext;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.velocity.exception.ResourceNotFoundException;
+import org.apache.roller.RollerException;
+import org.apache.roller.ThemeNotFoundException;
+import org.apache.roller.model.Roller;
+import org.apache.roller.model.RollerFactory;
+import org.apache.roller.model.ThemeManager;
+import org.apache.roller.model.UserManager;
+import org.apache.roller.pojos.Theme;
+import org.apache.roller.pojos.UserData;
+import org.apache.roller.pojos.WebsiteData;
+import org.apache.roller.presentation.RollerRequest;
+
+
+/**
+ * Allow users to preview what their blog would look like in a given theme.
+ *
+ * @web.servlet name="PreviewServlet" load-on-startup="1"
+ * @web.servlet-mapping url-pattern="/preview/*"
+ */
+public class PreviewServlet extends PageServlet {
+    
+    private static Log mLogger = LogFactory.getLog(PreviewServlet.class);
+    
+    
+    /**
+     * We function exactly like the PageServlet except that we temporarily
+     * modify the users theme for this request.
+     */
+    public Template handleRequest( HttpServletRequest request,
+                                HttpServletResponse response,
+                                Context ctx ) 
+            throws Exception {
+        
+        Theme previewTheme = null;
+        
+        // try getting the preview theme
+        String themeName = request.getParameter("theme");
+        if (themeName != null) {
+            try {
+                Roller roller = RollerFactory.getRoller();
+                ThemeManager themeMgr = roller.getThemeManager();
+                previewTheme = themeMgr.getTheme(themeName);
+                
+            } catch(ThemeNotFoundException tnfe) {
+                // bogus theme specified ... don't worry about it
+                // possibly "custom", but we'll handle that below
+            } catch(RollerException re) {
+                
+                response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+                request.setAttribute("DisplayException", re);
+                mLogger.error("EXCEPTION: in RollerServlet", re);
+            }
+        }
+        
+        if((previewTheme == null || !previewTheme.isEnabled()) &&
+                !themeName.equals(Theme.CUSTOM)) {
+            
+            // if we don't have a valid preview theme then
+            // leave it up to our parent
+            return super.handleRequest(request, response, ctx);
+            
+        }
+        
+        Template outty = null;
+        RollerRequest rreq = null;
+        
+        // first off lets parse the incoming request and validate it
+        try {
+            PageContext pageContext =
+                    JspFactory.getDefaultFactory().getPageContext(
+                    this, request, response,"", true, 8192, true);
+            rreq = RollerRequest.getRollerRequest(pageContext);
+        } catch (RollerException e) {
+            
+            // An error initializing the request is considered to be a 404
+            response.sendError(HttpServletResponse.SC_NOT_FOUND);
+            request.setAttribute("DisplayException", e);
+            
+            return null;
+        }
+        
+        
+        // request appears to be valid, lets render
+        try {
+            UserManager userMgr = RollerFactory.getRoller().getUserManager();
+            
+            WebsiteData website = null;
+            if (request.getAttribute(RollerRequest.OWNING_WEBSITE) != null) 
+            {
+                UserData user = (UserData)
+                    request.getAttribute(RollerRequest.OWNING_WEBSITE);
+            } 
+            else 
+            {
+                website = rreq.getWebsite();
+            }
+            
+            // construct a temporary Website object for this request
+            // and set the EditorTheme to our previewTheme
+            WebsiteData tmpWebsite = new WebsiteData();
+            tmpWebsite.setData(website);
+            if(previewTheme != null)
+                tmpWebsite.setEditorTheme(previewTheme.getName());
+            else
+                tmpWebsite.setEditorTheme(Theme.CUSTOM);
+            
+            org.apache.roller.pojos.Template page = null;
+            
+            page = tmpWebsite.getDefaultPage();
+            
+            // Still no page ID ... probably someone with no templates
+            // trying to preview a "custom" theme
+            if ( page == null ) {
+                // lets just call it a 404 and return
+                response.sendError(HttpServletResponse.SC_NOT_FOUND);
+                return null;
+            }
+            
+            // update our roller request object
+            rreq.setPage(page);
+            rreq.setWebsite(tmpWebsite);
+
+            // this sets up the page we want to render
+            outty = prepareForPageExecution(ctx, rreq, response, page);
+            
+            // if there is a decorator template then apply it
+            if (website != null) {
+                // parse/merge Page template
+                StringWriter sw = new StringWriter();
+                outty.merge(ctx, sw);
+                ctx.put("decorator_body", sw.toString());
+                
+                // replace outty with decorator Template
+                outty = findDecorator(tmpWebsite, (String) ctx.get("decorator"));
+            }
+            
+        } catch(ResourceNotFoundException rnfe ) {
+            
+            response.sendError(HttpServletResponse.SC_NOT_FOUND);
+            request.setAttribute("DisplayException", rnfe);
+            mLogger.warn("ResourceNotFound: "+ request.getRequestURL());
+            mLogger.debug(rnfe);
+        } catch(Exception e) {
+            
+            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+            request.setAttribute("DisplayException", e);
+            mLogger.error("Unexpected exception", e);
+        }
+        
+        return outty;
+    }
+}
+

Added: incubator/roller/trunk/src/org/apache/roller/presentation/velocity/RSDServlet.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/apache/roller/presentation/velocity/RSDServlet.java?rev=398712&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/apache/roller/presentation/velocity/RSDServlet.java (added)
+++ incubator/roller/trunk/src/org/apache/roller/presentation/velocity/RSDServlet.java Mon May  1 15:23:02 2006
@@ -0,0 +1,132 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+*  contributor license agreements.  The ASF licenses this file to You
+* under the Apache License, Version 2.0 (the "License"); you may not
+* use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+/*
+ * RSDServlet.java
+ *
+ * Created on December 14, 2005, 6:08 PM
+ */
+
+package org.apache.roller.presentation.velocity;
+
+import java.io.IOException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.velocity.Template;
+import org.apache.velocity.context.Context;
+import org.apache.velocity.exception.ResourceNotFoundException;
+import org.apache.velocity.servlet.VelocityServlet;
+import org.apache.roller.RollerException;
+import org.apache.roller.model.RollerFactory;
+import org.apache.roller.model.UserManager;
+import org.apache.roller.pojos.WebsiteData;
+import org.apache.roller.presentation.InvalidRequestException;
+import org.apache.roller.presentation.RollerContext;
+import org.apache.roller.presentation.WeblogRequest;
+
+
+/**
+ * Generates simple rsd feeds for a given weblog.
+ *
+ *
+ * @web.servlet name="RSDServlet" load-on-startup="0"
+ *
+ * @web.servlet-init-param name="org.apache.velocity.properties" 
+ * 		                  value="/WEB-INF/velocity.properties"
+ *  
+ * @web.servlet-mapping url-pattern="/rsd/*"
+ *
+ * @author Allen Gilliland
+ */
+public class RSDServlet extends VelocityServlet {
+    
+    private static Log mLogger = LogFactory.getLog(RSDServlet.class);
+    
+    
+    /**
+     * Process a request for a Weblog page.
+     */
+    public Template handleRequest(HttpServletRequest request,
+                                HttpServletResponse response, 
+                                Context ctx) 
+            throws IOException {
+        
+        Template template = null;
+        WeblogRequest weblogRequest = null;
+        WebsiteData weblog = null;
+        
+        // first off lets parse the incoming request and validate it
+        try {
+            weblogRequest = new WeblogRequest(request);
+            
+            // now make sure the specified weblog really exists
+            UserManager userMgr = RollerFactory.getRoller().getUserManager();
+            weblog = userMgr.getWebsiteByHandle(weblogRequest.getWeblogHandle(), Boolean.TRUE);
+            
+        } catch(InvalidRequestException ire) {
+            // An error initializing the request is considered to be a 404
+            response.sendError(HttpServletResponse.SC_NOT_FOUND);
+            request.setAttribute("DisplayException", ire);
+            mLogger.error("Bad Request: "+ire.getMessage());
+            
+            return null;
+            
+        } catch(RollerException re) {
+            // error looking up the weblog, we assume it doesn't exist
+            response.sendError(HttpServletResponse.SC_NOT_FOUND);
+            request.setAttribute("DisplayException", re);
+            mLogger.warn("Unable to lookup weblog ["+
+                    weblogRequest.getWeblogHandle()+"] "+re.getMessage());
+            
+            return null;
+        }
+        
+        
+        // request appears to be valid, lets render
+        try {
+            
+            // setup context
+            ctx.put("website", weblog);
+            
+            RollerContext rollerContext = new RollerContext();
+            ctx.put("absBaseURL", rollerContext.getAbsoluteContextUrl(request));
+            
+            // lookup our rsd template
+            template = getTemplate("/flavors/rsd.vm");
+            
+            // make sure response content type is properly set
+            response.setContentType("application/rsd+xml");
+            
+        } catch(ResourceNotFoundException rnfe ) {
+            
+            response.sendError(HttpServletResponse.SC_NOT_FOUND);
+            request.setAttribute("DisplayException", rnfe);
+            mLogger.warn("ResourceNotFound: "+ request.getRequestURL());
+            mLogger.debug(rnfe);
+        } catch(Exception e) {
+            
+            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+            request.setAttribute("DisplayException", e);
+            mLogger.error("Unexpected exception", e);
+        }
+        
+        return template;
+    }
+    
+}

Added: incubator/roller/trunk/src/org/apache/roller/presentation/velocity/RollerResourceLoader.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/apache/roller/presentation/velocity/RollerResourceLoader.java?rev=398712&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/apache/roller/presentation/velocity/RollerResourceLoader.java (added)
+++ incubator/roller/trunk/src/org/apache/roller/presentation/velocity/RollerResourceLoader.java Mon May  1 15:23:02 2006
@@ -0,0 +1,132 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+*  contributor license agreements.  The ASF licenses this file to You
+* under the Apache License, Version 2.0 (the "License"); you may not
+* use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+package org.apache.roller.presentation.velocity;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import org.apache.commons.collections.ExtendedProperties;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.velocity.exception.ResourceNotFoundException;
+import org.apache.velocity.runtime.resource.Resource;
+import org.apache.velocity.runtime.resource.loader.ResourceLoader;
+import org.apache.roller.RollerException;
+import org.apache.roller.model.RollerFactory;
+import org.apache.roller.pojos.WeblogTemplate;
+
+
+/**
+ * This is a simple template file loader that loads templates
+ * from the Roller instance instead of plain files.
+ *
+ * RollerResourceLoader makes use of RollerFactory.
+ *
+ * @author <a href="mailto:lance@brainopolis.com">Lance Lavandowska</a>
+ * @version $Id: RollerResourceLoader.java,v 1.9 2005/01/15 03:32:49 snoopdave Exp $
+ */
+public class RollerResourceLoader extends ResourceLoader {
+    
+    private static Log mLogger = LogFactory.getLog(RollerResourceLoader.class);
+    
+    
+    public void init(ExtendedProperties configuration) {
+        if (mLogger.isDebugEnabled()) {
+            mLogger.debug(configuration);
+        }
+    }
+    
+    
+    public boolean isSourceModified(Resource resource) {
+        return (resource.getLastModified() !=
+                readLastModified(resource, "checking timestamp"));
+    }
+    
+    
+    public long getLastModified(Resource resource) {
+        return readLastModified(resource, "getting timestamp");
+    }
+    
+    /**
+     * Get an InputStream so that the Runtime can build a
+     * template with it.
+     *
+     * @param name name of template
+     * @return InputStream containing template
+     */
+    public InputStream getResourceStream(String name)
+            throws ResourceNotFoundException {
+        
+        if (name == null || name.length() == 0) {
+            throw new ResourceNotFoundException("Need to specify a template name!");
+        }
+        
+        try {
+            WeblogTemplate page = 
+                    RollerFactory.getRoller().getUserManager().getPage(name);
+            
+            if (page == null) {
+                throw new ResourceNotFoundException(
+                        "RollerResourceLoader: page \"" +
+                        name + "\" not found");
+            }
+            return new ByteArrayInputStream( page.getContents().getBytes("UTF-8") );
+        } catch (UnsupportedEncodingException uex) {
+            // This should never actually happen.  We expect UTF-8 in all JRE installation.
+            // This rethrows as a Runtime exception after logging.
+            mLogger.error(uex);
+            throw new RuntimeException(uex);
+        } catch (RollerException re) {
+            String msg = "RollerResourceLoader Error: " +
+                    "database problem trying to load resource " + name;
+            mLogger.error( msg, re );
+            throw new ResourceNotFoundException(msg);
+        }
+    }
+    
+    
+    /**
+     * Fetches the last modification time of the resource
+     *
+     * @param resource Resource object we are finding timestamp of
+     * @param i_operation string for logging, indicating caller's intention
+     *
+     * @return timestamp as long
+     */
+    private long readLastModified(Resource resource, String i_operation) {
+        
+        /*
+         *  get the template name from the resource
+         */
+        String name = resource.getName();
+        try {
+            WeblogTemplate page = 
+                    RollerFactory.getRoller().getUserManager().getPage(name);
+            
+            if (mLogger.isDebugEnabled()) {
+                mLogger.debug(name + ": resource=" + resource.getLastModified() +
+                        " vs. page=" + page.getLastModified().getTime());
+            }
+            return page.getLastModified().getTime();
+        } catch (RollerException re) {
+            mLogger.error( "Error " + i_operation, re );
+        }
+        return 0;
+    }
+    
+}

Added: incubator/roller/trunk/src/org/apache/roller/presentation/velocity/ThemeResourceLoader.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/apache/roller/presentation/velocity/ThemeResourceLoader.java?rev=398712&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/apache/roller/presentation/velocity/ThemeResourceLoader.java (added)
+++ incubator/roller/trunk/src/org/apache/roller/presentation/velocity/ThemeResourceLoader.java Mon May  1 15:23:02 2006
@@ -0,0 +1,147 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+*  contributor license agreements.  The ASF licenses this file to You
+* under the Apache License, Version 2.0 (the "License"); you may not
+* use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+/*
+ * ThemeResourceLoader.java
+ *
+ * Created on June 28, 2005, 12:25 PM
+ */
+
+package org.apache.roller.presentation.velocity;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import org.apache.commons.collections.ExtendedProperties;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.velocity.exception.ResourceNotFoundException;
+import org.apache.velocity.runtime.resource.Resource;
+import org.apache.velocity.runtime.resource.loader.ResourceLoader;
+import org.apache.roller.RollerException;
+import org.apache.roller.ThemeNotFoundException;
+import org.apache.roller.model.RollerFactory;
+import org.apache.roller.model.ThemeManager;
+import org.apache.roller.pojos.Theme;
+import org.apache.roller.pojos.ThemeTemplate;
+
+
+/**
+ * The ThemeResourceLoader is a Velocity template loader which loads
+ * templates from shared themes.
+ *
+ * @author Allen Gilliland
+ */
+public class ThemeResourceLoader extends ResourceLoader {
+    
+    private static Log mLogger = 
+        LogFactory.getFactory().getInstance(ThemeResourceLoader.class);
+        
+    
+    public void init(ExtendedProperties configuration) {
+        mLogger.debug(configuration);
+    }
+    
+    
+    public InputStream getResourceStream( String name )
+        throws ResourceNotFoundException {
+        
+        mLogger.debug("Looking up resource named ... "+name);
+        
+        if (name == null || name.length() < 1) {
+            throw new ResourceNotFoundException("Need to specify a template name!");
+        }
+        
+        try {
+            // parse the name ... theme templates name are <theme>:<template>
+            String[] split = name.split(":", 2);
+            if(split.length < 2)
+                throw new ResourceNotFoundException("Invalid ThemeRL key "+name);
+            
+            // lookup the template from the proper theme
+            ThemeManager themeMgr = RollerFactory.getRoller().getThemeManager();
+            Theme theme = themeMgr.getTheme(split[0]);
+            ThemeTemplate template = theme.getTemplate(split[1]);
+            
+            if(template == null)
+                throw new ResourceNotFoundException("Template ["+split[1]+
+                        "] doesn't seem to be part of theme ["+split[0]+"]");
+            
+            mLogger.debug("Resource found!");
+            
+            // return the input stream
+            return new ByteArrayInputStream(template.getContents().getBytes("UTF-8"));
+            
+        } catch (UnsupportedEncodingException uex) {
+            // We expect UTF-8 in all JRE installation.
+            // This rethrows as a Runtime exception after logging.
+            mLogger.error(uex);
+            throw new RuntimeException(uex);
+           
+        } catch (ThemeNotFoundException tnfe) {
+            String msg = "ThemeResourceLoader Error: " + tnfe.getMessage();
+            mLogger.error(msg, tnfe);
+            throw new ResourceNotFoundException(msg);
+            
+        } catch (RollerException re) {
+            String msg = "RollerResourceLoader Error: " + re.getMessage();
+            mLogger.error( msg, re );
+            throw new ResourceNotFoundException(msg);
+        }
+    }
+    
+    
+    public boolean isSourceModified(Resource resource) {
+        return (resource.getLastModified() != this.getLastModified(resource));
+    }
+    
+    
+    public long getLastModified(Resource resource) {
+        long last_mod = 0;
+        String name = resource.getName();
+        
+        mLogger.debug("Checking last modified time for resource named ... "+name);
+        
+        if (name == null || name.length() < 1)
+            return last_mod;
+        
+        try {
+            // parse the name ... theme templates name are <theme>:<template>
+            String[] split = name.split(":", 2);
+            if(split.length < 2)
+                return last_mod;
+            
+            // lookup the template from the proper theme
+            ThemeManager themeMgr = RollerFactory.getRoller().getThemeManager();
+            Theme theme = themeMgr.getTheme(split[0]);
+            ThemeTemplate template = theme.getTemplate(split[1]);
+            
+            if(template == null)
+                return last_mod;
+            
+            last_mod = template.getLastModified().getTime();
+            
+        } catch (ThemeNotFoundException tnfe) {
+            // ignore
+        } catch (RollerException re) {
+            // we don't like to see this happen, but oh well
+        }
+        
+        return last_mod;
+    }
+    
+}

Added: incubator/roller/trunk/src/org/apache/roller/presentation/velocity/WebappResourceLoader.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/apache/roller/presentation/velocity/WebappResourceLoader.java?rev=398712&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/apache/roller/presentation/velocity/WebappResourceLoader.java (added)
+++ incubator/roller/trunk/src/org/apache/roller/presentation/velocity/WebappResourceLoader.java Mon May  1 15:23:02 2006
@@ -0,0 +1,137 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+*  contributor license agreements.  The ASF licenses this file to You
+* under the Apache License, Version 2.0 (the "License"); you may not
+* use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+package org.apache.roller.presentation.velocity;
+
+import java.io.InputStream;
+
+import javax.servlet.ServletContext;
+
+import org.apache.commons.collections.ExtendedProperties;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.velocity.exception.ResourceNotFoundException;
+import org.apache.velocity.runtime.resource.Resource;
+import org.apache.velocity.runtime.resource.loader.ResourceLoader;
+import org.apache.roller.presentation.RollerContext;
+
+/**
+ * Tries to load Velocity resources from the Webapp.
+ * This class borrows heavily from
+ * org.apache.velocity.tools.view.servlet.WebappLoader
+ * http://cvs.apache.org/viewcvs/jakarta-velocity-
+ * tools/view/src/java/org/apache/velocity/tools/view/servlet/WebappLoader.java?
+ * rev=1.1.1.1&content-type=text/vnd.viewcvs-markup
+ * 
+ * @author Lance Lavandowska
+ */
+public class WebappResourceLoader extends ResourceLoader
+{
+	private static Log mLogger = 
+		LogFactory.getFactory().getInstance(WebappResourceLoader.class);
+    
+    private static ServletContext mContext = null; 
+  
+	/**
+	 * @see org.apache.velocity.runtime.resource.loader.ResourceLoader#init(org.apache.commons.collections.ExtendedProperties)
+	 */
+	public void init(ExtendedProperties arg0)
+	{
+		rsvc.info("WebappResourceLoader : initialization starting.");
+
+		this.getContext();
+		if (mContext == null)
+		{
+			mLogger.warn("WebappResourceLoader : Unable to find ServletContext!");
+		}
+
+		rsvc.info("WebappResourceLoader : initialization complete.");
+	}
+	
+	private ServletContext getContext()
+	{
+		if (mContext == null)
+		{
+			mContext = RollerContext.getServletContext();
+		}
+		return mContext;
+	}
+	
+	public static void setServletContext(ServletContext context)
+	{
+		mContext = context;
+	}
+	
+	/**
+	 * @see org.apache.velocity.runtime.resource.loader.ResourceLoader#getResourceStream(java.lang.String)
+	 */
+	public InputStream getResourceStream(String name)
+		throws ResourceNotFoundException
+	{
+		InputStream result = null;
+        
+		if (name == null || name.length() == 0)
+		{
+			throw new ResourceNotFoundException ("No template name provided");
+		}
+        
+		try 
+		{
+			if (!name.startsWith("/"))
+				name = "/" + name;
+
+			result = getContext().getResourceAsStream( name );
+		}
+		catch( NullPointerException npe)
+		{
+			String msg = "WebappResourceLoader.getResourceStream(): " + name;
+			if (mContext == null)
+			{
+				mLogger.info("WebappResourceLoader("+name+"): ServletContext is null");
+				msg += "\n\tServletContext is null";
+			}
+			throw new ResourceNotFoundException(msg);
+		}
+		catch( Exception fnfe )
+		{
+			/*
+			 *  log and convert to a general Velocity ResourceNotFoundException
+			 */            
+			throw new ResourceNotFoundException( fnfe.getMessage() );
+		}
+        
+		return result;
+	}
+	
+	/**
+	 * Defaults to return false.
+	 * @see org.apache.velocity.runtime.resource.loader.ResourceLoader#isSourceModified(org.apache.velocity.runtime.resource.Resource)
+	 */
+	public boolean isSourceModified(Resource arg0)
+	{
+		return false;
+	}
+	
+	/**
+	 * Defaults to return 0.
+	 * @see org.apache.velocity.runtime.resource.loader.ResourceLoader#getLastModified(org.apache.velocity.runtime.resource.Resource)
+	 */
+	public long getLastModified(Resource arg0)
+	{
+		return 0;
+	}
+}

Added: incubator/roller/trunk/src/org/apache/roller/presentation/velocity/export_atom.vm
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/apache/roller/presentation/velocity/export_atom.vm?rev=398712&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/apache/roller/presentation/velocity/export_atom.vm (added)
+++ incubator/roller/trunk/src/org/apache/roller/presentation/velocity/export_atom.vm Mon May  1 15:23:02 2006
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding='utf-8'?>
+<feed version="0.3" xmlns="http://purl.org/atom/ns#">
+	<!-- Special Archive version of Atom, created by Roller -->
+    <title>$utilities.textToHTML($website.name,true)</title>
+    <tagline>$utilities.textToHTML($website.description,true)</tagline>
+    <modified>$utilities.formatIso8601Date($updateTime)</modified>
+
+    #foreach( $entry in $entries )
+    <entry xmlns="http://purl.org/atom/ns#" >
+        <id>$entry.id</id>
+        <title>$utilities.textToHTML($entry.title,true)</title>
+        <subject>$entry.category.path</subject>
+
+        <author>
+          <name>$fullName</name>
+        </author>
+
+        <issued>$utilities.formatIso8601Date($entry.pubTime)</issued>
+        <created>$utilities.formatIso8601Date($entry.pubTime)</created>
+        <modified>$utilities.formatIso8601Date($entry.updateTime)</modified>
+
+        <content><![CDATA[$entry.text]]></content>
+    </entry>
+        ## use "experimental" form of Comment 'annotation'
+        #foreach( $comment in $entry.comments )
+        <entry xmlns="http://purl.org/atom/ns#" >
+            <id>$comment.id</id>
+            <title>$utilities.truncateNicely($comment.content,40,50,"...")</title>
+
+            <author>
+              <name>$comment.name</name>
+              <url>$comment.url</url>
+              <email>$comment.email</email>
+            </author>
+            <issued>$utilities.formatIso8601Date($comment.postTime)</issued>
+            <content><![CDATA[$comment.content]]></content>
+            <annotate type="comment" rel="parent">$entry.id</annotate>
+        </entry>
+        #end
+    #end
+</feed>
\ No newline at end of file

Added: incubator/roller/trunk/src/org/apache/roller/presentation/velocity/export_rss.vm
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/apache/roller/presentation/velocity/export_rss.vm?rev=398712&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/apache/roller/presentation/velocity/export_rss.vm (added)
+++ incubator/roller/trunk/src/org/apache/roller/presentation/velocity/export_rss.vm Mon May  1 15:23:02 2006
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<rss version="2.0">
+<channel>
+  <!-- Special Archive version of RSS2, created by Roller -->
+  <title>$utilities.textToHTML($website.name,true)</title>
+  <description>$utilities.textToHTML($website.description,true)</description>
+  <language>$website.locale</language>
+  <copyright>Copyright #formatDate("yyyy" $now)</copyright>
+
+  #foreach( $entry in $entries )
+  <item>
+    <title>$utilities.textToHTML($entry.title,true)</title>
+    <description><![CDATA[$entry.text]]></description>
+    <category>$entry.category.path</category>
+    <pubDate>$utilities.formatRfc822Date($entry.pubTime)</pubDate>
+    <guid isPermaLink="false">$entry.id</guid>
+  </item>
+  #end
+</channel>
+</rss>
\ No newline at end of file

Added: incubator/roller/trunk/src/org/apache/roller/presentation/velocity/package.html
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/apache/roller/presentation/velocity/package.html?rev=398712&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/apache/roller/presentation/velocity/package.html (added)
+++ incubator/roller/trunk/src/org/apache/roller/presentation/velocity/package.html Mon May  1 15:23:02 2006
@@ -0,0 +1,27 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  The ASF licenses this file to You
+  under the Apache License, Version 2.0 (the "License"); you may not
+  use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.  For additional information regarding
+  copyright in this work, please see the NOTICE file in the top level
+  directory of this distribution.
+-->
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+  <title></title>
+</head>
+<body>
+Velocity based page-engine: servlets and supporting model classes.
+
+</body>
+</html>

Added: incubator/roller/trunk/src/org/apache/roller/presentation/velocity/roller-velocity.png
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/apache/roller/presentation/velocity/roller-velocity.png?rev=398712&view=auto
==============================================================================
Binary file - no diff available.

Propchange: incubator/roller/trunk/src/org/apache/roller/presentation/velocity/roller-velocity.png
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: incubator/roller/trunk/src/org/apache/roller/presentation/weblog/actions/BasePingTargetsAction.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/apache/roller/presentation/weblog/actions/BasePingTargetsAction.java?rev=398712&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/apache/roller/presentation/weblog/actions/BasePingTargetsAction.java (added)
+++ incubator/roller/trunk/src/org/apache/roller/presentation/weblog/actions/BasePingTargetsAction.java Mon May  1 15:23:02 2006
@@ -0,0 +1,410 @@
+/*
+ * Copyright 2005-2006 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.roller.presentation.weblog.actions;
+
+import java.util.List;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.logging.Log;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.action.ActionMessage;
+import org.apache.struts.action.ActionMessages;
+import org.apache.struts.actions.DispatchAction;
+import org.apache.roller.RollerException;
+import org.apache.roller.model.PingTargetManager;
+import org.apache.roller.model.RollerFactory;
+import org.apache.roller.pojos.WebsiteData;
+import org.apache.roller.pojos.PingTargetData;
+import org.apache.roller.presentation.BasePageModel;
+import org.apache.roller.presentation.forms.PingTargetForm;
+import org.apache.roller.presentation.RollerRequest;
+
+
+/**
+ * Base class for both common and custom ping target operations.  The methods 
+ * here apply to creating, editing and removing ping targets.  Operations for 
+ * maintaining automatic ping configurations are handled by 
+ * {@link PingSetupAction}.
+ */
+public abstract class BasePingTargetsAction extends DispatchAction
+{
+    // These are expected to be defined forwards by the concrete subclass actions.
+    protected static final String VIEW_PAGE = "pingTargets.page";
+    protected static final String PING_TARGET_EDIT_PAGE = "pingTargetEdit.page";
+    protected static final String PING_TARGET_DELETE_PAGE = "pingTargetDeleteOK.page";
+    protected static final String ACCESS_DENIED_PAGE = "access-denied";
+    
+    public abstract String getPingTargetsTitle();
+    public abstract String getPingTargetEditTitle();
+    public abstract String getPingTargetDeleteOKTitle();    
+
+    public BasePingTargetsAction() {
+
+    }
+
+    /**
+     * Implements the default action (view) if the method is not specified.
+     * @param mapping
+     * @param actionForm
+     * @param request
+     * @param response
+     * @return the same result as <code>view()</code>
+     * @throws Exception
+     * @see org.apache.struts.actions.DispatchAction#unspecified(org.apache.struts.action.ActionMapping, org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
+     */
+    protected ActionForward unspecified(ActionMapping mapping,
+                                        ActionForm actionForm,
+                                        HttpServletRequest request,
+                                        HttpServletResponse response)
+        throws Exception
+    {
+        return view(mapping, actionForm, request, response);
+    }
+
+    /**
+     * Display the ping targets.
+     *
+     * @param mapping
+     * @param form
+     * @param req
+     * @param res
+     * @return forward to the ping targets page
+     * @throws Exception
+     */
+    public ActionForward view(ActionMapping mapping, ActionForm form,
+                              HttpServletRequest req, HttpServletResponse res)
+        throws Exception
+    {
+        ActionForward forward = mapping.findForward(VIEW_PAGE);
+        RollerRequest rreq = RollerRequest.getRollerRequest(req);
+        try
+        {
+            BasePageModel pageModel = 
+                new BasePageModel(getPingTargetsTitle(), req, res, mapping);
+            req.setAttribute("model",pageModel);            
+            if (!hasRequiredRights(rreq, rreq.getWebsite()))
+            {
+                return mapping.findForward(ACCESS_DENIED_PAGE);
+            }
+
+            List pingTargets = getPingTargets(rreq);
+            req.setAttribute("pingTargets", pingTargets);
+            return forward;
+        }
+        catch (Exception e)
+        {
+            getLogger().error("ERROR in action", e);
+            throw new ServletException(e);
+        }
+    }
+
+    /**
+     * Save a ping target, new or existing (depending on whether the id is non-empty).
+     *
+     * @param mapping
+     * @param form
+     * @param req
+     * @param res
+     * @return the result of <code>view()</code> after the target is saved.
+     * @throws Exception
+     */
+    public ActionForward save(ActionMapping mapping, ActionForm form,
+                              HttpServletRequest req, HttpServletResponse res)
+        throws Exception
+    {
+        RollerRequest rreq = RollerRequest.getRollerRequest(req);
+        PingTargetManager pingTargetMgr = 
+                RollerFactory.getRoller().getPingTargetManager();
+        PingTargetForm pingTargetForm = (PingTargetForm) form;
+        try
+        {
+            BasePageModel pageModel = 
+                new BasePageModel(getPingTargetEditTitle(), req, res, mapping);
+            req.setAttribute("model",pageModel);            
+            if (!hasRequiredRights(rreq, rreq.getWebsite()))
+            {
+                return mapping.findForward(ACCESS_DENIED_PAGE);
+            }
+
+            PingTargetData pingTarget = null;
+            String pingTargetId = pingTargetForm.getId();
+            if (pingTargetId != null && pingTargetId.length() > 0)
+            {
+                pingTarget = pingTargetMgr.getPingTarget(
+                        pingTargetForm.getId());
+                if (pingTarget == null) 
+                    throw new RollerException(
+                            "No such ping target id: " + pingTargetId);
+                pingTargetForm.copyTo(pingTarget, req.getLocale());
+            }
+            else
+            {
+                pingTarget = createPingTarget(rreq, pingTargetForm);
+            }
+
+            // Call private helper to validate ping target
+            // If there are errors, go back to the target edit page.
+            ActionMessages errors = validate(rreq, pingTarget);
+            if (!errors.isEmpty()) {
+                saveErrors(rreq.getRequest(), errors);
+                return mapping.findForward(PING_TARGET_EDIT_PAGE);
+            }
+
+            // Appears to be ok.  
+            // Save it, commit and return refreshed view of target list.
+            pingTargetMgr.savePingTarget(pingTarget);
+            RollerFactory.getRoller().flush();
+            
+            ActionMessages msgs = new ActionMessages();
+            msgs.add(ActionMessages.GLOBAL_MESSAGE, 
+                    new ActionMessage("pingTarget.saved"));
+            saveMessages(req, msgs);     
+            
+            return view(mapping, form, req, res);
+        }
+        catch (Exception e)
+        {
+            getLogger().error("ERROR in action", e);
+            throw new ServletException(e);
+        }
+    }
+
+    /**
+     * Add a new ping target. Loads the edit view blank.
+     *
+     * @param mapping
+     * @param form
+     * @param req
+     * @param res
+     * @return the edit page (blank)
+     * @throws Exception
+     */
+    public ActionForward addNew(ActionMapping mapping, ActionForm form,
+                                HttpServletRequest req, HttpServletResponse res)
+        throws Exception
+    {
+        BasePageModel pageModel = 
+            new BasePageModel(getPingTargetEditTitle(), req, res, mapping);
+        req.setAttribute("model",pageModel);            
+        return mapping.findForward(PING_TARGET_EDIT_PAGE);
+    }
+
+    /**
+     * Edit a ping target (load edit view)
+     *
+     * @param mapping
+     * @param form
+     * @param req
+     * @param res
+     * @return the edit view with the form populated with the ping target specified by the id in the request.
+     * @throws Exception
+     */
+    public ActionForward editSelected(ActionMapping mapping, ActionForm form,
+                                      HttpServletRequest req, HttpServletResponse res)
+        throws Exception
+    {
+        ActionForward forward = mapping.findForward(PING_TARGET_EDIT_PAGE);
+        RollerRequest rreq = RollerRequest.getRollerRequest(req);
+        try
+        {
+            BasePageModel pageModel = 
+                new BasePageModel(getPingTargetEditTitle(), req, res, mapping);
+            req.setAttribute("model",pageModel);            
+            if (!hasRequiredRights(rreq, rreq.getWebsite()))
+            {
+                return mapping.findForward(ACCESS_DENIED_PAGE);
+            }
+            PingTargetData pingTarget = select(rreq);
+            ((PingTargetForm) form).copyFrom(pingTarget, req.getLocale());
+            return forward;
+        }
+        catch (Exception e)
+        {
+            getLogger().error("ERROR in action", e);
+            throw new ServletException(e);
+        }
+    }
+
+    /**
+     * Delete a ping target (load delete confirmation view).
+     *
+     * @param mapping
+     * @param form
+     * @param req
+     * @param res
+     * @return the delete confirmation view with the form populated with the ping target specified by the id in the request.
+     * @throws Exception
+     */
+    public ActionForward deleteSelected(ActionMapping mapping, ActionForm form,
+                                        HttpServletRequest req, HttpServletResponse res)
+        throws Exception
+    {
+        ActionForward forward = mapping.findForward(PING_TARGET_DELETE_PAGE);
+        RollerRequest rreq = RollerRequest.getRollerRequest(req);
+        try
+        {
+            BasePageModel pageModel = new BasePageModel(
+                    getPingTargetDeleteOKTitle(), req, res, mapping);
+            req.setAttribute("model",pageModel);            
+            if (!hasRequiredRights(rreq, rreq.getWebsite()))
+            {
+                return mapping.findForward(ACCESS_DENIED_PAGE);
+            }
+            PingTargetData pingTarget = select(rreq);
+            ((PingTargetForm) form).copyFrom(pingTarget, req.getLocale());
+            return forward;
+        }
+        catch (Exception e)
+        {
+            getLogger().error("ERROR in action", e);
+            throw new ServletException(e);
+        }
+    }
+
+    /**
+     * Delete a ping target (already confirmed).  This performs the actual deletion.
+     *
+     * @param mapping
+     * @param form
+     * @param req
+     * @param res
+     * @return the result of <code>view()</code> after the deletion
+     * @throws Exception
+     */
+    public ActionForward deleteConfirmed(ActionMapping mapping, ActionForm form,
+                                         HttpServletRequest req, HttpServletResponse res)
+        throws Exception
+    {
+        RollerRequest rreq = RollerRequest.getRollerRequest(req);
+        PingTargetForm pingTargetForm = (PingTargetForm) form;
+        PingTargetManager pingTargetMgr = RollerFactory.getRoller().getPingTargetManager();
+        try
+        {
+            if (!hasRequiredRights(rreq, rreq.getWebsite()))
+            {
+                return mapping.findForward(ACCESS_DENIED_PAGE);
+            }
+            String pingTargetId = pingTargetForm.getId();
+            if (pingTargetId == null || pingTargetId.length() == 0)
+            {
+                throw new RollerException("Missing ping target id.");
+            }
+            PingTargetData ping = pingTargetMgr.getPingTarget(pingTargetId);
+            pingTargetMgr.removePingTarget(ping);
+            RollerFactory.getRoller().flush();
+            return view(mapping, form, req, res);
+        }
+        catch (Exception e)
+        {
+            getLogger().error("ERROR in action", e);
+            throw new ServletException(e);
+        }
+    }
+
+    // TODO: Consider unifying with other RollerRequest methods
+    /**
+     * Helper to select the ping target specified by the id in the request.
+     * @param rreq
+     * @return the ping target specified by the id in the request
+     * @throws RollerException
+     */
+    private PingTargetData select(RollerRequest rreq) throws RollerException
+    {
+        String pingTargetId = rreq.getRequest().getParameter(RollerRequest.PINGTARGETID_KEY);
+        PingTargetManager pingTargetMgr = RollerFactory.getRoller().getPingTargetManager();
+        if (pingTargetId == null || pingTargetId.length() == 0)
+        {
+            throw new RollerException("Missing ping target id: " + pingTargetId);
+        }
+
+        PingTargetData pingTarget = pingTargetMgr.getPingTarget(pingTargetId);
+        if (pingTarget == null)
+        {
+            throw new RollerException("No such ping target id: " + pingTargetId);
+        }
+        return pingTarget;
+    }
+
+    /**
+     * Private helper to validate a ping target.
+     * @param rreq the request
+     * @param pingTarget the ping target to validate
+     * @return an <code>ActionMessages</code> object with 
+     *         <code>ActionMessage</code> for each error encountered, 
+     *         empty if no errors were encountered.
+     * @throws RollerException
+     */
+    private ActionMessages validate(
+        RollerRequest rreq, PingTargetData pingTarget) throws RollerException
+    {
+        ActionMessages errors = new ActionMessages();
+
+        PingTargetManager pingTargetMgr = 
+                RollerFactory.getRoller().getPingTargetManager();
+        if (!pingTargetMgr.isNameUnique(pingTarget))
+        {
+            errors.add(ActionMessages.GLOBAL_MESSAGE,
+                new ActionMessage("pingTarget.nameNotUnique"));
+        }
+        if (!pingTargetMgr.isUrlWellFormed(pingTarget))
+        {
+            errors.add(ActionMessages.GLOBAL_MESSAGE,
+                new ActionMessage("pingTarget.malformedUrl"));
+        } else if (!pingTargetMgr.isHostnameKnown(pingTarget))
+        {
+            errors.add(ActionMessages.GLOBAL_MESSAGE,
+                new ActionMessage("pingTarget.unknownHost"));
+        }
+        return errors;
+    }
+
+
+    /**
+     * Helper defined by the subclass to determine if user has adequate 
+     * rights for the action. This and the 
+     * {@link org.apache.roller.pojos.PingTargetData#canSave()} method determine the 
+     * access control for the action.
+     */
+    protected abstract boolean hasRequiredRights(
+            RollerRequest rreq, WebsiteData website) throws RollerException;
+
+    /**
+     * Get the logger from the concrete subclass
+     */
+    protected abstract Log getLogger();
+
+    /**
+     * Get the ping targets for the view.  This is implemented differently in 
+     * the concrete subclasses.
+     */
+    protected abstract List getPingTargets(RollerRequest rreq) 
+        throws RollerException;
+
+
+    /**
+     * Create a new ping target (blank). This is implemented differently in 
+     * the concrete subclasses.
+     */
+    protected abstract PingTargetData createPingTarget(
+        RollerRequest rreq, PingTargetForm pingTargetForm) 
+            throws RollerException;
+}

Added: incubator/roller/trunk/src/org/apache/roller/presentation/weblog/actions/CategoriesAction.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/apache/roller/presentation/weblog/actions/CategoriesAction.java?rev=398712&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/apache/roller/presentation/weblog/actions/CategoriesAction.java (added)
+++ incubator/roller/trunk/src/org/apache/roller/presentation/weblog/actions/CategoriesAction.java Mon May  1 15:23:02 2006
@@ -0,0 +1,281 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+*  contributor license agreements.  The ASF licenses this file to You
+* under the Apache License, Version 2.0 (the "License"); you may not
+* use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+/*
+ * Created on Oct 21, 2003
+ */
+package org.apache.roller.presentation.weblog.actions;
+
+import java.text.MessageFormat;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.LinkedList;
+import java.util.Set;
+import java.util.TreeSet;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.commons.lang.StringUtils;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.action.ActionMessage;
+import org.apache.struts.action.ActionMessages;
+import org.apache.struts.actions.DispatchAction;
+import org.apache.roller.RollerException;
+import org.apache.roller.model.RollerFactory;
+import org.apache.roller.model.WeblogManager;
+import org.apache.roller.pojos.WeblogCategoryData;
+import org.apache.roller.pojos.WebsiteData;
+import org.apache.roller.presentation.BasePageModel;
+import org.apache.roller.presentation.RollerRequest;
+import org.apache.roller.presentation.RollerSession;
+import org.apache.roller.presentation.weblog.formbeans.CategoriesForm;
+
+/**
+ * Actions that are initiated from the CategoriesForm.
+ * 
+ * @struts.action name="categoriesForm" path="/editor/categories" parameter="method"
+ * @struts.action-forward name="CategoriesForm" path=".CategoriesForm"
+ * 
+ * @author Dave Johnson
+ */
+public class CategoriesAction extends DispatchAction
+{
+    private static Log mLogger = 
+        LogFactory.getFactory().getInstance(CategoriesAction.class);
+
+    /**
+     * Present the CategoriesForm loaded with category specified by request.
+     * @param mapping Action mapping.
+     * @param actionForm Form bean.
+     * @param request Request.
+     * @param response Response.
+     * @return Forward to CategoriesForm or access-denied.
+     * @throws RollerException
+     */
+    public ActionForward selectCategory(
+        ActionMapping       mapping,
+        ActionForm          actionForm,
+        HttpServletRequest  request,
+        HttpServletResponse response)
+        throws RollerException
+    {
+        CategoriesPageModel pageModel = new CategoriesPageModel(
+                request, response, mapping, (CategoriesForm)actionForm);
+        RollerSession rses = RollerSession.getRollerSession(request);
+        if (rses.isUserAuthorizedToAuthor(pageModel.getCategory().getWebsite()))
+        {
+            request.setAttribute("model", pageModel);
+            return mapping.findForward("CategoriesForm");
+        }
+        else
+        {
+            return mapping.findForward("access-denied");
+        }
+    }
+
+     /**
+     * Move Categories and bookmarks indicated by CategoriesForm bean.
+     * @param mapping Action mapping.
+     * @param actionForm  Form bean.
+     * @param request Request.
+     * @param response Response.
+     * @return Forward to CategoriesForm or access-denied.
+     * @throws RollerException
+     */
+    public ActionForward moveSelected(
+        ActionMapping       mapping,
+        ActionForm          actionForm,
+        HttpServletRequest  request,
+        HttpServletResponse response)
+        throws RollerException
+    {
+        ActionMessages messages = new ActionMessages();
+        ActionForward forward = mapping.findForward("CategoriesForm");
+
+        CategoriesPageModel pageModel = new CategoriesPageModel(
+                request, response, mapping, (CategoriesForm)actionForm);
+        
+        RollerSession rses = RollerSession.getRollerSession(request);
+        if (rses.isUserAuthorizedToAuthor(pageModel.getCategory().getWebsite()))
+        {
+            request.setAttribute("model", pageModel);
+            try 
+            {
+                WeblogManager wmgr = RollerFactory.getRoller().getWeblogManager();
+                CategoriesForm form = (CategoriesForm)actionForm; 
+    
+                mLogger.debug("Moving categories to category, id=" 
+                    + form.getMoveToCategoryId());
+                    
+                // Move subCategories to new category.
+                String Categories[] = form.getSelectedCategories();
+                WeblogCategoryData parent = 
+                    wmgr.getWeblogCategory(form.getMoveToCategoryId());
+                if (null != Categories)
+                {
+                    for (int i = 0; i < Categories.length; i++)
+                    {
+                        WeblogCategoryData cd = 
+                            wmgr.getWeblogCategory(Categories[i]); 
+                        
+                        // Don't move category into itself.                  
+                        if (    !cd.getId().equals(parent.getId()) 
+                             && !parent.descendentOf(cd))
+                        {
+                            cd.setParent(parent);
+                            wmgr.saveWeblogCategory(cd);
+                        }
+                        else 
+                        {
+                            messages.add(null, new ActionMessage(
+                                "categoriesForm.warn.notMoving",cd.getName()));
+                        }
+                    }
+                }    
+                // TODO: new manager method, moveCategory(cat, newPath)
+                RollerFactory.getRoller().flush();
+                saveMessages(request, messages);
+            }
+            catch (RollerException e)
+            {
+                ActionErrors errors = new ActionErrors();
+                errors.add(null, new ActionError("categoriesForm.error.move"));
+                saveErrors(request, errors);       
+            }
+        }
+        else
+        {
+            forward = mapping.findForward("access-denied");
+        }
+        return forward;
+    }
+
+    private static final class CategoryPathComparator implements Comparator
+    {
+        public int compare(Object o1, Object o2) {
+            WeblogCategoryData f1 = (WeblogCategoryData)o1; 
+            WeblogCategoryData f2 = (WeblogCategoryData)o2; 
+            return f1.getPath().compareTo(f2.getPath());
+        }
+    }
+    
+    
+    public class CategoriesPageModel extends BasePageModel
+    {
+        private CategoriesForm form = null;
+        private WeblogCategoryData cat = null;
+        private TreeSet allCategories = null;
+        private List catPath = null;
+        
+        public WeblogCategoryData getCategory() { return cat; }
+        public Set getAllCategories() { return allCategories; }
+        public List getCategoryPath() { return catPath; }
+        
+        public CategoriesPageModel(
+                HttpServletRequest request,
+                HttpServletResponse response,
+                ActionMapping mapping,
+                CategoriesForm form) throws RollerException
+        {
+            super("dummy",  request, response, mapping);
+            this.form = form;
+            
+            RollerRequest rreq = RollerRequest.getRollerRequest(request);
+            WeblogManager wmgr = RollerFactory.getRoller().getWeblogManager();
+
+            allCategories = new TreeSet(new CategoryPathComparator());
+
+            // Find catid wherever it may be
+            String catId = (String)
+                request.getAttribute(RollerRequest.WEBLOGCATEGORYID_KEY);
+            if (null == catId) 
+            {
+                catId = request.getParameter(RollerRequest.WEBLOGCATEGORYID_KEY);
+            }  
+            if (null == catId)
+            {
+                catId = form.getId();     
+            }
+
+            cat = null;
+            if (null == catId || catId.equals("null"))
+            {
+                cat = wmgr.getRootWeblogCategory(website);
+            }
+            else 
+            {
+                cat = wmgr.getWeblogCategory(catId);  
+                website = cat.getWebsite();
+            }
+            form.setId(cat.getId());
+
+            //request.setAttribute("categories", cat.getWeblogCategories());
+
+            if (null != cat.getParent())
+            {
+                catPath = new LinkedList();
+                catPath.add(0, cat);
+                WeblogCategoryData parent = cat.getParent();
+                while (parent != null) 
+                {
+                    catPath.add(0, parent);
+                    parent = parent.getParent();   
+                }
+                //request.setAttribute("categoryPath", catPath);
+
+                request.setAttribute(
+                    RollerRequest.PARENTID_KEY, cat.getParent().getId());
+            }
+
+            // Build collection of all Categories, except for current one, 
+            // sorted by path.
+            Iterator iter = wmgr.getWeblogCategories(website).iterator();
+            while (iter.hasNext())
+            {
+                WeblogCategoryData cd = (WeblogCategoryData) iter.next();
+                if (!cd.getId().equals(catId))
+                {
+                    allCategories.add(cd);
+                }
+            }
+            // For Struts tags
+            request.setAttribute("allCategories", allCategories);
+            request.setAttribute("category", cat);
+        }        
+        public String getTitle()
+        {
+            if (catPath == null || catPath.isEmpty()) 
+            {
+                return bundle.getString("categoriesForm.rootTitle");
+            }
+            else 
+            {
+                return MessageFormat.format(
+                        bundle.getString("categoriesForm.parent"),
+                        new Object[] {cat.getName()});
+            }
+        }
+    }
+}