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 [37/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/PageModel.java
URL: http://svn.apache.org/viewcvs/incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/PageModel.java?rev=327589&view=auto
==============================================================================
--- incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/PageModel.java (added)
+++ incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/PageModel.java Fri Oct 21 14:27:36 2005
@@ -0,0 +1,827 @@
+package org.roller.presentation.velocity;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.roller.RollerException;
+import org.roller.config.RollerRuntimeConfig;
+import org.roller.model.BookmarkManager;
+import org.roller.model.RefererManager;
+import org.roller.model.Roller;
+import org.roller.model.RollerFactory;
+import org.roller.pojos.Template;
+import org.roller.model.UserManager;
+import org.roller.model.WeblogManager;
+import org.roller.pojos.CommentData;
+import org.roller.pojos.FolderData;
+import org.roller.pojos.RefererData;
+import org.roller.pojos.UserData;
+import org.roller.pojos.WeblogCategoryData;
+import org.roller.pojos.WeblogEntryData;
+import org.roller.pojos.WebsiteData;
+import org.roller.pojos.wrapper.CommentDataWrapper;
+import org.roller.pojos.wrapper.FolderDataWrapper;
+import org.roller.pojos.wrapper.RefererDataWrapper;
+import org.roller.pojos.wrapper.TemplateWrapper;
+import org.roller.pojos.wrapper.WeblogCategoryDataWrapper;
+import org.roller.pojos.wrapper.WeblogEntryDataWrapper;
+import org.roller.presentation.RollerRequest;
+import org.roller.util.StringUtils;
+
+/**
+ * Provides Roller page templates with access to Roller domain model objects.
+ *  
+ * @author llavandowska
+ * @author David M Johnson
+ */
+public class PageModel
+{
+    public final static String VELOCITY_NULL = "nil";
+    
+    protected static Log mLogger = 
+       LogFactory.getFactory().getInstance(PageModel.class);
+    
+    private BookmarkManager      mBookmarkMgr = null;
+    private WeblogManager        mWeblogMgr = null;
+    private UserManager          mUserMgr = null;
+    private RefererManager       mRefererMgr = null;
+
+    private Map                  mCategories = new HashMap();
+    private HashMap              mPageMap = new HashMap();
+    private RollerRequest        mRollerReq = null;
+    private String               mUsername = null;
+    private WebsiteData          mWebsite = null;
+    
+    private WeblogEntryDataWrapper      mNextEntry = null;
+    private WeblogEntryDataWrapper      mPreviousEntry = null;
+
+    private WeblogEntryDataWrapper      mLastEntry = null;
+
+    private WeblogEntryDataWrapper      mFirstEntry = null;
+        
+    //------------------------------------------------------------------------
+    
+    /** init() must be called to complete construction */
+    public PageModel() {}
+    
+    /**
+     * Initialize PageModel and allow PageModel to initialized VelocityContext.
+     * @param rreq
+     * @param ctx
+     */
+    public void init(RollerRequest rreq)
+    {
+        mRollerReq = rreq;
+        UserData user = null;
+        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 )
+        {
+            mUsername = user.getUserName();
+        }
+        
+        try
+        {
+            Roller mRoller = RollerFactory.getRoller();
+            mBookmarkMgr = mRoller.getBookmarkManager();
+            mRefererMgr  = mRoller.getRefererManager();
+            mUserMgr     = mRoller.getUserManager();
+            mWeblogMgr   = mRoller.getWeblogManager();
+            
+            /** 
+             * Preload what we can for encapsulation.  What we cannot preload we
+             * will use the Managers later to fetch.
+             */
+            if ( mUsername != null )
+            {
+                // if we have website from RollerRequest, use it
+                mWebsite = rreq.getWebsite();
+                if(mWebsite == null)
+                    mWebsite = mUserMgr.getWebsite(user.getUserName());
+                
+                // Get the pages, put into context & load map
+                List pages = mWebsite.getPages();
+                Iterator pageIter = pages.iterator();
+                while (pageIter.hasNext())
+                {
+                    Template page = (Template) pageIter.next();
+                    mPageMap.put(page.getName(), TemplateWrapper.wrap(page));
+                }
+            }
+            
+        }
+        catch (RollerException e)
+        {
+            mLogger.error("PageModel Roller get*Manager Exception", e);
+        }
+    }
+    
+    //------------------------------------------------------------------------
+    
+    /** Encapsulates folder.getBookmarks() & sorting */
+    public Collection getBookmarks(FolderDataWrapper folder)
+    {
+        mLogger.debug("Getting bookmarks for folder : "+folder.getName());
+        
+        // since we already have a wrapped pojo we know the output
+        // will be wrapped as well :)
+        Collection bookmarks = folder.getBookmarks();
+        
+        // TODO: need to setup new BookmarkWrapperComparator
+        //List mBookmarks = new ArrayList(bookmarks);
+        //Collections.sort( mBookmarks, new BookmarkComparator() );
+        
+        return bookmarks;
+    }
+    
+    //------------------------------------------------------------------------
+    
+    /** Get top level bookmark folders. */
+    public Collection getTopLevelFolders()
+    {
+        List tops = null;
+        try
+        {
+            Collection mTops = mBookmarkMgr.getRootFolder(mWebsite).getFolders();
+            
+            // wrap pojos
+            tops = new ArrayList(mTops.size());
+            Iterator it = mTops.iterator();
+            int i=0;
+            while(it.hasNext()) {
+                tops.add(i, FolderDataWrapper.wrap((FolderData) it.next()));
+                i++;
+            }
+        }
+        catch (RollerException e)
+        {
+            tops = new ArrayList();
+        }
+        return tops;
+    }
+    
+    //------------------------------------------------------------------------
+    
+    /** Encapsulates WeblogManager.getComments().size() */
+    public int getCommentCount(String entryId)
+    {
+        try
+        {
+            return mWeblogMgr.getComments( entryId ).size();
+        }
+        catch (RollerException e)
+        {
+            mLogger.error("PageModel getCommentCount()", e);
+        }
+        return 0;
+    }
+    
+    //------------------------------------------------------------------------
+    
+    /** Get comments for weblog entry specified by request */
+    public List getComments( WeblogEntryData entry )
+    {
+        List comments = new ArrayList();
+        try
+        {
+            Collection mComments = mWeblogMgr.getComments( entry.getId() );
+            
+            // wrap pojos
+            comments = new ArrayList(mComments.size());
+            Iterator it = mComments.iterator();
+            int i=0;
+            while(it.hasNext()) {
+                comments.add(i, CommentDataWrapper.wrap((CommentData) it.next()));
+                i++;
+            }
+        }
+        catch (RollerException e)
+        {
+            mLogger.error("PageModel getComments()", e);
+        }
+        return comments;
+    }
+    
+    //------------------------------------------------------------------------
+    
+    /** Encapsulates RefererManager */
+    public int getDayHits()
+    {
+        try
+        {
+            return mRefererMgr.getDayHits(mRollerReq.getWebsite());
+        }
+        catch (RollerException e)
+        {
+            mLogger.error("PageModel getDayHits()", e);
+        }
+        return 0;
+    }
+    
+    //------------------------------------------------------------------------
+    
+    /** Encapsulates BookmarkManager.getFolder() */
+    public FolderDataWrapper getFolder(String folderPath)
+    {
+        try
+        {
+            return FolderDataWrapper.wrap(
+                    mBookmarkMgr.getFolder(mWebsite, folderPath));
+        }
+        catch (RollerException e)
+        {
+            mLogger.error("PageModel getFolder()", e);
+        }
+        return null;
+    }
+        
+    //------------------------------------------------------------------------
+    
+    /** Encapsulates UserManager.getPageByName() */
+    public TemplateWrapper getUsersPageByName(WebsiteData website, String pageName)
+    {
+        TemplateWrapper page = null;
+        try
+        {
+            if (website == null) 
+                throw new NullPointerException("website is null");
+                
+            if (pageName == null) 
+                throw new NullPointerException("pageName is null");
+                
+            page = TemplateWrapper.wrap(website.getPageByName(pageName));
+        }
+        catch (NullPointerException npe)
+        {
+            mLogger.warn(npe.getMessage());
+        }
+        catch (RollerException e)
+        {
+            mLogger.error("ERROR getting user's page by name: " + e.getMessage(),e);
+        }
+        return page;
+    }
+    
+    //------------------------------------------------------------------------
+    
+    /** Encapsulates UserManager.getPageByName() */
+    public TemplateWrapper getPageByName(String pageName)
+    {
+        return (TemplateWrapper) mPageMap.get(pageName);
+    }
+    
+    //------------------------------------------------------------------------
+    
+    /** Encapsulates UserManager.getPageByName() */
+    public String getPageIdByName(String pageName)
+    {
+        mLogger.debug("looking up page ["+pageName+"]");
+        
+        String template_id = null;
+        
+        try {
+            Template pd = mWebsite.getPageByName(pageName);
+            template_id = pd.getId();
+        } catch(Exception e) {
+            mLogger.error(e);
+        }
+        
+        mLogger.debug("returning template id ["+template_id+"]");
+        
+        return template_id;
+    }
+    
+    //------------------------------------------------------------------------
+    
+    /**
+     * Get collection of user pages.
+     * @return
+     */
+    public Object getPages()
+    {
+        return mPageMap.values();
+    }
+    
+    //------------------------------------------------------------------------
+    
+    /** 
+     * Returns a map of up to 100 recent weblog entries for the user and day 
+     * specified in the request, filtered by the category specified by the 
+     * request, limited by the 'maxEntries' argument, and sorted by reverse 
+     * chronological order.
+     * 
+     * <p>This method will look for a category name in the following places
+     * and in the following order:</p>
+     * <ul>
+     * <li>The request via RollerRequest.getWeblogCategory().</li>
+     * <li>The categoryName argument to this method.</li>
+     * <li>The default category for the website specified by the request via 
+     *     RollerRequest.getWebsite().getDefaultCategory().</li>
+     * <li></li>
+     * </ul>
+     * 
+     * @param maxEntries Maximum number of entries to be returned.
+     * @param categoryName Only return entries from this category and it's 
+     *         subcategories. If null, returns all categories of entry.
+     * @return Map of Lists of WeblogEntryData, keyed by 8-char date strings.
+     */
+    public Map getRecentWeblogEntries(int maxEntries, String categoryName)
+    {
+        if (VELOCITY_NULL.equals(categoryName)) categoryName = null;
+        Map ret = new HashMap();
+        try
+        {
+            Date day = mRollerReq.getDate();
+            if (day == null) day = new Date();
+            
+            // If request specifies a category, then use that
+            String catParam = null;
+            if (mRollerReq.getWeblogCategory() != null)
+            {
+                catParam = mRollerReq.getWeblogCategory().getPath();
+            }
+            else if (categoryName != null)
+            {
+                // use category argument instead
+                catParam = categoryName;
+            }
+            else if (mRollerReq.getWebsite() != null) // MAIN
+            {
+                catParam = mRollerReq.getWebsite().getDefaultCategory().getPath();
+                if (catParam.equals("/"))
+                {
+                    catParam = null;
+                }
+            }
+            
+            Map mRet = RollerFactory.getRoller().getWeblogManager().getWeblogEntryObjectMap(
+                            mRollerReq.getWebsite(),  
+                            null,                     // startDate
+                            day,                 // endDate
+                            catParam,                 // catName
+                            WeblogManager.PUB_ONLY,   // status
+                            new Integer(maxEntries)); // maxEntries
+            
+            // need to wrap pojos
+            java.util.Date key = null;
+            Iterator days = mRet.keySet().iterator();
+            while(days.hasNext()) {
+                key = (java.util.Date)days.next();
+                
+                // now we need to go through each entry in a day and wrap
+                List wrappedEntries = new ArrayList();
+                List entries = (List) mRet.get(key);
+                for(int i=0; i < entries.size(); i++) {
+                    wrappedEntries.add(i, 
+                            WeblogEntryDataWrapper.wrap((WeblogEntryData)entries.get(i)));
+                }
+                mRet.put(key, wrappedEntries);
+            }
+
+	    ret = mRet;
+            
+            setFirstAndLastEntries( ret );
+        }
+        catch (Exception e)
+        {
+            mLogger.error("PageModel getRecentWeblogEntries()", e);
+        }
+        return ret;
+    }
+    
+    //------------------------------------------------------------------------
+    
+    /**
+     * Pull the last WeblogEntryData out of the Map.
+     * @param ret
+     */
+    private void setFirstAndLastEntries(Map days)
+    {
+        int numDays = days.keySet().size();
+        if (numDays > 0) // there is at least one day
+        {
+            // get first entry in map
+            Object[] keys = days.keySet().toArray(new Object[numDays]);
+            List vals = (List)days.get( keys[0] );
+            int valSize = vals.size();
+            if (valSize > 0) 
+            {
+                mFirstEntry = (WeblogEntryDataWrapper)vals.get(0);
+            }
+            
+            // get last entry in map
+            vals = (List)days.get( keys[--numDays] );
+            valSize = vals.size();
+            if (valSize > 0)
+            {
+                mLastEntry = (WeblogEntryDataWrapper)vals.get(--valSize);
+            }
+        }
+    }
+
+    //------------------------------------------------------------------------
+    
+    /** 
+     * Returns list of recent weblog entries for the user and day specified in 
+     * the request, filtered by the category specified by the request, limited 
+     * by the 'maxEntries' argument, and sorted by reverse chronological order.
+     * 
+     * <p>This method will look for a category name in the same places and 
+     * same order as does the getRecentWeblogEntries() method.</p>
+     * 
+     * @param maxEntries   Maximum number of entries to be returned.
+     * @param categoryName Only return entries from this category and it's 
+     *         subcategories. If null, returns all categories of entry.
+     * @return List of WeblogEntryData objects in revese chronological order.
+     */
+    public List getRecentWeblogEntriesArray(int maxEntries, String categoryName)
+    {
+        if (VELOCITY_NULL.equals(categoryName)) categoryName = null;
+        List ret = new ArrayList();
+        try
+        {
+            Date day = mRollerReq.getDate();
+            if (day == null) day = new Date();
+            
+            // If request specifies a category, then use that
+            String catParam = null;
+            if (mRollerReq.getWeblogCategory() != null)
+            {
+                catParam = mRollerReq.getWeblogCategory().getPath();
+            }
+            else if (categoryName != null)
+            {
+                // use category argument instead
+                catParam = categoryName;
+            }
+            else if (mRollerReq.getWebsite() != null) // MAIN
+            {
+                catParam = mRollerReq.getWebsite().getDefaultCategory().getPath();
+                if (catParam.equals("/"))
+                {
+                    catParam = null;
+                }
+            }
+            WeblogManager mgr = RollerFactory.getRoller().getWeblogManager();
+            
+            //ret = mgr.getRecentWeblogEntriesArray( 
+                //name, day, catParam, maxEntries, true );
+            
+            List mEntries = mgr.getWeblogEntries(
+                            mRollerReq.getWebsite(), 
+                            null,                    // startDate
+                            day,                      // endDate
+                            catParam,                 // catName
+                            WeblogManager.PUB_ONLY,   // status
+                            new Integer(maxEntries)); // maxEntries
+            
+            // wrap pojos
+            ret = new ArrayList(mEntries.size());
+            Iterator it = mEntries.iterator();
+            int i=0;
+            while(it.hasNext()) {
+                ret.add(i, WeblogEntryDataWrapper.wrap((WeblogEntryData) it.next()));
+                i++;
+            }
+        }
+        catch (Exception e)
+        {
+            mLogger.error("PageModel getRecentWeblogEntries()", e);
+        }
+        return ret;
+    }   
+    
+    //------------------------------------------------------------------------
+    
+    /** Encapsulates RefererManager **/
+    public List getReferers(String date)
+    {
+        date = date.trim();
+        ArrayList referers = new ArrayList();
+        try
+        {
+            List refs = 
+                mRefererMgr.getReferersToDate(mRollerReq.getWebsite(), date);
+            
+            for (Iterator rdItr = refs.iterator(); rdItr.hasNext();) {
+                RefererData referer = (RefererData) rdItr.next();
+                String title =referer.getTitle();
+                String excerpt = referer.getExcerpt();
+                if (   StringUtils.isNotEmpty(title) 
+                    && StringUtils.isNotEmpty(excerpt) )
+                {
+                    if (   referer.getVisible().booleanValue() 
+                        || this.mRollerReq.isUserAuthorizedToEdit() )
+                    { 
+                        referers.add(RefererDataWrapper.wrap(referer));
+                    }
+                }
+            }
+            
+        }
+        catch (Exception e)
+        {
+            mLogger.error("PageModel getReferersToDate() fails with URL" 
+                            + mRollerReq.getRequestURL(), e);
+        }
+        return referers;
+    }   
+    
+    //------------------------------------------------------------------------
+    
+    /** Encapsulates RefererManager */
+    public List getTodaysReferers()
+    {
+        List referers = null;
+        try
+        {
+            List mReferers = mRefererMgr.getTodaysReferers(mRollerReq.getWebsite());
+            
+            // wrap pojos
+            referers = new ArrayList(mReferers.size());
+            Iterator it = mReferers.iterator();
+            int i=0;
+            while(it.hasNext()) {
+                referers.add(i, RefererDataWrapper.wrap((RefererData) it.next()));
+                i++;
+            }
+         
+        }
+        catch (RollerException e)
+        {
+            mLogger.error("PageModel getTodaysReferers()", e);
+        }
+        return (referers == null ? Collections.EMPTY_LIST : referers);
+    }
+    
+    //------------------------------------------------------------------------
+    
+    /** Encapsulates RefererManager */
+    public int getTotalHits()
+    {
+        try
+        {
+            return mRefererMgr.getTotalHits(mRollerReq.getWebsite());
+        }
+        catch (RollerException e)
+        {
+            mLogger.error("PageModel getTotalHits()", e);
+        }
+        return 0;
+    }
+    
+    //------------------------------------------------------------------------
+    /**
+     * Returns most recent update time of collection of weblog entries.
+     * @param weblogEntries Collection of weblog entries.
+     * @return Most recent update time.
+     */
+    public static Date getUpdateTime( ArrayList weblogEntries )
+    {
+        Date updateTime = null;
+        Iterator iter = weblogEntries.iterator();
+        while (iter.hasNext())
+        {
+            // NOTE: this will need to be WeblogEntryDataWrapper
+            WeblogEntryData wd = (WeblogEntryData)iter.next();
+            if ( updateTime == null )
+            {
+                updateTime = wd.getUpdateTime();
+            }
+            //else if ( updateTime.compareTo(wd.getUpdateTime()) < 0 )
+            else if (updateTime.before( wd.getUpdateTime() ))
+            {
+                updateTime = wd.getUpdateTime();
+            }
+        }
+        return updateTime;
+    }
+
+    //------------------------------------------------------------------------
+    
+    /** Encapsulates WeblogManager.getWeblogCategories() */
+    public List getWeblogCategories(String categoryName)
+    {
+        List ret = null;
+        if (VELOCITY_NULL.equals(categoryName)) categoryName = null;
+        
+        // Make sure we have not already fetched this category.
+        if (categoryName != null)
+        {
+            ret = (List)mCategories.get(categoryName);
+        }
+        
+        if (null == ret) 
+        {
+            try 
+            {
+                WeblogCategoryData category = null;                
+                if (categoryName != null)
+                {
+                    category = mWeblogMgr.getWeblogCategoryByPath(
+                                  mRollerReq.getWebsite(), null, categoryName);                    
+                }
+                else 
+                {
+                    category = mRollerReq.getWebsite().getDefaultCategory();
+                }
+                
+                List mRet = category.getWeblogCategories();
+                
+                // wrap pojos
+                ret = new ArrayList(mRet.size());
+                Iterator it = mRet.iterator();
+                int i=0;
+                while(it.hasNext()) {
+                    ret.add(i, WeblogCategoryDataWrapper.wrap((WeblogCategoryData) it.next()));
+                    i++;
+                }
+        
+                mCategories.put(categoryName, ret);
+            }
+            catch (RollerException e) 
+            {
+                mLogger.error(e);
+            }
+        }       
+        return ret;
+    }
+    
+    //------------------------------------------------------------------------
+    
+    /** Encapsulates RollerRequest.getWeblogEntry() */
+    public WeblogEntryDataWrapper getWeblogEntry()
+    {
+        WeblogEntryData entry = mRollerReq.getWeblogEntry();
+        
+        if(entry != null)
+            return WeblogEntryDataWrapper.wrap(entry);
+        else
+            return null;
+    }
+    
+    //------------------------------------------------------------------------
+    
+    /**
+     * Get the next occurring Entry.
+     */
+    public WeblogEntryDataWrapper getNextEntry()
+    {
+        WeblogEntryDataWrapper currentEntry = getWeblogEntry();
+        if (mFirstEntry != null) currentEntry = mFirstEntry;
+        if (mNextEntry == null && currentEntry != null) 
+        {
+            String catName = null;
+            if (mRollerReq.getWeblogCategory() != null)
+            {
+                catName = mRollerReq.getWeblogCategory().getName();
+            }
+            try
+            {
+                WeblogEntryData nextEntry = 
+                        mWeblogMgr.getNextEntry(currentEntry.getPojo(), catName);
+                
+                if(nextEntry != null)
+                    mNextEntry = WeblogEntryDataWrapper.wrap(nextEntry);
+                
+                // make sure that mNextEntry is not published to future
+                if (mNextEntry != null && 
+                    mNextEntry.getPubTime().after( new Date() ))
+                {
+                    mNextEntry = null;
+                }
+            }
+            catch (RollerException e)
+            {
+                mLogger.error("PageModel.getNextEntry)", e);
+            }
+        }
+        return mNextEntry;
+    }
+
+    //------------------------------------------------------------------------
+    
+    /**
+     * Get the previous occurring Entry.
+     */
+    public WeblogEntryDataWrapper getPreviousEntry()
+    {
+        WeblogEntryDataWrapper currentEntry = getWeblogEntry();
+        if (mLastEntry != null) currentEntry = mLastEntry;
+        if (mPreviousEntry == null && currentEntry != null )
+        {   
+            String catName = null;
+            if (mRollerReq.getWeblogCategory() != null)
+            {
+                catName = mRollerReq.getWeblogCategory().getName();
+            }
+            try
+            {
+                WeblogEntryData prevEntry = 
+                        mWeblogMgr.getPreviousEntry(currentEntry.getPojo(), catName);
+                
+                if(prevEntry != null)
+                    mPreviousEntry = WeblogEntryDataWrapper.wrap(prevEntry);
+            }
+            catch (RollerException e)
+            {
+                mLogger.error("PageModel.getPreviousEntry)", e);
+            }            
+        }
+        return mPreviousEntry;
+    }
+
+    //------------------------------------------------------------------------
+    
+    public boolean isUserAuthorizedToEdit()
+    {
+        try
+        {
+            return mRollerReq.isUserAuthorizedToEdit();
+        }
+        catch (Exception e)
+        {
+            mLogger.warn("PageModel.isUserAuthorizedToEdit)", e);
+        }
+        return false;
+    }
+    
+    //------------------------------------------------------------------------
+    
+    public String getRequestParameter(String key)
+    {
+        return mRollerReq.getRequest().getParameter(key);
+    }
+    
+    //------------------------------------------------------------------------
+    
+    public FolderDataWrapper getFolderByPath(String path)
+    {
+        try
+        {
+            FolderData folder = mBookmarkMgr.getFolderByPath(
+                mUserMgr.getWebsite(mUsername), null, path);
+            
+            if(folder != null)
+                return FolderDataWrapper.wrap(folder);
+        }
+        catch (RollerException e)
+        {
+            mLogger.error(e);
+        }
+        
+        return null;
+    }
+
+    /**
+     * Facade for WeblogManager.getRecentComments().
+     * Get the most recent (chronologically) posted Comments
+     * for this website, limited to maxCount.  
+     * @return List of Comments.
+     */
+    public List getRecentComments(int maxCount)
+    {
+        List recentComments = new ArrayList();
+        try
+        {
+            List recent = mWeblogMgr.getRecentComments(mRollerReq.getWebsite(), maxCount);
+            
+            // wrap pojos
+            recentComments = new ArrayList(recent.size());
+            Iterator it = recent.iterator();
+            int i=0;
+            while(it.hasNext()) {
+                recentComments.add(i, CommentDataWrapper.wrap((CommentData) it.next()));
+                i++;
+            }
+        }
+        catch (RollerException e)
+        {
+            mLogger.error(e);
+        }
+        return recentComments;
+    }
+ 
+    public boolean getEmailComments() 
+    {
+        WebsiteData website = mRollerReq.getWebsite();
+        boolean emailComments = RollerRuntimeConfig.getBooleanProperty("users.comments.emailnotify");
+        
+        return (website.getEmailComments().booleanValue() && emailComments);
+    }
+}

