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 2005/10/21 23:46:28 UTC

svn commit: r327589 [35/72] - in /incubator/roller/branches/roller_1.x: ./ contrib/ contrib/lib/ contrib/plugins/ contrib/plugins/src/ contrib/plugins/src/org/ contrib/plugins/src/org/roller/ contrib/plugins/src/org/roller/presentation/ contrib/plugins...

Added: incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/CommentServlet.java
URL: http://svn.apache.org/viewcvs/incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/CommentServlet.java?rev=327589&view=auto
==============================================================================
--- incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/CommentServlet.java (added)
+++ incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/CommentServlet.java Fri Oct 21 14:27:36 2005
@@ -0,0 +1,573 @@
+package org.roller.presentation.velocity;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ResourceBundle;
+import java.util.Set;
+import java.util.TreeSet;
+
+import javax.mail.MessagingException;
+import javax.mail.Session;
+import javax.naming.InitialContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+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.struts.util.RequestUtils;
+import org.apache.velocity.Template;
+import org.apache.velocity.context.Context;
+import org.roller.RollerException;
+import org.roller.config.RollerConfig;
+import org.roller.config.RollerRuntimeConfig;
+import org.roller.model.IndexManager;
+import org.roller.model.Roller;
+import org.roller.model.UserManager;
+import org.roller.model.WeblogManager;
+import org.roller.pojos.CommentData;
+import org.roller.pojos.WeblogTemplate;
+import org.roller.pojos.UserData;
+import org.roller.pojos.WeblogEntryData;
+import org.roller.pojos.WebsiteData;
+import org.roller.presentation.LanguageUtil;
+import org.roller.presentation.RollerContext;
+import org.roller.presentation.RollerRequest;
+import org.roller.presentation.RollerSession;
+import org.roller.presentation.pagecache.PageCacheFilter;
+import org.roller.presentation.weblog.formbeans.CommentFormEx;
+import org.roller.util.CommentSpamChecker;
+import org.roller.util.MailUtil;
+import org.roller.util.StringUtils;
+
+/**
+ * Extend PageServlet to do special handling needed to support viewing and 
+ * posting of comments. Handles in-page comments and popup-style comments.
+ * <p />
+ * This servlet overrides the VelocityServlet's handleRequest method so that 
+ * the correct comments page template can be loaded for popup comments.
+ * If this servlet is called with the request paramteter 'popup' 
+ * defined, then it will load the user's _popupcomments page and if no such
+ * page is found it will use /popupcomments.vm which looks just like the old
+ * pre-0.9.9 popup comments page. 
+ * <p />
+ * This servlet also overrides doPost() to handle review and posting of new
+ * comments. If the request paramter 'method' is set to 'preview' then the 
+ * posted comment will be previewed, otherwise if it will be posted.
+ * <p />
+ * Incoming comments are tested against the MT Blacklist. If they are found
+ * to be spam, then they are marked as spam and hidden from view.
+ * <p />
+ * If email notification is turned on, each new comment will result in an 
+ * email sent to the blog owner and all who have commented on the same post.
+ *
+ * @web.servlet name="CommentServlet" 
+ * @web.servlet-mapping url-pattern="/comments/*"
+ * @web.servlet-init-param name="org.apache.velocity.properties" 
+ * 		                  value="/WEB-INF/velocity.properties"
+ *
+ * @author Dave Johnson 
+ */
+public class CommentServlet extends PageServlet 
+{
+    private static final String COMMENT_SPAM_MSG = 
+              "Your comment has been recognized as "
+            + "<a href='http://www.jayallen.org/projects/mt-blacklist/'>"
+            + "Comment Spam</a> and rejected.";
+    private transient ResourceBundle bundle = 
+        ResourceBundle.getBundle("ApplicationResources");
+    private static Log mLogger = 
+        LogFactory.getFactory().getInstance(CommentServlet.class);
+    
+    //-----------------------------------------------------------------------
+    /**
+     * Override VelocityServlet so we can pick the right page and stick
+     * the right stuff into the VelocityContext before page execution.
+     */
+    public Template handleRequest( HttpServletRequest request,
+                                   HttpServletResponse response,
+                                   Context ctx ) throws Exception
+    {
+        Template template = null;
+        if (request.getParameter("popup") == null)
+        {
+            // Request does not specify popup, so normal return
+            template = super.handleRequest(request, response, ctx); 
+        }
+        else
+        {
+            PageContext pageContext =
+                JspFactory.getDefaultFactory().getPageContext(
+                    this, request, response,"", true, 8192, true);
+            RollerRequest rreq= RollerRequest.getRollerRequest(pageContext);
+            UserManager userMgr = rreq.getRoller().getUserManager();
+            WebsiteData website = rreq.getWebsite();
+                
+            // Request specifies popup
+            org.roller.pojos.Template page = null;
+            Exception pageException = null;
+            try 
+            {
+                // Does user have a popupcomments page?
+                page = website.getPageByName("_popupcomments");
+            }
+            catch(Exception e )
+            {
+               pageException = e;
+               response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+            }
+            if (pageException != null)
+            {
+                mLogger.error("EXCEPTION: in RollerServlet", pageException);
+                request.setAttribute("DisplayException", pageException);
+            }
+            // 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);
+            template = prepareForPageExecution(ctx, rreq, response, page);
+        }
+        return template;
+    }
+    
+    //-----------------------------------------------------------------------
+    /**
+     * Handle POST from comment form, then hand off to super for page rendering.
+     */
+    public void doPost(
+            HttpServletRequest request, HttpServletResponse response)
+            throws IOException, ServletException
+    {
+        if (request.getParameter("method") != null 
+            && request.getParameter("method").equals("preview"))
+        {
+            doPreviewPost(request, response);
+            return;
+        }
+
+        RollerRequest rreq = RollerRequest.getRollerRequest(request);
+        HttpSession session = request.getSession();
+        try
+        {
+            // Get weblog entry object, put in page context
+            WeblogEntryData wd = rreq.getWeblogEntry();
+            if (wd == null || wd.getId() == null)
+            {
+                throw new RollerException(
+                    "Unable to find WeblogEntry for "
+                    + request.getParameter(RollerRequest.WEBLOGENTRYID_KEY));
+            }
+            if (   !wd.getWebsite().getAllowComments().booleanValue()
+                || !wd.getCommentsStillAllowed())
+            {
+                throw new RollerException("ERROR comments not allowed");
+            }
+            
+            request.setAttribute("blogEntry", wd);
+
+            // get the User to which the blog belongs
+            UserData user = wd.getWebsite().getUser();
+            
+            // TODO: A hack to be replaced by Object.canEdit()
+            request.setAttribute(RollerRequest.OWNING_USER, user);
+
+            // Save comment
+            WeblogManager mgr = rreq.getRoller().getWeblogManager();
+            CommentFormEx cf = new CommentFormEx();
+            CommentData cd = new CommentData();
+            RequestUtils.populate(cf, request);
+            cf.copyTo(cd, request.getLocale());
+            cd.setWeblogEntry(wd);
+            cd.setRemoteHost(request.getRemoteHost());
+            cd.setPostTime(new java.sql.Timestamp(System.currentTimeMillis()));
+            
+            if (!testCommentSpam(cd, request)) 
+            {
+                if (RollerContext.getCommentAuthenticator().authenticate(cd, request))
+                {
+                    cd.save();
+                    rreq.getRoller().commit();
+                    reindexEntry(rreq.getRoller(), wd);
+
+                    // Refresh user's entries in page cache
+                    PageCacheFilter.removeFromCache(request, user);
+
+                    // Put array of comments in context
+                    List comments = mgr.getComments(wd.getId());
+                    request.setAttribute("blogComments", comments);
+
+                    // MR: Added functionality to e-mail comments
+                    sendEmailNotification(request, rreq, wd, cd, user,comments);
+                    
+                    super.doPost(request, response);
+                    return;
+                }
+                else
+                {
+                    request.getSession().setAttribute(
+                        RollerSession.ERROR_MESSAGE, 
+                        bundle.getString("error.commentAuthFailed"));
+                }
+            }
+            doPreviewPost(request, response);
+        }
+        catch (Exception e)
+        {
+            mLogger.error("ERROR posting comment", e);
+            // Noted: this never gets back to the user.  Not sure why it is being set.
+            session.setAttribute(RollerSession.ERROR_MESSAGE, e.getMessage());
+        }
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Load comment and blog entry objects and forward to either the popup
+     * or the in-page comment page for comment preview.
+     */
+    public void doPreviewPost(
+        HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException
+    {
+        RollerRequest rreq = RollerRequest.getRollerRequest(request);
+        try
+        {
+            WeblogEntryData wd = rreq.getWeblogEntry();
+            if (wd == null || wd.getId() == null)
+            {
+                throw new RollerException(
+                   "Unable to find WeblogEntry for "
+                   + request.getParameter(RollerRequest.WEBLOGENTRYID_KEY));
+            }
+            request.setAttribute("blogEntry", wd);
+
+            // TODO: A hack to be replaced by Object.canEdit()
+            request.setAttribute(RollerRequest.OWNING_USER, wd.getWebsite().getUser());
+
+            CommentFormEx cf = new CommentFormEx();
+            RequestUtils.populate(cf, request);
+            cf.setWeblogEntry(wd);
+            cf.setPostTime(new java.sql.Timestamp(System.currentTimeMillis()));
+            request.setAttribute("commentForm", cf);
+            request.setAttribute("previewComments","dummy");
+        }
+        catch (Exception e)
+        {
+            // TODO: error message for browser and log
+            mLogger.error(e);
+        }
+        super.doPost(request, response);
+    }
+
+    /**
+     * Re-index the WeblogEntry so that the new comment gets indexed.
+     * @param entry
+     */
+    private void reindexEntry(Roller roller, WeblogEntryData entry) throws RollerException
+    {
+        IndexManager manager = roller.getIndexManager();
+
+        // remove entry before (re)adding it, or in case it isn't Published
+        manager.removeEntryIndexOperation(entry);
+
+        // if published, index the entry
+        if (entry.getPublishEntry() == Boolean.TRUE)
+        {
+            manager.addEntryIndexOperation(entry);
+        }
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Test CommentData to see if it is spam, if it is set it's spam property
+     * to true and put a RollerSession.ERROR_MESSAGE message in the session.
+     * @param cd CommentData to be tested.
+     */
+    private boolean testCommentSpam(CommentData cd, HttpServletRequest req)
+    {
+        boolean ret = false;
+        CommentSpamChecker checker = new CommentSpamChecker();
+        checker.testComment(cd);
+        if (cd.getSpam().booleanValue())
+        {
+           HttpSession session = req.getSession();
+           session.setAttribute(
+              RollerSession.ERROR_MESSAGE, COMMENT_SPAM_MSG);
+          ret = true;
+        }        
+        return ret;
+    }
+
+    //-----------------------------------------------------------------------
+
+    // Email notification
+
+    // agangolli: Incorporated suggested changes from Ken Blackler, with server-wide configurable options
+    // TODO: Make the addressing options configurable on a per-website basis.
+
+    private static final String EMAIL_ADDR_REGEXP = "^.*@.*[.].{2,}$";
+
+    // Servlet init params that control how messages are addressed server-wide.  These default to false for old behavior
+    // Controls whether the owner and commenters get separate messages (owner's message contains a link to the entry edit page).
+    private static final String SEPARATE_OWNER_MSG_PARAM = CommentServlet.class.getName() + ".separateOwnerMessage";
+    // Controls whether the commenters addresses are placed in a Bcc header or a visible address field
+    private static final String HIDE_COMMENTER_ADDRESSES_PARAM = CommentServlet.class.getName() + ".hideCommenterAddresses";
+
+
+    /**
+     * Send email notification of comment.
+     */
+    private void sendEmailNotification(
+        HttpServletRequest request,
+        RollerRequest rreq, 
+        WeblogEntryData wd,
+        CommentData cd,
+        UserData user, 
+        List comments) throws MalformedURLException
+    {
+        RollerContext rc = RollerContext.getRollerContext(request);
+        ResourceBundle resources = ResourceBundle.getBundle(
+            "ApplicationResources",LanguageUtil.getViewLocale(request));
+        UserManager userMgr = null;
+        WebsiteData site = null;
+        try
+        {
+            userMgr = RollerContext.getRoller(request).getUserManager();
+            site = userMgr.getWebsite(user.getUserName());
+        }
+        catch (RollerException re)
+        {
+            re.printStackTrace();
+            mLogger.error(
+              "Couldn't get UserManager from RollerContext", re.getRootCause());
+        }
+
+        // Send e-mail to owner and subscribed users (if enabled)
+        boolean notify = RollerRuntimeConfig.getBooleanProperty("users.comments.emailnotify");
+        if (notify && site.getEmailComments().booleanValue())
+        {
+            mLogger.debug("Comment notification enabled ... preparing email");
+            
+            // Determine message and addressing options from init parameters
+            boolean separateMessages = 
+                    RollerConfig.getBooleanProperty("comment.notification.separateOwnerMessage");
+            boolean hideCommenterAddrs = 
+                    RollerConfig.getBooleanProperty("comment.notification.hideCommenterAddresses");
+
+            //------------------------------------------
+            // --- Determine the "from" address
+            // --- Use either the site configured from address or the user's address
+            
+            String from = 
+               (StringUtils.isEmpty(site.getEmailFromAddress())) 
+                  ? user.getEmailAddress() 
+                  : site.getEmailFromAddress();
+            
+            //------------------------------------------
+            // --- Build list of email addresses to send notification to
+            
+            // Get all the subscribers to this comment thread
+            Set subscribers = new TreeSet();
+            for (Iterator it = comments.iterator(); it.hasNext();)
+            {
+                CommentData comment = (CommentData) it.next();
+                if (!StringUtils.isEmpty(comment.getEmail()))
+                {
+	                // If user has commented twice, 
+	                // count the most recent notify setting
+	                if (comment.getNotify().booleanValue())
+	                {
+	                    // only add those with valid email
+	                    if (comment.getEmail().matches(EMAIL_ADDR_REGEXP))
+	                    {
+	                        subscribers.add(comment.getEmail());
+	                    }
+	                }
+	                else
+	                {
+	                    // remove user who doesn't want to be notified
+	                    subscribers.remove(comment.getEmail());
+	                }
+                }
+            }
+
+            // Form array of commenter addrs
+            String[] commenterAddrs = (String[])subscribers.toArray(new String[0]);
+
+            //------------------------------------------
+            // --- Form the messages to be sent -
+            // For simplicity we always build separate owner and commenter messages even if sending a single one
+            
+            // Determine with mime type to use for e-mail
+            StringBuffer msg = new StringBuffer();
+            StringBuffer ownermsg = new StringBuffer();
+            boolean escapeHtml = RollerRuntimeConfig.getBooleanProperty("users.comments.escapehtml");
+                        
+            if (!escapeHtml)
+            {
+                msg.append("<html><body style=\"background: white; ");
+                msg.append(" color: black; font-size: 12px\">");
+            }
+
+            if (!StringUtils.isEmpty(cd.getName()))
+            {
+                msg.append(cd.getName() + " " 
+                           + resources.getString("email.comment.wrote")+": ");
+            }
+            else
+            {
+                msg.append(resources.getString("email.comment.anonymous")+": ");
+            }
+
+            msg.append((escapeHtml) ? "\n\n" : "<br /><br />");
+            msg.append(cd.getContent());
+            msg.append((escapeHtml) ? "\n\n----\n"
+                    : "<br /><br /><hr /><span style=\"font-size: 11px\">");
+            msg.append(resources.getString("email.comment.respond") + ": ");
+            msg.append((escapeHtml) ? "\n" : "<br />");
+
+            String rootURL = rc.getAbsoluteContextUrl(request);
+            if (rootURL == null || rootURL.trim().length()==0)
+            {
+            	rootURL = RequestUtils.serverURL(request) + request.getContextPath();
+            }
+
+            // Build link back to comment
+            
+            StringBuffer commentURL = new StringBuffer(rootURL);
+            commentURL.append("/comments/");
+            commentURL.append(rreq.getUser().getUserName());
+            
+            org.roller.pojos.Template page = rreq.getPage();
+            if (page == null)
+            {
+                commentURL.append("?entry=");
+            }
+            else
+            {
+                commentURL.append("/").append(page.getLink()).append("/");
+            }
+
+            commentURL.append(wd.getAnchor());
+	
+            if (escapeHtml) 
+            {
+                msg.append(commentURL.toString());
+            } 
+            else 
+            {
+             msg.append("<a href=\""+commentURL+"\">"+commentURL+"</a></span>");
+            }
+            
+            ownermsg.append(msg);
+
+             // add link to weblog edit page so user can login to manage comments
+            ownermsg.append((escapeHtml) ? "\n\n----\n" :
+                    "<br /><br /><hr /><span style=\"font-size: 11px\">");
+            ownermsg.append("Link to comment management page:");            
+            ownermsg.append((escapeHtml) ? "\n" : "<br />");
+            
+            StringBuffer deleteURL = new StringBuffer(rootURL);
+			deleteURL.append("/editor/weblog.do?method=edit&entryid="+wd.getId());            
+            
+            if (escapeHtml) 
+            {
+                 ownermsg.append(deleteURL.toString());
+            } 
+            else 
+            {
+                 ownermsg.append(
+                  "<a href=\"" + deleteURL + "\">" + deleteURL + "</a></span>");
+                 msg.append("</Body></html>");
+                 ownermsg.append("</Body></html>");
+            }
+            
+            String subject = null;
+            if ((subscribers.size() > 1) || 
+                (StringUtils.equals(cd.getEmail(), user.getEmailAddress())))
+            {
+                subject= "RE: "+resources.getString("email.comment.title")+": ";
+            }
+            else
+            {
+                subject = resources.getString("email.comment.title") + ": ";
+            }
+            subject += wd.getTitle();
+
+            //------------------------------------------
+            // --- Send message to email recipients
+            try
+            {
+                javax.naming.Context ctx = (javax.naming.Context)
+                    new InitialContext().lookup("java:comp/env");
+                Session session = (Session)ctx.lookup("mail/Session");
+                boolean isHtml = !escapeHtml;
+                if (separateMessages)
+                {
+                    // Send separate messages to owner and commenters
+                    sendMessage(session, from,
+                        new String[]{user.getEmailAddress()}, null, null, subject, ownermsg.toString(), isHtml);
+                    if (commenterAddrs.length > 0)
+                    {
+                        // If hiding commenter addrs, they go in Bcc: otherwise in the To: of the second message
+                        String[] to = hideCommenterAddrs ? null : commenterAddrs;
+                        String[] bcc = hideCommenterAddrs ? commenterAddrs : null;
+                        sendMessage(session, from, to, null, bcc, subject, msg.toString(), isHtml);
+
+                    }
+                }
+                else
+                {
+                    // Single message.  User in To: header, commenters in either cc or bcc depending on hiding option
+                    String[] cc = hideCommenterAddrs ? null : commenterAddrs;
+                    String[] bcc = hideCommenterAddrs ? commenterAddrs : null;
+                    sendMessage(session, from, new String[]{user.getEmailAddress()}, cc, bcc, subject,
+                        ownermsg.toString(), isHtml);
+                }
+            }
+            catch (javax.naming.NamingException ne)
+            {
+                mLogger.error("Unable to lookup mail session.  Check configuration.  NamingException: " + ne.getMessage());
+            }
+            catch (Exception e)
+            {
+                mLogger.warn("Exception sending comment mail: " + e.getMessage());
+                // This will log the stack trace if debug is enabled
+                if (mLogger.isDebugEnabled())
+                {
+                    mLogger.debug(e);
+                }
+            }
+
+            mLogger.debug("Done sending email message");
+            
+        } // if email enabled
+    }
+
+    // This is somewhat ridiculous, but avoids duplicating a bunch of logic in the already messy sendEmailNotification
+    private void sendMessage(Session session, String from, String[] to, String[] cc, String[] bcc, String subject,
+                             String msg, boolean isHtml) throws MessagingException
+    {
+        if (isHtml)
+            MailUtil.sendHTMLMessage(session, from, to, cc, bcc, subject, msg);
+        else
+            MailUtil.sendTextMessage(session, from, to, cc, bcc, subject, msg);
+    }
+
+    /* old method not used anymore -- Allen G
+    private boolean getBooleanContextParam(String paramName, boolean defaultValue) {
+        String paramValue = getServletContext().getInitParameter(paramName);
+        if (paramValue == null) return defaultValue;
+        return Boolean.valueOf(paramValue).booleanValue();
+    }
+    */
+}
+

Added: incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/ContextLoader.java
URL: http://svn.apache.org/viewcvs/incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/ContextLoader.java?rev=327589&view=auto
==============================================================================
--- incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/ContextLoader.java (added)
+++ incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/ContextLoader.java Fri Oct 21 14:27:36 2005
@@ -0,0 +1,603 @@
+package org.roller.presentation.velocity;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TimeZone;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.struts.util.RequestUtils;
+import org.apache.velocity.context.Context;
+import org.apache.velocity.tools.view.context.ChainedContext;
+import org.apache.velocity.tools.view.context.ToolboxContext;
+import org.apache.velocity.tools.view.servlet.ServletToolboxManager;
+import org.roller.RollerException;
+import org.roller.config.RollerConfig;
+import org.roller.config.RollerRuntimeConfig;
+import org.roller.model.Roller;
+import org.roller.model.RollerFactory;
+import org.roller.pojos.Template;
+import org.roller.pojos.CommentData;
+import org.roller.pojos.RollerPropertyData;
+import org.roller.pojos.UserData;
+import org.roller.pojos.WeblogEntryData;
+import org.roller.pojos.WebsiteData;
+import org.roller.pojos.wrapper.CommentDataWrapper;
+import org.roller.pojos.wrapper.TemplateWrapper;
+import org.roller.pojos.wrapper.WeblogEntryDataWrapper;
+import org.roller.pojos.wrapper.WebsiteDataWrapper;
+import org.roller.presentation.LanguageUtil;
+import org.roller.presentation.RollerContext;
+import org.roller.presentation.RollerRequest;
+import org.roller.presentation.RollerSession;
+import org.roller.presentation.newsfeeds.NewsfeedCache;
+import org.roller.presentation.weblog.formbeans.CommentFormEx;
+import org.roller.util.RegexUtil;
+import org.roller.util.StringUtils;
+import org.roller.util.Utilities;
+
+/**
+ * Load Velocity Context with Roller objects, values, and custom plugins.
+ * 
+ * @author llavandowska
+ * @author David M Johnson
+ */
+public class ContextLoader
+{   
+    private RollerRequest mRollerReq = null;
+    
+    // List of PagePlugins for "transforming" WeblogEntries
+    static List mPagePlugins = new ArrayList();
+    
+    private static Log mLogger = 
+       LogFactory.getFactory().getInstance(ContextLoader.class);
+
+    //------------------------------------------------------------------------
+    
+    /**
+     * Setup the a Velocity context by loading it with objects, values, and
+     * RollerPagePlugins needed for Roller page execution.
+     */
+    public static void setupContext( Context ctx, 
+        RollerRequest rreq, HttpServletResponse response ) 
+        throws RollerException
+    {
+        mLogger.debug("setupContext( ctx = "+ctx+")");
+        
+        HttpServletRequest request = rreq.getRequest();
+        RollerContext rollerCtx = RollerContext.getRollerContext( request );
+        
+        try 
+        {    
+            // Add page model object to context 
+            String pageModelClassName = 
+                RollerConfig.getProperty("velocity.pagemodel.classname");
+            Class pageModelClass = Class.forName(pageModelClassName);
+            PageModel pageModel = (PageModel)pageModelClass.newInstance();
+            pageModel.init(rreq);
+            ctx.put("pageModel", pageModel );
+            ctx.put("pages", pageModel.getPages());
+        }
+        catch (Exception e)
+        {
+            throw new RollerException("ERROR creating Page Model",e);
+        }
+        
+        // Add Velocity page helper to context
+        PageHelper pageHelper = new PageHelper(rreq, response, ctx);
+        pageHelper.initializePlugins(mPagePlugins);
+        ctx.put("pageHelper", pageHelper );
+
+        // Add legacy macros too, so that old-school pages still work
+        Macros macros= new Macros(rreq.getPageContext(), pageHelper);
+        ctx.put("macros", macros);
+
+        // Load standard Roller objects and values into the context 
+        String userName = loadWebsiteValues(ctx, rreq, rollerCtx );
+        loadWeblogValues( ctx, rreq, rollerCtx, userName );            
+        loadPathValues( ctx, rreq, rollerCtx, userName );                                         
+        loadRssValues( ctx, rreq, userName );                        
+        loadUtilityObjects( ctx, rreq, rollerCtx, userName ); 
+        loadRequestParamKeys(ctx);
+        loadStatusMessage( ctx, rreq );
+        
+        // If single entry is specified, load comments too
+        if ( rreq.getWeblogEntry() != null )
+        {
+            loadCommentValues( ctx, rreq, rollerCtx );
+        }
+        
+        // add Velocity Toolbox tools to context
+        loadToolboxContext(request, response, ctx);        
+    }
+    
+    //------------------------------------------------------------------------
+    
+    /**
+     * If there is an ERROR or STATUS message in the session,
+     * place it into the Context for rendering later.
+     * 
+     * @param rreq
+     */
+    private static void loadStatusMessage(Context ctx, RollerRequest rreq)
+    {
+        mLogger.debug("Loading status message");
+        
+        HttpSession session = rreq.getRequest().getSession(false);
+        String msg = null;
+        if (session != null)
+            msg = (String)session.getAttribute(RollerSession.ERROR_MESSAGE);
+        if (msg != null)
+        {
+            ctx.put("errorMessage", msg);
+            session.removeAttribute(RollerSession.ERROR_MESSAGE);
+        }
+
+        if (session != null)
+            msg = (String)session.getAttribute(RollerSession.STATUS_MESSAGE);
+        if (msg != null)
+        {
+            ctx.put("statusMessage", msg);
+            session.removeAttribute(RollerSession.STATUS_MESSAGE);
+        }
+    }
+    
+    //------------------------------------------------------------------------
+
+    /**
+     * @param ctx
+     * @param rreq
+     * @param rollerCtx
+     * @param userName
+     */
+    private static void loadWeblogValues(
+       Context ctx, RollerRequest rreq, RollerContext rollerCtx, String userName)
+       throws RollerException
+    {
+        mLogger.debug("Loading weblog values");
+        
+        // if there is an "_entry" page, only load it once
+        WebsiteData website = RollerFactory.getRoller().getUserManager().getWebsite(userName);
+        //PageModel pageModel = (PageModel)ctx.get("pageModel");
+        if (website != null) 
+        {
+            /* alternative display pages - customization */
+            Template entryPage = website.getPageByName("_entry");
+            if (entryPage != null)
+            {
+                ctx.put("entryPage", TemplateWrapper.wrap(entryPage));
+            }
+            Template descPage = website.getPageByName("_desc");
+            if (descPage != null)
+            {
+                ctx.put("descPage", TemplateWrapper.wrap(descPage));
+            }
+        }
+    }
+
+    private static String figureResourcePath( RollerRequest rreq )
+    {   
+        String uploadurl = null;
+        try {
+            uploadurl = RollerFactory.getRoller().getFileManager().getUploadUrl();
+        } catch(Exception e) {}
+        
+        return uploadurl;
+    }
+
+    //------------------------------------------------------------------------
+    
+    public boolean isUserAuthorizedToEdit()
+    {
+        try
+        {
+            return mRollerReq.isUserAuthorizedToEdit();
+        }
+        catch (Exception e)
+        {
+            mLogger.warn("PageHelper.isUserAuthorizedToEdit)", e);
+        }
+        return false;
+    }
+    
+    //------------------------------------------------------------------------
+    
+    protected static void loadCommentValues(
+       Context ctx, RollerRequest rreq, RollerContext rollerCtx ) 
+       throws RollerException
+    {
+        mLogger.debug("Loading comment values");
+        
+        HttpServletRequest request = rreq.getRequest();
+        
+        String escapeHtml = RollerRuntimeConfig.getProperty("users.comments.escapehtml");
+        String autoFormat = RollerRuntimeConfig.getProperty("users.comments.autoformat");
+        
+        // Add comments related values to context
+        ctx.put("isCommentPage", Boolean.TRUE);
+        ctx.put("escapeHtml", new Boolean(escapeHtml) );
+        ctx.put("autoformat", new Boolean(autoFormat) );
+        
+        // Make sure comment form object is available in context
+        CommentFormEx commentForm = 
+                (CommentFormEx) request.getAttribute("commentForm");
+        if ( commentForm == null )
+        {
+            commentForm = new CommentFormEx();
+        
+            // Set fields to spaces to please Velocity
+            commentForm.setName("");
+            commentForm.setEmail("");
+            commentForm.setUrl("");
+            commentForm.setContent("");
+        }        
+        ctx.put("commentForm",commentForm); 
+            
+        // Either put a preview comment in to context          
+        if ( request.getAttribute("previewComments")!=null )
+        {
+            ArrayList list = new ArrayList();
+            CommentData cd = new CommentData();
+            commentForm.copyTo(cd, request.getLocale());
+            list.add(CommentDataWrapper.wrap(cd));
+            ctx.put("previewComments",list);            
+        }
+        WeblogEntryData entry = rreq.getWeblogEntry();
+        ctx.put("entry", WeblogEntryDataWrapper.wrap(entry));            
+    }   
+
+    //------------------------------------------------------------------------
+    
+    protected static void loadPathValues(
+        Context ctx, RollerRequest rreq, RollerContext rollerCtx, String userName) 
+        throws RollerException
+    {
+        mLogger.debug("Loading path values");
+        
+        HttpServletRequest request = rreq.getRequest();
+        String url = null;
+        if ( userName != null && !userName.equals("zzz_none_zzz"))
+        {
+            url = Utilities.escapeHTML( 
+                rollerCtx.getAbsoluteContextUrl(request)+"/page/"+userName);
+        }
+        else
+        {
+            url= Utilities.escapeHTML(rollerCtx.getAbsoluteContextUrl(request));
+        }
+        ctx.put("websiteURL", url);
+        ctx.put("baseURL",    rollerCtx.getContextUrl( request ) );
+        ctx.put("absBaseURL", rollerCtx.getAbsoluteContextUrl( request ) );
+        ctx.put("ctxPath",    request.getContextPath() );
+        ctx.put("uploadPath", ContextLoader.figureResourcePath( rreq ) );
+        
+        try
+        {
+            URL absUrl = RequestUtils.absoluteURL(request, "/");
+            ctx.put("host", absUrl.getHost());
+        }
+        catch (MalformedURLException e)
+        {
+            throw new RollerException(e);
+        }
+    }
+
+    //------------------------------------------------------------------------
+    
+    protected static void loadRequestParamKeys(Context ctx)
+    {
+        mLogger.debug("Loading request param keys");
+        
+        // Since Velocity *requires* accessor methods, these values from
+        // RollerRequest are not available to it, put them into the context
+        ctx.put("USERNAME_KEY",           RollerRequest.USERNAME_KEY);
+        ctx.put("WEBSITEID_KEY",          RollerRequest.WEBSITEID_KEY);
+        ctx.put("FOLDERID_KEY",           RollerRequest.FOLDERID_KEY);
+        ctx.put("NEWSFEEDID_KEY",         RollerRequest.NEWSFEEDID_KEY);
+        ctx.put("PAGEID_KEY",             RollerRequest.PAGEID_KEY);
+        ctx.put("PAGELINK_KEY",           RollerRequest.PAGELINK_KEY);
+        ctx.put("ANCHOR_KEY",             RollerRequest.ANCHOR_KEY);
+        ctx.put("EXCERPTS_KEY",           RollerRequest.EXCERPTS_KEY);
+        ctx.put("BOOKMARKID_KEY",         RollerRequest.BOOKMARKID_KEY);
+        ctx.put("REFERERID_KEY",          RollerRequest.REFERERID_KEY);
+        ctx.put("WEBLOGENTRYID_KEY",      RollerRequest.WEBLOGENTRYID_KEY);
+        ctx.put("WEBLOGCATEGORYNAME_KEY", RollerRequest.WEBLOGCATEGORYNAME_KEY);
+        ctx.put("WEBLOGCATEGORYID_KEY",   RollerRequest.WEBLOGENTRIES_KEY);
+        ctx.put("WEBLOGENTRIES_KEY",      RollerRequest.WEBLOGENTRIES_KEY);
+        ctx.put("WEBLOGDAY_KEY",          RollerRequest.WEBLOGDAY_KEY);
+        ctx.put("WEBLOGCOMMENTID_KEY",    RollerRequest.WEBLOGCOMMENTID_KEY);
+    }
+
+    //------------------------------------------------------------------------
+    
+    protected static void loadRssValues(
+       Context ctx, RollerRequest rreq, String userName) throws RollerException
+    {
+        mLogger.debug("Loading rss values");
+        
+        HttpServletRequest request = rreq.getRequest();
+        
+        int entryLength = -1;
+        String sExcerpts = request.getParameter("excerpts");
+        if ( sExcerpts!=null && sExcerpts.equalsIgnoreCase("true"))
+        {
+            entryLength = 150;
+        }
+        ctx.put("entryLength",  new Integer(entryLength));
+        
+        int entryCount = 15;
+        String sCount = request.getParameter("count");
+        if ( sCount!=null && sExcerpts.trim().equals(""))
+        {
+            try
+            {
+                entryCount = Integer.parseInt(sCount);
+            }
+            catch (NumberFormatException e)
+            {
+                mLogger.warn("Improperly formatted count parameter");
+            }
+            if ( entryCount > 50 ) entryCount = 50;
+            if ( entryCount < 0 ) entryCount = 15;
+        }
+        ctx.put("entryCount",  new Integer(entryCount));
+            
+        String catname = null;
+        String catPath = null;
+        if ( rreq.getWeblogCategory() != null )
+        {
+            catname = rreq.getWeblogCategory().getName();
+            catPath = rreq.getWeblogCategory().getPath();
+        } 
+        ctx.put("catname", (catname!=null) ? catname : "");
+        ctx.put("catPath", (catPath != null) ? catPath : "");
+        ctx.put("updateTime", request.getAttribute("updateTime"));
+        ctx.put("now", new Date());
+    }
+
+    //------------------------------------------------------------------------
+    
+    protected static void loadUtilityObjects(
+        Context ctx, RollerRequest rreq, RollerContext rollerCtx, String username)
+        throws RollerException
+    {
+        mLogger.debug("Loading utility objects");
+        
+        // date formatter for macro's
+        // set this up with the Locale to make sure we can reuse it with other patterns
+        // in the macro's
+        Locale viewLocale = (Locale) ctx.get("viewLocale");
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd", viewLocale);
+        WebsiteData website = RollerFactory.getRoller().getUserManager().getWebsite(username);
+        if (website != null)
+        {
+            sdf.setTimeZone(website.getTimeZoneInstance());
+        }
+        // add formatter to context
+        ctx.put("dateFormatter", sdf );
+
+        // Note: in the macro's, the formats are taken from the ResourceBundles.
+        // Only the plainFormat is specified here, because it is used to render
+        // the Entry Day link.
+        ctx.put("plainFormat", "yyyyMMdd");
+
+        ctx.put("page",            TemplateWrapper.wrap(rreq.getPage()));
+        ctx.put("utilities",       new Utilities() );
+        ctx.put("stringUtils",     new StringUtils() );        
+        ctx.put("rollerVersion",   rollerCtx.getRollerVersion() );
+        ctx.put("rollerBuildTime", rollerCtx.getRollerBuildTime() );
+        ctx.put("rollerBuildUser", rollerCtx.getRollerBuildUser() );
+        ctx.put("newsfeedCache",   NewsfeedCache.getInstance() );
+        
+        ctx.put("requestParameters", rreq.getRequest().getParameterMap());
+    }
+    
+    //------------------------------------------------------------------------
+    
+    protected static String loadWebsiteValues(
+        Context ctx, RollerRequest rreq, RollerContext rollerCtx )
+        throws RollerException
+    {
+        mLogger.debug("Loading website values");
+        
+        String userName = null;
+        UserData user = null;
+        WebsiteData website = null;
+        
+        Roller mRoller = RollerFactory.getRoller();
+        Map props = mRoller.getPropertiesManager().getProperties();
+        
+        if ( rreq.getRequest().getAttribute(RollerRequest.OWNING_USER) != null)
+        {
+            user = (UserData)
+                rreq.getRequest().getAttribute(RollerRequest.OWNING_USER);
+        }
+        else if ( rreq.getUser() != null )
+        {
+            user = rreq.getUser();
+        }
+        
+        if ( user != null )
+        {
+            userName = user.getUserName();
+            website = RollerFactory.getRoller().getUserManager().getWebsite(userName);
+            ctx.put("userName",      user.getUserName() );
+            ctx.put("fullName",      user.getFullName() );
+            ctx.put("emailAddress",  user.getEmailAddress() );
+
+            ctx.put("encodedEmail",  RegexUtil.encode(user.getEmailAddress()));
+            ctx.put("obfuscatedEmail",  RegexUtil.obfuscateEmail(user.getEmailAddress()));
+            
+            // setup Locale for future rendering
+            ctx.put("locale", website.getLocaleInstance());
+           
+            // setup Timezone for future rendering
+            ctx.put("timezone", website.getTimeZoneInstance());
+        }
+        else
+        {
+            website = new WebsiteData();
+            website.setName(((RollerPropertyData)props.get("site.name")).getValue());
+            website.setAllowComments(Boolean.FALSE);
+            website.setDescription(((RollerPropertyData)props.get("site.description")).getValue());
+            userName = "zzz_none_zzz";
+            ctx.put("userName",userName );
+            ctx.put("fullName","zzz_none_zzz");
+            ctx.put("emailAddress",
+                ((RollerPropertyData)props.get("site.adminemail")).getValue());
+            ctx.put("locale", Locale.getDefault());
+            ctx.put("timezone", TimeZone.getDefault());
+        }
+        ctx.put("website", WebsiteDataWrapper.wrap(website) );
+
+        String siteName = ((RollerPropertyData)props.get("site.name")).getValue();
+        if ("Roller-based Site".equals(siteName)) siteName = "Main";
+        ctx.put("siteName", siteName);        
+
+        // add language of the session (using locale of viewer set by Struts)
+        ctx.put(
+            "viewLocale",
+            LanguageUtil.getViewLocale(rreq.getRequest()));
+        mLogger.debug("context viewLocale = "+ctx.get( "viewLocale"));
+
+        return userName;
+    }
+    
+    //------------------------------------------------------------------------
+
+    /**
+     * Initialize PagePlugins declared in web.xml.  By using the full class
+     * name we also allow for the implementation of "external" Plugins
+     * (maybe even packaged seperately).  These classes are then later 
+     * instantiated by PageHelper.
+     * 
+     * @param mContext
+     */
+    public static void initializePagePlugins(ServletContext mContext)
+    {
+        mLogger.debug("Initializing page plugins");
+        
+        String pluginStr = RollerConfig.getProperty("plugins.page");
+        if (mLogger.isDebugEnabled()) mLogger.debug(pluginStr);
+        if (pluginStr != null)
+        {
+            String[] plugins = StringUtils.stripAll(
+                                   StringUtils.split(pluginStr, ",") );
+            for (int i=0; i<plugins.length; i++)
+            {
+                if (mLogger.isDebugEnabled()) mLogger.debug("try " + plugins[i]);
+                try
+                {
+                    Class pluginClass = Class.forName(plugins[i]);
+                    if (isPagePlugin(pluginClass))
+                    {
+                        mPagePlugins.add(pluginClass.newInstance());
+                    }
+                    else
+                    {
+                        mLogger.warn(pluginClass + " is not a PagePlugin");
+                    }
+                } 
+                catch (ClassNotFoundException e)
+                {
+                    mLogger.error("ClassNotFoundException for " + plugins[i]);
+                }
+                catch (InstantiationException e)
+                {
+                    mLogger.error("InstantiationException for " + plugins[i]);
+                }
+                catch (IllegalAccessException e)
+                {
+                    mLogger.error("IllegalAccessException for " + plugins[i]);
+                }
+            }
+        }
+    }
+    
+    /**
+     * @param pluginClass
+     * @return
+     */
+    private static boolean isPagePlugin(Class pluginClass)
+    {
+        Class[] interfaces = pluginClass.getInterfaces();
+        for (int i=0; i<interfaces.length; i++)
+        {
+            if (interfaces[i].equals(PagePlugin.class)) return true;
+        }
+        return false;
+    }
+
+    public static boolean hasPlugins()
+    {
+        mLogger.debug("mPluginClasses.size(): " + mPagePlugins.size());
+        return (mPagePlugins != null && mPagePlugins.size() > 0);
+    }
+    
+    public static List getPagePlugins()
+    {
+        return mPagePlugins;
+    }
+
+
+    private static final String TOOLBOX_KEY = 
+        "org.roller.presentation.velocity.toolbox";
+
+    private static final String TOOLBOX_MANAGER_KEY = 
+        "org.roller.presentation.velocity.toolboxManager";
+
+    private static ToolboxContext loadToolboxContext(
+                    HttpServletRequest request, HttpServletResponse response, Context ctx) 
+    {
+        mLogger.debug("Loading toolbox context");
+        
+        ServletContext servletContext = RollerContext.getServletContext();
+
+        // get the toolbox manager
+        ServletToolboxManager toolboxManager = 
+            (ServletToolboxManager)servletContext.getAttribute(TOOLBOX_MANAGER_KEY);
+        if (toolboxManager==null) 
+        {
+            String file = RollerConfig.getProperty("velocity.toolbox.file");
+            mLogger.debug("Creating new toolboxContext using config-file: "+file);
+            toolboxManager = ServletToolboxManager.getInstance(servletContext, file);
+            servletContext.setAttribute(TOOLBOX_MANAGER_KEY, toolboxManager);
+        }
+        
+        // load a toolbox context
+        ChainedContext chainedContext = 
+            new ChainedContext(ctx, request, response, servletContext);
+        ToolboxContext toolboxContext = 
+            toolboxManager.getToolboxContext(chainedContext);
+
+        if (toolboxContext != null)
+        {
+            // add MessageTool to VelocityContext
+            ctx.put("text", toolboxContext.internalGet("text"));
+            
+            /*          
+            Object[] keys = toolboxContext.internalGetKeys();
+            for (int i=0;i<keys.length;i++) {
+                String key = (String)keys[i];
+                System.out.println("key = "+key);
+                Object tool = toolboxContext.get(key);
+                System.out.println("tool = "+tool);
+                ctx.put(key, tool);
+            }
+            */  
+        }
+        
+        return toolboxContext;  
+    }
+}

Added: incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/DefaultCommentAuthenticator.java
URL: http://svn.apache.org/viewcvs/incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/DefaultCommentAuthenticator.java?rev=327589&view=auto
==============================================================================
--- incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/DefaultCommentAuthenticator.java (added)
+++ incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/DefaultCommentAuthenticator.java Fri Oct 21 14:27:36 2005
@@ -0,0 +1,28 @@
+package org.roller.presentation.velocity;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.velocity.context.Context;
+import org.roller.pojos.CommentData;
+
+/**
+ * Default authenticator does nothing, always returns true.
+ * @author David M Johnson
+ */
+public class DefaultCommentAuthenticator implements CommentAuthenticator 
+{   
+    public String getHtml(
+                        Context context,
+                        HttpServletRequest request, 
+                        HttpServletResponse response)
+    {
+        return "<!-- custom authenticator would go here -->";
+    }
+    
+    public boolean authenticate(
+                        CommentData comment,
+                        HttpServletRequest request) 
+    {
+        return true;
+    }
+}

Added: incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/ExportRss.java
URL: http://svn.apache.org/viewcvs/incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/ExportRss.java?rev=327589&view=auto
==============================================================================
--- incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/ExportRss.java (added)
+++ incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/ExportRss.java Fri Oct 21 14:27:36 2005
@@ -0,0 +1,231 @@
+package org.roller.presentation.velocity;
+
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.exception.ParseErrorException;
+import org.apache.velocity.exception.ResourceNotFoundException;
+import org.roller.RollerException;
+import org.roller.model.RollerFactory;
+import org.roller.pojos.UserData;
+import org.roller.pojos.WebsiteData;
+import org.roller.presentation.RollerContext;
+import org.roller.util.RegexUtil;
+import org.roller.util.StringUtils;
+import org.roller.util.Utilities;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.text.SimpleDateFormat;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Locale;
+import java.util.Properties;
+import java.util.TimeZone;
+
+/**
+ * Does a lot of the same work as ContextLoader in preparing
+ * a VelocityContext for parsing.  However, it is ignorant of
+ * any HttpServletRequest related features, and so has 
+ * considerably trimmed down information.
+ * 
+ * Created on Mar 25, 2004
+ * @author lance.lavandowska
+ */
+public class ExportRss
+{
+    private VelocityEngine ve = null;
+    private VelocityContext ctx = null;
+    private UserData user = null;
+    private boolean exportAtom;
+    
+    private static final String RSS_TEMPLATE = "org/roller/presentation/velocity/export_rss.vm";
+    private static final String ATOM_TEMPLATE = "org/roller/presentation/velocity/export_atom.vm";
+    
+    public ExportRss(WebsiteData website) throws Exception
+    {
+        Properties props = new Properties();
+        props.load(RollerContext.getServletContext().
+                   getResourceAsStream("/WEB-INF/velocity.properties"));
+        ve = new VelocityEngine();
+        ve.info("*******************************************");
+        ve.info("Initializing VelocityEngine for ExportRss");
+        ve.init( props );
+        ve.info("Done initializing VelocityEngine for ExportRss");
+        ve.info("************************************************");
+        
+        ctx = new VelocityContext();
+        
+        user = website.getUser();
+        RollerContext rollerCtx = RollerContext.getRollerContext(
+                                      RollerContext.getServletContext());
+        loadPageHelper();
+        
+        loadDates(website);
+        
+        loadWebsiteInfo(rollerCtx, website);
+
+        loadTheRest(rollerCtx);
+    }
+    
+    public void setExportAtom(boolean atom)
+    {
+        exportAtom = atom;
+    }
+    
+    /**
+     * Export the given entries using export_rss.vm.
+     * 
+     * @param entries
+     * @throws ResourceNotFoundException
+     * @throws ParseErrorException
+     * @throws Exception
+     */
+    public void exportEntries(Collection entries, String fileName) throws ResourceNotFoundException, ParseErrorException, Exception 
+    {
+        ctx.put("entries", entries);
+        
+        String templateFile = RSS_TEMPLATE;
+        if (exportAtom) templateFile = ATOM_TEMPLATE;
+        Template template = ve.getTemplate( templateFile, "utf-8" );
+        StringWriter sw = new StringWriter();
+        template.merge(ctx, sw);
+        
+        writeResultsToFile((String)ctx.get("uploadPath"), sw, fileName);
+    }
+
+    /**
+     * @param sw
+     */
+    private void writeResultsToFile(String filePath, StringWriter sw, String fileName) 
+        throws RollerException, IOException
+    {
+        filePath += "/" + user.getUserName();
+        new java.io.File( filePath ).mkdirs(); // create dir path on drive
+        
+        filePath += "/" + fileName;
+        
+        File outputFile = new java.io.File( filePath );
+        FileOutputStream out = null;
+        try
+        {
+            //outputFile.createNewFile();
+            out = new FileOutputStream( outputFile );
+            out.write( sw.toString().getBytes() );
+            out.flush();
+        }
+        catch ( FileNotFoundException e )
+        {
+            throw new RollerException( "Unable to write to: " + outputFile.getAbsolutePath(), e );
+        }
+        finally
+        {
+            try
+            {
+                if ( out != null )
+                {
+                    out.close();
+                }
+            }
+            catch ( java.io.IOException ioe )
+            {
+                System.err.println( "ExportRss unable to close OutputStream" );
+            }
+        }
+    }
+
+    /**
+     * Load miscellaneous values into the Context.
+     * @param rollerCtx
+     */
+    private void loadTheRest(RollerContext rollerCtx)
+    {
+        ctx.put("utilities",       new Utilities() );
+        ctx.put("stringUtils",     new StringUtils() );        
+        ctx.put("entryLength",     new Integer(-1));
+    }
+
+    /**
+     * Load information pertaining to the Website and
+     * its associated User.
+     * @param rollerCtx
+     */
+    private void loadWebsiteInfo(RollerContext rollerCtx, WebsiteData website)
+    {
+        ctx.put("website",       website);
+        ctx.put("userName",      user.getUserName() );
+        ctx.put("fullName",      user.getFullName() );
+        ctx.put("emailAddress",  user.getEmailAddress() );
+
+        ctx.put("encodedEmail",  RegexUtil.encode(user.getEmailAddress()));
+        ctx.put("obfuscatedEmail",  RegexUtil.obfuscateEmail(user.getEmailAddress()));
+
+        
+        // custom figureResourcePath() due to no "request" object
+        StringBuffer sb = new StringBuffer();
+        String uploadDir = null;
+        try {
+            uploadDir = RollerFactory.getRoller().getFileManager().getUploadDir();
+        } catch(Exception e) {}
+
+        ctx.put("uploadPath", uploadDir);
+    }
+
+    /**
+     * Load time-related information.
+     * @param website
+     */
+    private void loadDates(WebsiteData website)
+    {
+        try
+        {
+            // Add current time and last updated times to context
+            Date updateTime = RollerFactory.getRoller().getWeblogManager()
+            .getWeblogLastPublishTime( user.getUserName(), null );                        
+            ctx.put("updateTime",   updateTime);
+        }
+        catch (RollerException e)
+        {                       
+            ctx.put("updateTime",   new Date());
+        }
+        ctx.put("now",              new Date());
+        
+        // setup Locale for future rendering
+        Locale locale = website.getLocaleInstance();
+        ctx.put("locale", locale);
+        
+        // setup Timezone for future rendering
+        ctx.put("timezone", website.getTimeZoneInstance());
+
+        // date formats need to be run through the Localized
+        // SimpleDateFormat and pulled back out as localized patterns.
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd", locale);  
+        sdf.setTimeZone( (TimeZone)ctx.get("timezone") );    
+        ctx.put("plainFormat",     sdf.toLocalizedPattern());
+        
+        sdf.applyPattern("EEEE MMMM dd, yyyy");
+        ctx.put("toStringFormat",  sdf.toLocalizedPattern());
+        
+        sdf.applyPattern("MMM dd yyyy, hh:mm:ss a z");
+        ctx.put("timestampFormat", sdf.toLocalizedPattern());
+        
+        ctx.put("dateFormatter", sdf );
+    }
+    /**
+     * Create a PageHelper.  Note that will have no values
+     * necessary in parsing a Web request (such as /page) -
+     * it is only useful for the restricted export_rss.vm
+     * and has no PagePlugins either.  We want the exported
+     * Entry.text to be the raw values.
+     */
+    private void loadPageHelper()
+    {
+        // Add Velocity page helper to context
+        PageHelper pageHelper = new PageHelper(null, null, ctx);
+        // set no PagePlugins - we *do not* want to render them.
+        ctx.put("pageHelper", pageHelper );
+    }
+}

Added: incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/FlavorServlet.java
URL: http://svn.apache.org/viewcvs/incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/FlavorServlet.java?rev=327589&view=auto
==============================================================================
--- incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/FlavorServlet.java (added)
+++ incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/FlavorServlet.java Fri Oct 21 14:27:36 2005
@@ -0,0 +1,133 @@
+package org.roller.presentation.velocity;
+
+import org.apache.commons.lang.StringUtils;
+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.roller.RollerException;
+import org.roller.presentation.RollerRequest;
+import java.io.IOException;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.jsp.JspFactory;
+import javax.servlet.jsp.PageContext;
+
+/////////////////////////////////////////////////////////////////////////////
+/**
+  * <p>Responsible for rendering RSS feeds and other "flavors" of output for a
+  * weblog.</p>
+  *
+  * <p>If Servlet is mapped to <code>/rss</code> and user has defined
+  * an RSS override page (i.e. a page named "_rss"), then that Velocity
+  * template will be used for rendering.</p>
+  *
+  * <p>If there is a request parameter named "flavor", then the Velocity
+  * template specified by that parameter will be used for rendering. For
+  * example if the flavor is "rss092" then the template at classpath
+  * "/flavors/rss092.vm" will be used for rendering.</p>
+  *
+  * <p>Otherwise, the template /flavors/rss.vm" will be used for rendering.</p>
+  *
+  * <p>Assumes that If-Modified-Since has already been handled.</p>
+  *
+  * @author David M Johnson
+  *
+  * @web.servlet name="RssServlet"
+  * @web.servlet-mapping url-pattern="/rss/*"
+  * @web.servlet-mapping url-pattern="/flavor/*"
+  */
+public class FlavorServlet extends VelocityServlet
+{
+    static final long serialVersionUID = -2720532269434186051L;
+    
+    private static Log mLogger = LogFactory.getFactory()
+                                           .getInstance(RollerRequest.class);
+    public Template handleRequest(HttpServletRequest request,
+                                  HttpServletResponse response, Context ctx)
+    {
+        RollerRequest rreq = null;
+        try
+        {
+            rreq = RollerRequest.getRollerRequest(request,getServletContext());
+            
+            // This is an ugly hack to fix the following bug: 
+            // ROL-547: "Site wide RSS feed is your own if you are logged in"
+            String[] pathInfo = StringUtils.split(rreq.getPathInfo(),"/"); 
+            if (pathInfo.length < 1) 
+            {
+                // If website not specified in URL, set it to null
+                rreq.setWebsite(null);
+            }
+        }
+        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
+        {
+            // Needed to init request attributes, etc.
+            PageContext pageContext =
+                JspFactory.getDefaultFactory().getPageContext(
+                this, request,  response, "", true, 8192, true);
+            rreq.setPageContext(pageContext);
+            ContextLoader.setupContext(ctx, rreq, response);
+
+            final String useTemplate;
+            PageModel pageModel = (PageModel)ctx.get("pageModel");
+            if (    request.getServletPath().endsWith("rss")
+                 && pageModel.getPageByName("_rss") != null )
+            {
+                // If the request specified the "/rss" mapping and the
+                // user has defined an RSS override page, we will use that.
+                useTemplate = pageModel.getPageByName("_rss").getId();
+            }
+            else if (request.getParameter("flavor") != null)
+            {
+                // If request specifies a "flavor" then use that.
+                String flavor = request.getParameter("flavor");
+                useTemplate = "/flavors/" + flavor + ".vm";
+            }
+            else
+            {
+                // Fall through to default RSS page template.
+                useTemplate = "/flavors/rss.vm";
+            }
+            return getTemplate(useTemplate);
+        }
+        catch (Exception e)
+        {
+            mLogger.error("ERROR in RssServlet", e);
+        }
+        return null;
+    }
+
+    //------------------------------------------------------------------------
+    /**
+     * Handle error in Velocity processing.
+     */
+    protected void error( HttpServletRequest req, HttpServletResponse res,
+        Exception e) throws ServletException, IOException
+    {
+        mLogger.warn("ERROR in FlavorServlet",e);
+    }
+}
+

Added: incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/FoafServlet.java
URL: http://svn.apache.org/viewcvs/incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/FoafServlet.java?rev=327589&view=auto
==============================================================================
--- incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/FoafServlet.java (added)
+++ incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/FoafServlet.java Fri Oct 21 14:27:36 2005
@@ -0,0 +1,154 @@
+package org.roller.presentation.velocity;
+
+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.roller.RollerException;
+import org.roller.model.UserManager;
+import org.roller.pojos.WeblogTemplate;
+import org.roller.pojos.UserData;
+import org.roller.pojos.WebsiteData;
+import org.roller.presentation.RollerContext;
+import org.roller.presentation.RollerRequest;
+import org.roller.util.Utilities;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+
+//////////////////////////////////////////////////////////////////////////////
+
+/**
+  * <p>Responsible for rendering FOAF feed.  This servlet requires
+  * that the RequestFilter is in place for it, and should also
+  * have the IfModifiedFilter configured.</p>
+  * 
+  * <p>Resources:<br />
+  * <a href="http://xmlns.com/foaf/0.1/"
+  *     >FOAF Vocabulary Specification</a><br />
+  * <a href="http://www.xml.com/lpt/a/2004/02/04/foaf.html"
+  *     >An Introduction to FOAF</a></p>
+  * 
+  * <p>FOAF Autodiscovery: <br />
+  * <link rel="meta" type="application/rdf+xml" title="FOAF" 
+  *     href="$absBaseURL/foaf/$userName" /> </p>
+  *
+  * @author Lance Lavandowska
+  *
+  * @web.servlet name="FoafServlet"
+  * @web.servlet-mapping url-pattern="/foaf/*"
+  */
+public class FoafServlet extends VelocityServlet
+{
+    static final long serialVersionUID = -1893244416537298619L;
+    
+    private static Log mLogger = LogFactory.getFactory()
+                                           .getInstance(RollerRequest.class);
+
+    /**
+     * This Velocity servlet does not make use of ContextLoader and associated
+     * classes (as do FlavorServlet and PageServlet) because that is more
+     * work than is really necessary.  It implements its own setupContext()
+     * to load necessary values into the Velocity Context.
+     * 
+     * @param ctx
+     * @param rreq
+     * @throws RollerException
+     */
+    public Template handleRequest(HttpServletRequest request,
+                                  HttpServletResponse response, Context ctx)
+    {
+        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
+        {
+            setupContext(ctx, rreq);
+
+            response.setContentType("application/rdf+xml");
+            return getTemplate("/flavors/foaf.vm");
+        }
+        catch (Exception e)
+        {
+            mLogger.error("ERROR in FoafServlet", e);
+        }
+        return null;
+    }
+
+    /**
+	 * @param ctx
+	 */
+	private void setupContext(Context ctx, RollerRequest rreq) throws RollerException
+	{
+        HttpServletRequest request = rreq.getRequest();
+        RollerContext rollerCtx = RollerContext.getRollerContext( request );
+        
+        UserData user = rreq.getUser();
+        ctx.put("fullName", user.getFullName()); // name for FlavorServlet compatibility
+        
+        // foaf:homepage to equal base URL for user
+        String homepage = Utilities.escapeHTML( 
+                rollerCtx.getAbsoluteContextUrl(request) + 
+                    "/page/" + rreq.getUser().getUserName() );
+        ctx.put("websiteURL", homepage); // name for FlavorServlet compatibility
+
+        // see if foaf:weblog is different Page
+        WebsiteData website = rreq.getWebsite();
+        UserManager usrMgr = RollerContext.getRoller(request).getUserManager();
+        org.roller.pojos.Template weblog = website.getPageByName("Weblog");
+        
+        // if weblog != homepage, add to context
+        if (weblog != null && !website.getDefaultPageId().equals(weblog.getId()))
+        {
+            String weblogUrl = Utilities.escapeHTML( 
+                    rollerCtx.getAbsoluteContextUrl(request) + 
+                        "/page/" + rreq.getUser().getUserName() + 
+                            "/" + weblog.getLink() );
+        	ctx.put("weblog", weblogUrl);
+        }
+        
+        // use SHA1 encrypted email address, including mailto: prefix
+        String shaEmail = Utilities.encodePassword(
+                "mailto:" + user.getEmailAddress(), "SHA");
+        ctx.put("shaEmail", shaEmail);
+	}
+
+	//------------------------------------------------------------------------
+    /**
+     * Handle error in Velocity processing.
+     */
+    protected void error( HttpServletRequest req, HttpServletResponse res,
+        Exception e) throws ServletException, IOException
+    {
+        mLogger.warn("ERROR in FoafServlet",e);
+    }
+}

Added: incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/LanguageServlet.java
URL: http://svn.apache.org/viewcvs/incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/LanguageServlet.java?rev=327589&view=auto
==============================================================================
--- incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/LanguageServlet.java (added)
+++ incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/LanguageServlet.java Fri Oct 21 14:27:36 2005
@@ -0,0 +1,121 @@
+/*
+ * Filename: LanguageServlet.java
+ * 
+ * Created on 02-May-04
+ */
+package org.roller.presentation.velocity;
+
+import java.util.Locale;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.struts.Globals;
+import org.apache.velocity.Template;
+import org.apache.velocity.context.Context;
+import org.roller.presentation.LanguageUtil;
+import org.roller.presentation.RollerContext;
+
+/**
+ * Changes the language of the current Locale to the language specified
+ * by the user. The new language must be supported by Roller.  
+ * 
+ * And new supported languages to the web.servlet-init-param value. Make sure you add language-only
+ * values at the end of a chain. So "en_US,en" instead of "en,en_US". And no spaces.
+ * 
+ * @web.servlet name="LanguageServlet" load-on-startup="10"
+ * @web.servlet-init-param name="org.roller.presentation.supported.languages" value="en,nl,zh_cn,zh_tw,vi"
+ *  
+ * @web.servlet-mapping url-pattern="/language/*"
+ * 
+ * @author <a href="mailto:molen@mail.com">Jaap van der Molen</a>
+ * @version $Revision: 1.8 $
+ */
+public class LanguageServlet extends BasePageServlet
+{
+    static final long serialVersionUID = -6548723098429557183L;
+    
+	/**
+	 * Logger
+	 */
+	private static Log mLogger =
+		LogFactory.getFactory().getInstance(LanguageServlet.class);
+
+	/**
+	 * @see org.roller.presentation.velocity.BasePageServlet#init(javax.servlet.ServletConfig)
+	 */
+	public void init(ServletConfig config) throws ServletException
+	{
+		super.init(config);
+
+		// load supported languages
+		ServletContext ctx = config.getServletContext();
+		String supportedLanguages =
+			config.getInitParameter(LanguageUtil.SUPPORTED_LANGUAGES);
+		if (supportedLanguages != null
+			&& supportedLanguages.trim().length() > 0)
+		{
+			// extract langauges
+			ctx.setAttribute(
+				LanguageUtil.SUPPORTED_LANGUAGES,
+				LanguageUtil.extractLanguages(supportedLanguages));
+		}
+	}
+
+	/**
+	 * @see org.roller.presentation.velocity.BasePageServlet#handleRequest(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, org.apache.velocity.context.Context)
+	 */
+	public Template handleRequest(
+		HttpServletRequest request,
+		HttpServletResponse response,
+		Context ctx) throws Exception
+	{
+		mLogger.debug("Processing language change...");
+        ServletContext servletContext = RollerContext.getServletContext();
+        
+		Locale[] supportedLanguages =
+			LanguageUtil.getSupportedLanguages(servletContext);
+
+		if (supportedLanguages == null || supportedLanguages.length == 0)
+		{
+			// add error message
+			ctx.put("languageError", "Unable to switch language: no supported languages defined.");
+			// proceed with request serving
+			return super.handleRequest(request, response, ctx);
+		}
+
+		String newLang = request.getParameter("language");
+		mLogger.debug("New language in Request: " + newLang);
+		if (newLang == null || newLang.length() == 0) {
+			// add error message
+			ctx.put("languageError", "Unable to switch language: no new language specified.");
+			// proceed with request serving
+			return super.handleRequest(request, response, ctx);
+		}
+		
+		Locale newLocale = LanguageUtil.createLocale(newLang);
+
+		// verify if new language is supported
+		if (!LanguageUtil.isSupported(newLocale, servletContext)) {
+			// add error message
+			ctx.put("languageError", "Unable to switch language: new language '"+newLang+"' is not supported.");
+			// proceed with request serving
+			return super.handleRequest(request, response, ctx);
+		}
+
+		// by now, all should be fine: change Locale
+		HttpSession session = request.getSession();
+		session.setAttribute(Globals.LOCALE_KEY, newLocale);
+		mLogger.debug("Changed language to: " + newLocale);
+
+		// proceed with request serving
+		return super.handleRequest(request, response, ctx);
+	}
+
+}