Added: incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/PagePlugin.java
URL: http://svn.apache.org/viewcvs/incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/PagePlugin.java?rev=327589&view=auto
==============================================================================
--- incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/PagePlugin.java (added)
+++ incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/PagePlugin.java Fri Oct 21 14:27:36 2005
@@ -0,0 +1,84 @@
+/*
+ * Created on May 26, 2003
+ */
+package org.roller.presentation.velocity;
+
+import org.apache.velocity.context.Context;
+import org.roller.RollerException;
+import org.roller.pojos.WeblogEntryData;
+import org.roller.presentation.RollerRequest;
+
+/**
+ * Interface for a Roller WeblogEntry Plugin.  Implementors of this
+ * class are expected to operate on the text field of a WeblogEntryData
+ * object.  Existing implementations autogenerate links from Bookmarks (BookmarkPlugin),
+ * obfuscate email addresses (EmailObfuscator), truncate an Entry at 250
+ * characters and add a Read More... link (ReadMorePlugin), and transform
+ * 'simple markup' according to several schemes (JSPWiki, Radeox, Textile).
+ * See the "contrib" directory for these implementations.
+ * 
+ * @author David M Johnson
+ */
+public interface PagePlugin
+{    
+    public String name = "PagePlugin";
+    
+    /** 
+     * Plugins can this as an opportunity to add any required objects
+     * to the RollerRequest and the VelocityContext, or to initialize
+     * any internal values reachable from RollerRequest.
+     * 
+     * @param rreq Plugins may need to access RollerRequest.
+     * @param ctx Plugins may place objects into the Velocity Context.
+     */    
+    public void init(RollerRequest rreq, Context ctx) throws RollerException;
+
+	/**
+     * Apply plugin to content of specified WeblogEntry.  The WeblogEntryData
+     * is actually a copy of the real thing, so that changes made via
+     * entry.setText() are not persisted.  Notice this; no changes made
+     * to the entry will be persisted.
+     * Some Plugins are only suited to rendering during Page display 
+     * (not when generating RSS or Trackback content or in the 
+     * Entry Preview) - ReadMorePlugin is an example of such a case.  
+     * If the skipFlag is set to 'true' it merely returns the 
+     * unadorned contents of entry.getText().
+     * 
+     * @param entry WeblogEntry to which plugin should be applied.
+     * @param skipFlag Should processing be skipped.
+     * @return Results of applying plugin to entry.
+	 */
+	public String render(WeblogEntryData entry, boolean skipFlag);
+    
+    /**
+     * Apply plugin to content of specified String.  Some plugins
+     * may require interaction with an Entry to do its job (such
+     * as the BookmarkPlugin) and will simply return the String 
+     * that was passed in.
+     * 
+     * @param str String to which plugin should be applied.
+     * @return Results of applying plugin to string.
+     */
+    public String render(String str);
+    
+    /**
+     * Must implement toString(), returning the human-friendly
+     * name of this Plugin.  This is what users will see.
+     * @return The human-friendly name of this Plugin.
+     */
+    public String toString();
+    
+    /**
+     * Returns the human-friendly name of this Plugin.
+     * This is what users will see.
+     * @return The human-friendly name of this Plugin.
+     */
+    public String getName();
+    
+    /**
+     * Briefly describes the function of the Plugin. May
+     * contain HTML.
+     * @return A brief description of the Plugin.
+     */
+    public String getDescription();
+}

Added: incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/PageServlet.java
URL: http://svn.apache.org/viewcvs/incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/PageServlet.java?rev=327589&view=auto
==============================================================================
--- incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/PageServlet.java (added)
+++ incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/PageServlet.java Fri Oct 21 14:27:36 2005
@@ -0,0 +1,25 @@
+package org.roller.presentation.velocity;
+
+
+/**
+ * The PageServlet handles all requests for weblog pages at /page/*
+ *
+ * All the real work is handled by the BasePageServlet though.
+ *
+ * @see org.roller.presentation.velocity.BasePageServlet
+ * 
+ * @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/*"
+ */ 
+public class PageServlet extends BasePageServlet {
+    
+    /**
+     * This class used to have some special velocity init code that was
+     * related to how previewing worked, but it became obsolete when we
+     * redid the theme management stuff, so now this class does nothing.
+     */
+}
+

Added: incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/PlanetFeedServlet.java
URL: http://svn.apache.org/viewcvs/incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/PlanetFeedServlet.java?rev=327589&view=auto
==============================================================================
--- incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/PlanetFeedServlet.java (added)
+++ incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/PlanetFeedServlet.java Fri Oct 21 14:27:36 2005
@@ -0,0 +1,91 @@
+package org.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.roller.RollerException;
+import org.roller.model.PlanetManager;
+import org.roller.presentation.RollerRequest;
+import org.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 = 
+                rreq.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());
+            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/branches/roller_1.x/src/org/roller/presentation/velocity/PlanetPageModel.java
URL: http://svn.apache.org/viewcvs/incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/PlanetPageModel.java?rev=327589&view=auto
==============================================================================
--- incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/PlanetPageModel.java (added)
+++ incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/PlanetPageModel.java Fri Oct 21 14:27:36 2005
@@ -0,0 +1,69 @@
+
+package org.roller.presentation.velocity;
+import java.util.ArrayList;
+import java.util.List;
+import org.roller.model.PlanetManager;
+import org.roller.model.Roller;
+import org.roller.pojos.PlanetGroupData;
+import org.roller.pojos.PlanetSubscriptionData;
+import org.roller.presentation.RollerRequest;
+import org.roller.presentation.velocity.PageModel;
+
+/**
+ * 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 = rreq.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/branches/roller_1.x/src/org/roller/presentation/velocity/PreviewServlet.java
URL: http://svn.apache.org/viewcvs/incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/PreviewServlet.java?rev=327589&view=auto
==============================================================================
--- incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/PreviewServlet.java (added)
+++ incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/PreviewServlet.java Fri Oct 21 14:27:36 2005
@@ -0,0 +1,143 @@
+
+package org.roller.presentation.velocity;
+
+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.roller.ThemeNotFoundException;
+import org.roller.model.Roller;
+import org.roller.model.RollerFactory;
+import org.roller.model.ThemeManager;
+import org.roller.model.UserManager;
+import org.roller.pojos.Theme;
+import org.roller.pojos.UserData;
+import org.roller.pojos.WeblogTemplate;
+import org.roller.pojos.WebsiteData;
+import org.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-init-param name="properties" value="/WEB-INF/velocity.properties"
+ * @web.servlet-mapping url-pattern="/preview/*"
+ */
+public class PreviewServlet extends BasePageServlet {
+    
+    private static Log mLogger =
+            LogFactory.getFactory().getInstance(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
+            }
+        }
+        
+        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;
+        Exception pageException = null;
+        
+        try {
+            PageContext pageContext =
+                    JspFactory.getDefaultFactory().getPageContext(
+                    this, request, response,"", true, 8192, true);
+            // Needed to init request attributes, etc.
+            RollerRequest rreq = RollerRequest.getRollerRequest(pageContext);
+            UserManager userMgr = RollerFactory.getRoller().getUserManager();
+            
+            WebsiteData website = null;
+            if (request.getAttribute(RollerRequest.OWNING_USER) != null) {
+                UserData user = (UserData)
+                    request.getAttribute(RollerRequest.OWNING_USER);
+                website = userMgr.getWebsite(user.getUserName());
+            } 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.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(404);
+                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( Exception e ) {
+            pageException = e;
+            response.setStatus( HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+        }
+        
+        if (pageException != null) {
+            mLogger.error("EXCEPTION: in RollerServlet", pageException);
+            request.setAttribute("DisplayException", pageException);
+        }
+        
+        return outty;
+    }
+}
+

Added: incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/RollerResourceLoader.java
URL: http://svn.apache.org/viewcvs/incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/RollerResourceLoader.java?rev=327589&view=auto
==============================================================================
--- incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/RollerResourceLoader.java (added)
+++ incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/RollerResourceLoader.java Fri Oct 21 14:27:36 2005
@@ -0,0 +1,144 @@
+package org.roller.presentation.velocity;
+
+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.roller.RollerException;
+import org.roller.model.Roller;
+import org.roller.pojos.WeblogTemplate;
+import org.roller.presentation.RollerContext;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+
+/**
+ * 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.getFactory().getInstance(RollerResourceLoader.class);
+        
+    public void init( ExtendedProperties configuration)
+    {
+        if (mLogger.isDebugEnabled())
+        {
+		    mLogger.debug(configuration);
+        }
+    }
+    
+    /**
+     * The web-app startup timing may be tricky.  In the case that roller is
+     * null (which means that RollerContext may not have had a chance to call
+     * RollerFactory yet) ask RollerContext for Roller because it has the
+     * information necessary for RollerFactory to do its job.
+     * @return Roller
+     */
+    private Roller getRoller()
+    {
+    	return RollerContext.getRoller( null );
+    }
+
+    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 = 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 = 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;
+    }
+
+    public WeblogTemplate getPage(String id) throws RollerException
+    {
+    	if (getRoller() == null) throw new RollerException(
+			"RollerResourceLoader.getRoller() returned NULL");
+        return getRoller().getUserManager().retrievePage(id); //retrievePageReadOnly( id );
+    }
+
+}

Added: incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/SearchServlet.java
URL: http://svn.apache.org/viewcvs/incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/SearchServlet.java?rev=327589&view=auto
==============================================================================
--- incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/SearchServlet.java (added)
+++ incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/SearchServlet.java Fri Oct 21 14:27:36 2005
@@ -0,0 +1,434 @@
+package org.roller.presentation.velocity;
+
+import java.io.IOException;
+import java.util.Date;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.jsp.JspFactory;
+import javax.servlet.jsp.PageContext;
+
+import org.apache.commons.collections.comparators.ReverseComparator;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.search.Hits;
+import org.apache.velocity.Template;
+import org.apache.velocity.context.Context;
+import org.roller.RollerException;
+import org.roller.business.search.FieldConstants;
+import org.roller.business.search.operations.SearchOperation;
+import org.roller.config.RollerConfig;
+import org.roller.model.IndexManager;
+import org.roller.model.Roller;
+import org.roller.model.UserManager;
+import org.roller.model.WeblogManager;
+import org.roller.pojos.WeblogCategoryData;
+import org.roller.pojos.WeblogEntryComparator;
+import org.roller.pojos.WeblogEntryData;
+import org.roller.pojos.WebsiteData;
+import org.roller.presentation.RollerContext;
+import org.roller.presentation.RollerRequest;
+import org.roller.util.DateUtil;
+import org.roller.util.StringUtils;
+
+
+
+/**
+ * This servlet retrieves (and displays) search results.
+ *
+ * @web.servlet name="SearchServlet" load-on-startup="5"
+ * @web.servlet-init-param name="properties" value="/WEB-INF/velocity.properties"
+ * @web.servlet-mapping url-pattern="/search/*"
+ */
+public class SearchServlet extends BasePageServlet
+{
+    //~ Static fields/initializers =============================================
+    static final long serialVersionUID = -2150090108300585670L;
+
+    private static Log mLogger =
+        LogFactory.getFactory().getInstance(SearchServlet.class);
+    
+    /* How many results to display */
+    private static int LIMIT = 10;
+    
+    /* Where to start fetching results */
+    private static int OFFSET = 0;
+    
+    /* is searching enabled? */
+    private boolean searchEnabled = true;
+    
+
+    //~ Methods ================================================================
+
+    public Template handleRequest(HttpServletRequest request,
+                        HttpServletResponse response, Context ctx) throws Exception
+    {
+        // Note: Removed request character encoding here; was too late; it is now set uniformly in CharEncodingFilter.
+        // See ROL-760.
+
+        String enabled = RollerConfig.getProperty("search.enabled");
+        if("false".equalsIgnoreCase(enabled))
+            this.searchEnabled = false;
+        
+        if(! this.searchEnabled) {
+            Template outty = null;
+            Exception pageException = null;
+            try {
+                ContextLoader.setupContext(
+                        ctx, RollerRequest.getRollerRequest(request), response );
+                outty = getTemplate( "searchdisabled.vm", "UTF-8" );
+            } 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);
+            }
+            return outty;
+        }
+
+        ctx.put("term", "");
+        ctx.put("hits", new Integer(0));
+        ctx.put("searchResults", new TreeMap());          
+        // to enable better unit testing
+        request.setAttribute("zzz_VelocityContext_zzz", ctx);
+        
+        mLogger.debug("q = "+request.getParameter("q"));
+        
+        // do no work if query term is empty
+        if (StringUtils.isEmpty(request.getParameter("q")))
+        {  
+            return generalSearchResults(request, response, ctx);        
+        }
+
+        boolean userSpecificSearch = checkForUser(request);
+        try
+        {
+            RollerRequest rreq = getRollerRequest(request, response);
+            
+            SearchOperation search = 
+                new SearchOperation(rreq.getRoller().getIndexManager());
+            search.setTerm(request.getParameter("q"));
+            ctx.put("term", request.getParameter("q"));
+
+            WebsiteData website = null;
+            if (userSpecificSearch)
+            {    
+                website = rreq.getWebsite();
+                search.setUsername(rreq.getUser().getUserName());
+                ctx.put("username", rreq.getUser().getUserName());
+            }
+            
+            if (StringUtils.isNotEmpty(request.getParameter("c")))
+            {
+                search.setCategory(request.getParameter("c"));
+            }
+
+            // execute search
+            executeSearch(rreq.getRoller(), search);
+
+            Map searchResults = new TreeMap();
+            if (search.getResultsCount() == -1)
+            {
+                // this means there has been a parsing (or IO) error
+                //ctx.put("errorMessage", search.getParseError());
+                ctx.put("errorMessage", "There was a problem with your search.");
+            }
+            else
+            {    
+                // Convert the Hits into WeblogEntryData instances.
+                Hits hits = search.getResults();
+                searchResults = convertHitsToEntries(rreq, website, hits);
+                ctx.put("offset", request.getAttribute("offset"));
+                ctx.put("limit", request.getAttribute("limit"));
+                
+                if (request.getAttribute("categories") != null)
+                {
+                    Set cats = (Set)request.getAttribute("categories");
+                    if (cats.size() > 0)
+                    {
+                        ctx.put("categories", cats);
+                    }
+                }
+            }
+            ctx.put("searchResults", searchResults);
+            ctx.put("hits", new Integer(search.getResultsCount()));      
+        }
+        catch (Exception e)
+        {
+            mLogger.error("EXCEPTION: in SearchServlet", e);
+            request.setAttribute("DisplayException", e);
+            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+        }        
+
+        if (userSpecificSearch)   
+        {    
+            return super.handleRequest(request, response, ctx);
+        }
+
+        return generalSearchResults(request, response, ctx);
+    }
+
+    /**
+     * execute search
+     * @param search
+     */
+    private void executeSearch(Roller roller, SearchOperation search) 
+    throws RollerException
+    {
+        IndexManager indexMgr = roller.getIndexManager();
+        indexMgr.executeIndexOperationNow(search);
+        if (mLogger.isDebugEnabled())
+        {
+            mLogger.debug("numresults = " + search.getResultsCount());
+        }
+    }
+
+    /**
+     * Iterate over Hits and build sets of WeblogEntryData
+     * objects, placed into Date buckets (in reverse order).
+     * 
+     * @param rreq
+     * @param website
+     * @param hits
+     * @throws RollerException
+     * @throws IOException
+     */
+    private TreeMap convertHitsToEntries(
+        RollerRequest rreq, WebsiteData website, Hits hits) 
+        throws RollerException, IOException
+    {
+        // determine offset (starting point)
+        int offset = useOffset(rreq.getRequest());
+        if (offset >= hits.length()) offset = OFFSET;
+        rreq.getRequest().setAttribute("offset", new Integer(offset));
+        
+        // determine limit (number of results to display)
+        int limit = useLimit(rreq.getRequest());
+        rreq.getRequest().setAttribute("limit", new Integer(limit));
+        if (offset + limit > hits.length()) limit = hits.length()-offset;
+        
+        boolean userSpecificSearch = checkForUser(rreq.getRequest());
+        TreeMap searchResults = new TreeMap(new ReverseComparator());
+        TreeSet categories = new TreeSet();
+        UserManager userMgr = 
+            RollerContext.getRoller(rreq.getRequest()).getUserManager();
+        WeblogManager weblogMgr =
+            RollerContext.getRoller(rreq.getRequest()).getWeblogManager();
+        WeblogEntryData entry;
+        Document doc = null;
+        String username = null;
+        for (int i = offset; i < offset+limit; i++)
+        {
+            entry = null; // reset for each iteration
+            
+            doc = hits.doc(i);
+            username =
+                doc.getField(FieldConstants.USERNAME).stringValue();
+            
+            if (userSpecificSearch && website != null) 
+            {
+                // "wrong user" results have been reported
+                if (username.equals(rreq.getUser().getUserName()))
+                {    
+                    //entry = buildSearchEntry(website, doc);
+                    
+                    // get real entry for display on user's site
+                    entry = weblogMgr.retrieveWeblogEntry(
+                        doc.getField(FieldConstants.ID).stringValue() );
+                }
+            }
+            else
+            {
+                website = userMgr.getWebsite(username);
+                // if user is not enabled, website will be null
+                if (website != null)
+                {
+                    entry = buildSearchEntry(website, doc);
+                    if (doc.getField(FieldConstants.CATEGORY) != null)
+                    {
+                        categories.add(
+                            doc.getField(FieldConstants.CATEGORY)
+                                .stringValue());
+                    }
+                }
+            }
+            
+            // maybe null if search result returned inactive user
+            // or entry's user is not the requested user.
+            if (entry != null)
+            {    
+                addToSearchResults(searchResults, entry);
+            }
+        }
+        rreq.getRequest().setAttribute("categories", categories);
+        return searchResults;
+    }
+
+    /**
+     * @param request
+     * @return
+     */
+    private int useOffset(HttpServletRequest request)
+    {
+        int offset = OFFSET;
+        if (request.getParameter("o") != null)
+        {
+            try
+            {
+                offset = Integer.valueOf(request.getParameter("o")).intValue();
+            }
+            catch (NumberFormatException e)
+            {
+                // Not a valid Integer
+            }
+        }
+        return offset;
+    }
+
+    /**
+     * @param request
+     * @return
+     */
+    private int useLimit(HttpServletRequest request)
+    {
+        int limit = LIMIT;
+        if (request.getParameter("n") != null)
+        {
+            try
+            {
+                limit = Integer.valueOf(request.getParameter("n")).intValue();
+            }
+            catch (NumberFormatException e)
+            {
+                // Not a valid Integer
+            }
+        }
+        return limit;
+    }
+
+    /**
+     * @param request
+     * @param response
+     * @return
+     */
+    private RollerRequest getRollerRequest(HttpServletRequest request, HttpServletResponse response)
+    {
+        PageContext pageContext =
+            JspFactory.getDefaultFactory()
+                .getPageContext(this, request, response, "", true, 8192, true);
+        
+        // Needed to init request attributes, etc.
+        return RollerRequest.getRollerRequest(pageContext);
+    }
+
+    /**
+     * @param searchResults
+     * @param entry
+     */
+    private void addToSearchResults(TreeMap searchResults, WeblogEntryData entry)
+    {
+        // convert entry's each date to midnight (00m 00h 00s)
+        Date midnight = DateUtil.getStartOfDay( entry.getPubTime() );
+        
+        // ensure we do not get duplicates from Lucene by
+        // using a Set Collection.  Entries sorted by pubTime.
+        TreeSet set = (TreeSet) searchResults.get(midnight);
+        if (set == null)
+        {
+            // date is not mapped yet, so we need a new Set
+            set = new TreeSet( new WeblogEntryComparator() );
+            searchResults.put(midnight, set);
+        }
+        set.add(entry);
+    }
+
+    /**
+     * @param website
+     * @param doc
+     * @return
+     */
+    private WeblogEntryData buildSearchEntry(WebsiteData website, Document doc)
+    {
+        String pubTimeStr = doc.getField(FieldConstants.PUBLISHED).stringValue();
+        WeblogEntryData entry = new WeblogEntryData();
+        entry.setWebsite(website);
+        entry.setTitle(doc.getField(FieldConstants.TITLE).stringValue());
+        entry.setAnchor(doc.getField(FieldConstants.ANCHOR).stringValue());
+        entry.setPubTime( DateUtil.parseTimestampFromFormats(pubTimeStr) );
+        entry.setText( doc.getField(FieldConstants.CONTENT_STORED).stringValue() );
+        if (doc.getField(FieldConstants.CATEGORY) != null)
+        {
+            WeblogCategoryData category = new WeblogCategoryData();
+            category.setWebsite(website);
+            category.setName(doc.getField(FieldConstants.CATEGORY).stringValue());
+            entry.setCategory( category );
+        }
+        return entry;
+    }
+
+    /**
+     * If this is not a user-specific search, we need to display the 
+     * "generic" search results list.
+     * 
+     * @param request
+     * @param response
+     * @param ctx
+     * @return
+     */
+    private Template generalSearchResults(HttpServletRequest request, HttpServletResponse response, Context ctx)
+    {
+        Template outty = null;
+        Exception pageException = null;
+        try
+        {
+            ContextLoader.setupContext( 
+                ctx, RollerRequest.getRollerRequest(request), response );
+            outty = getTemplate( "searchresults.vm", "UTF-8" );
+        }
+        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);
+        }
+        return outty;
+    }
+
+    /**
+     * Look in PathInfo so req.getRemoteUser() doesn't interfere.
+     * 
+     * @param request
+     * @return
+     */
+    private boolean checkForUser(HttpServletRequest request)
+    {
+        if (StringUtils.isNotEmpty(
+                request.getParameter(RollerRequest.USERNAME_KEY))) 
+        {
+            return true;
+        }
+        
+        String pathInfoStr = request.getPathInfo();
+        pathInfoStr = (pathInfoStr!=null) ? pathInfoStr : "";            
+        
+        String[] pathInfo = StringUtils.split(pathInfoStr,"/");            
+        if ( pathInfo.length > 0 )
+        {
+            return true; // is a user page
+        }
+        return false;
+    }
+
+}

Added: incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/ThemeResourceLoader.java
URL: http://svn.apache.org/viewcvs/incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/ThemeResourceLoader.java?rev=327589&view=auto
==============================================================================
--- incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/ThemeResourceLoader.java (added)
+++ incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/ThemeResourceLoader.java Fri Oct 21 14:27:36 2005
@@ -0,0 +1,130 @@
+/*
+ * ThemeResourceLoader.java
+ *
+ * Created on June 28, 2005, 12:25 PM
+ */
+
+package org.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.roller.RollerException;
+import org.roller.ThemeNotFoundException;
+import org.roller.model.RollerFactory;
+import org.roller.model.ThemeManager;
+import org.roller.pojos.Theme;
+import org.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/branches/roller_1.x/src/org/roller/presentation/velocity/VelocityPageInsert.java
URL: http://svn.apache.org/viewcvs/incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/VelocityPageInsert.java?rev=327589&view=auto
==============================================================================
--- incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/VelocityPageInsert.java (added)
+++ incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/VelocityPageInsert.java Fri Oct 21 14:27:36 2005
@@ -0,0 +1,21 @@
+package org.roller.presentation.velocity;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.velocity.VelocityContext;
+import org.roller.pojos.WeblogTemplate;
+
+/**
+ * A web page insert is a big of HTML that is inserted into a page into 
+ * any page that is rendered by the Roller Page Servlet and CommentServlet.
+ *
+ * @author David M Johnson
+ */
+public interface VelocityPageInsert 
+{
+    public void display(
+        WeblogTemplate page,
+        VelocityContext context,
+        HttpServletRequest request, 
+        HttpServletResponse response);
+}

Added: incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/WebappResourceLoader.java
URL: http://svn.apache.org/viewcvs/incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/WebappResourceLoader.java?rev=327589&view=auto
==============================================================================
--- incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/WebappResourceLoader.java (added)
+++ incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/WebappResourceLoader.java Fri Oct 21 14:27:36 2005
@@ -0,0 +1,120 @@
+package org.roller.presentation.velocity;
+
+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.roller.presentation.RollerContext;
+
+import java.io.InputStream;
+
+import javax.servlet.ServletContext;
+
+/**
+ * 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/branches/roller_1.x/src/org/roller/presentation/velocity/export_atom.vm
URL: http://svn.apache.org/viewcvs/incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/export_atom.vm?rev=327589&view=auto
==============================================================================
--- incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/export_atom.vm (added)
+++ incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/export_atom.vm Fri Oct 21 14:27:36 2005
@@ -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/branches/roller_1.x/src/org/roller/presentation/velocity/export_rss.vm
URL: http://svn.apache.org/viewcvs/incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/export_rss.vm?rev=327589&view=auto
==============================================================================
--- incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/export_rss.vm (added)
+++ incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/export_rss.vm Fri Oct 21 14:27:36 2005
@@ -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/branches/roller_1.x/src/org/roller/presentation/velocity/package.html
URL: http://svn.apache.org/viewcvs/incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/package.html?rev=327589&view=auto
==============================================================================
--- incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/package.html (added)
+++ incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/package.html Fri Oct 21 14:27:36 2005
@@ -0,0 +1,10 @@
+<!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/branches/roller_1.x/src/org/roller/presentation/velocity/roller-velocity.png
URL: http://svn.apache.org/viewcvs/incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/roller-velocity.png?rev=327589&view=auto
==============================================================================
Binary file - no diff available.

Propchange: incubator/roller/branches/roller_1.x/src/org/roller/presentation/velocity/roller-velocity.png
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream