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/06/09 05:19:20 UTC

svn commit: r189695 [32/67] - in /incubator/roller/trunk: ./ 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/src/org/roller/presentation/velocity/ contrib/plugins/src/org/roller/presentation/velocity/plugins/ contrib/plugins/src/org/roller/presentation/velocity/plugins/acronyms/ contrib/plugins/src/org/roller/presentation/velocity/plugins/bookmarks/ contrib/plugins/src/org/roller/presentation/velocity/plugins/email/ contrib/plugins/src/org/roller/presentation/velocity/plugins/jspwiki/ contrib/plugins/src/org/roller/presentation/velocity/plugins/radeox/ contrib/plugins/src/org/roller/presentation/velocity/plugins/readmore/ contrib/plugins/src/org/roller/presentation/velocity/plugins/smileys/ contrib/plugins/src/org/roller/presentation/velocity/plugins/textile/ contrib/plugins/src/org/roller/presentation/velocity/plugins/topictag/ custom/ custom/src/ custom/web/ docs/ docs/images/ docs/installguide/ docs/installguide/old/ docs/userguide/ docs/userguide/images/ docs/userguide/old/ metadata/ metadata/database/ metadata/database/hibernate/ metadata/xdoclet/ nbproject/ personal/ personal/eclipse/ personal/testing/ sandbox/ sandbox/planetroller/ sandbox/planetroller/metadata/ sandbox/planetroller/metadata/database/ sandbox/planetroller/src/ sandbox/planetroller/src/org/ sandbox/planetroller/src/org/roller/ sandbox/planetroller/src/org/roller/tools/ sandbox/planetroller/src/org/roller/tools/planet/ sandbox/planetroller/templates/ sandbox/planetroller/test/ sandbox/planetroller/test/org/ sandbox/planetroller/test/org/roller/ sandbox/planetroller/test/org/roller/model/ sandbox/planetroller/test/org/roller/tools/ sandbox/planetroller/test/org/roller/tools/planet/ sandbox/planetroller/testdata/ sandbox/planetroller/testdata/cache/ sandbox/planetroller/testdata/output/ sandbox/standalone/ sandbox/standalone/jspwiki/ sandbox/standalone/jspwiki/default/ sandbox/standalone/jspwiki/default/images/ sandbox/standalone/lib/ sandbox/standalone/src/ sandbox/standalone/src/org/ sandbox/standalone/src/org/roller/ sandbox/standalone/src/org/roller/jspwiki/ sandbox/standalone/src/org/roller/tomcat/ sandbox/standalone/src/org/roller/util/ sandbox/standalone/tests/ sandbox/standalone/tests/org/ sandbox/standalone/tests/org/roller/ sandbox/standalone/tests/org/roller/util/ sandbox/standalone/tomcat/ src/ src/org/ src/org/roller/ src/org/roller/business/ src/org/roller/business/hibernate/ src/org/roller/business/search/ src/org/roller/business/search/operations/ src/org/roller/business/utils/ src/org/roller/config/ src/org/roller/config/runtime/ src/org/roller/model/ src/org/roller/pojos/ src/org/roller/presentation/ src/org/roller/presentation/atomapi/ src/org/roller/presentation/bookmarks/ src/org/roller/presentation/bookmarks/actions/ src/org/roller/presentation/bookmarks/formbeans/ src/org/roller/presentation/bookmarks/tags/ src/org/roller/presentation/filters/ src/org/roller/presentation/forms/ src/org/roller/presentation/newsfeeds/ src/org/roller/presentation/pagecache/ src/org/roller/presentation/pagecache/rollercache/ src/org/roller/presentation/pings/ src/org/roller/presentation/planet/ src/org/roller/presentation/tags/ src/org/roller/presentation/tags/calendar/ src/org/roller/presentation/tags/menu/ src/org/roller/presentation/util/ src/org/roller/presentation/velocity/ src/org/roller/presentation/weblog/ src/org/roller/presentation/weblog/actions/ src/org/roller/presentation/weblog/formbeans/ src/org/roller/presentation/weblog/tags/ src/org/roller/presentation/website/ src/org/roller/presentation/website/actions/ src/org/roller/presentation/website/formbeans/ src/org/roller/presentation/website/tags/ src/org/roller/presentation/xmlrpc/ src/org/roller/util/ src/org/roller/util/rome/ tests/ tests/org/ tests/org/roller/ tests/org/roller/ant/ tests/org/roller/business/ tests/org/roller/presentation/ tests/org/roller/presentation/atomapi/ tests/org/roller/presentation/bookmarks/ tests/org/roller/presentation/filters/ tests/org/roller/presentation/velocity/ tests/org/roller/presentation/velocity/plugins/ tests/org/roller/presentation/velocity/plugins/smileys/ tests/org/roller/presentation/velocity/plugins/textile/ tests/org/roller/presentation/weblog/ tests/org/roller/presentation/xmlrpc/ tests/org/roller/util/ tests/org/roller/util/rome/ tools/ tools/buildtime/ tools/buildtime/ant-1.6.2/ tools/buildtime/findbugs/ tools/buildtime/findbugs/lib/ tools/buildtime/findbugs/plugin/ tools/buildtime/mockrunner-0.3/ tools/buildtime/mockrunner-0.3/lib/ tools/buildtime/mockrunner-0.35/ tools/buildtime/mockrunner-0.35/lib/ tools/buildtime/tomcat-4.1.24/ tools/buildtime/xdoclet-1.2/ tools/buildtime/xdoclet-1.2/lib/ tools/hibernate-2.1/ tools/hibernate-2.1/lib/ tools/lib/ tools/standard-1.0.3/ tools/standard-1.0.3/lib/ tools/standard-1.0.3/tld/ tools/struts-1.2.4/ tools/struts-1.2.4/lib/ web/ web/WEB-INF/ web/WEB-INF/classes/ web/WEB-INF/classes/flavors/ web/WEB-INF/classes/themes/ web/bookmarks/ web/editor/ web/editor/images/ web/images/ web/images/editor/ web/images/midas/ web/images/preview/ web/images/smileys/ web/planet/ web/tags/ web/templates/ web/theme/ web/theme/images/ web/theme/lavender/ web/theme/scripts/ web/theme/scripts/classes/ web/themes/ web/themes/basic/ web/themes/berkley/ web/themes/berkley/images/ web/themes/brushedmetal/ web/themes/brushedmetal/images/ web/themes/cheb/ web/themes/cheb/images/ web/themes/cheb/scripts/ web/themes/clean/ web/themes/currency-i18n/ web/themes/currency-i18n/images/ web/themes/currency/ web/themes/currency/images/ web/themes/grey2/ web/themes/moonshine/ web/themes/movablemanila/ web/themes/movablemanila/images/ web/themes/pacifica/ web/themes/robot/ web/themes/rolling/ web/themes/rolling/images/ web/themes/sotto/ web/themes/sotto/images/ web/themes/sotto/styles/ web/themes/sunsets/ web/themes/sunsets/images/ web/themes/sunsets/scripts/ web/themes/sunsets/styles/ web/themes/werner/ web/themes/x2/ web/themes/x2/images/ web/themes/x2/scripts/ web/themes/x2/styles/ web/weblog/ web/website/

Added: incubator/roller/trunk/src/org/roller/presentation/planet/PlanetConfigAction.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/planet/PlanetConfigAction.java?rev=189695&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/presentation/planet/PlanetConfigAction.java (added)
+++ incubator/roller/trunk/src/org/roller/presentation/planet/PlanetConfigAction.java Wed Jun  8 20:18:46 2005
@@ -0,0 +1,254 @@
+/*
+ * Copyright 2005 Sun Microsystems, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.roller.presentation.planet;
+
+import java.io.File;
+import java.io.IOException;
+
+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.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.action.ActionMessage;
+import org.apache.struts.action.ActionMessages;
+import org.apache.struts.actions.DispatchAction;
+import org.roller.presentation.forms.PlanetConfigForm;
+import org.roller.config.RollerRuntimeConfig;
+import org.roller.model.PlanetManager;
+import org.roller.model.Roller;
+import org.roller.pojos.PlanetConfigData;
+import org.roller.pojos.PlanetGroupData;
+import org.roller.presentation.RollerRequest;
+import org.roller.presentation.planet.RefreshEntriesTask;
+import org.roller.presentation.planet.SyncWebsitesTask;
+
+/////////////////////////////////////////////////////////////////////////////
+/**
+ * Allows configuration of Planet Roller.
+ * 
+ * @struts.action name="planetConfigForm" path="/admin/planetConfig"
+ *                scope="request" parameter="method"
+ * 
+ * @struts.action-forward name="planetConfig.page" 
+ *                        path="/planet/PlanetConfig.jsp"
+ */
+public final class PlanetConfigAction extends DispatchAction
+{
+    private static Log logger = 
+        LogFactory.getFactory().getInstance(PlanetConfigAction.class);
+
+    /** Populate config form and forward to config page */
+    public ActionForward getConfig(ActionMapping mapping,
+            ActionForm actionForm, HttpServletRequest request,
+            HttpServletResponse response) throws IOException, ServletException
+    {
+        ActionForward forward = mapping.findForward("planetConfig.page");
+        try
+        {
+            RollerRequest rreq = RollerRequest.getRollerRequest(request);
+            if (rreq.isUserAuthorizedToEdit())
+            {
+                Roller roller = rreq.getRoller();
+                PlanetManager planet = roller.getPlanetManager();
+                PlanetConfigData config = planet.getConfiguration();
+                PlanetConfigForm form = (PlanetConfigForm)actionForm;
+                if (config != null)
+                {
+                    form.copyFrom(config, request.getLocale());
+                }
+                else 
+                {
+                    form.setTitle("Planet Roller");
+                    form.setAdminEmail(RollerRuntimeConfig.getProperty("site.adminemail"));
+                    form.setSiteUrl(RollerRuntimeConfig.getProperty("site.absoluteurl"));
+                    form.setCacheDir("/tmp");
+                }
+            }
+            else
+            {
+                forward = mapping.findForward("access-denied");
+            }
+        }
+        catch (Exception e)
+        {
+            request.getSession().getServletContext().log("ERROR", e);
+            throw new ServletException(e);
+        }
+        return forward;
+    }
+
+    /** Save posted config form data */
+    public ActionForward saveConfig(ActionMapping mapping,
+            ActionForm actionForm, HttpServletRequest request,
+            HttpServletResponse response) throws IOException, ServletException
+    {
+        ActionForward forward = mapping.findForward("planetConfig.page");
+        try
+        {
+            RollerRequest rreq = RollerRequest.getRollerRequest(request);
+            if (rreq.isUserAuthorizedToEdit())
+            {
+                Roller roller = rreq.getRoller();
+                PlanetManager planet = roller.getPlanetManager();
+                PlanetConfigData config = planet.getConfiguration();
+                if (config == null)
+                {
+                    config = new PlanetConfigData();
+                }
+                PlanetConfigForm form = (PlanetConfigForm) actionForm;
+                ActionErrors errors = validate(form);
+                if (errors.isEmpty())
+                {
+                    form.copyTo(config, request.getLocale());
+                    planet.saveConfiguration(config);
+                    if (planet.getGroup("external") == null) 
+                    {
+                        PlanetGroupData group = new PlanetGroupData();
+                        group.setHandle("external");
+                        group.setTitle("external");
+                        planet.saveGroup(group);
+                    }
+                    roller.commit();
+                    ActionMessages messages = new ActionMessages();
+                    messages.add(null, new ActionMessage("planetConfig.success.saved"));
+                    saveMessages(request, messages);
+                }                
+                else
+                {
+                    saveErrors(request, errors);
+                }
+            }
+            else
+            {
+                forward = mapping.findForward("access-denied");
+            }
+        }
+        catch (Exception e)
+        {
+            request.getSession().getServletContext().log("ERROR", e);
+            throw new ServletException(e);
+        }
+        return forward;
+    }
+
+    /** Refresh entries in backgrounded thread (for testing) */
+    public ActionForward refreshEntries(ActionMapping mapping,
+            ActionForm actionForm, HttpServletRequest request,
+            HttpServletResponse response) throws IOException, ServletException
+    {
+        ActionForward forward = mapping.findForward("planetConfig.page");
+        try
+        {
+            RollerRequest rreq = RollerRequest.getRollerRequest(request);
+            if (rreq.isUserAuthorizedToEdit())
+            {
+                Roller roller = rreq.getRoller();
+                RefreshEntriesTask task = new RefreshEntriesTask();
+                task.init(roller, "dummy");
+                roller.getThreadManager().executeInBackground(task);
+                
+                ActionMessages messages = new ActionMessages();
+                messages.add(null, 
+                        new ActionMessage("planetConfig.success.refreshed"));
+                saveMessages(request, messages);
+            }
+            else
+            {
+                forward = mapping.findForward("access-denied");
+            }
+        }
+        catch (Exception e)
+        {
+            request.getSession().getServletContext().log("ERROR", e);
+            throw new ServletException(e);
+        }
+        return forward;
+    }
+
+    /** Sync websites in backgrounded thread (for testing) */
+    public ActionForward syncWebsites(ActionMapping mapping,
+            ActionForm actionForm, HttpServletRequest request,
+            HttpServletResponse response) throws IOException, ServletException
+    {
+        ActionForward forward = mapping.findForward("planetConfig.page");
+        try
+        {
+            RollerRequest rreq = RollerRequest.getRollerRequest(request);
+            if (rreq.isUserAuthorizedToEdit())
+            {
+                Roller roller = (Roller)rreq.getRoller();
+                SyncWebsitesTask task = new SyncWebsitesTask();
+                task.init(roller, "dummy");
+                roller.getThreadManager().executeInBackground(task);
+                ActionMessages messages = new ActionMessages();
+                messages.add(null, 
+                        new ActionMessage("planetConfig.success.synced"));
+                saveMessages(request, messages);
+            }
+            else
+            {
+                forward = mapping.findForward("access-denied");
+            }
+        }
+        catch (Exception e)
+        {
+            request.getSession().getServletContext().log("ERROR", e);
+            throw new ServletException(e);
+        }
+        return forward;
+    }
+    
+    /** Validate config form, returns empty collection if all OK */
+    public ActionErrors validate(PlanetConfigForm form)
+    {
+        ActionErrors errors = new ActionErrors();
+        if (form.getCacheDir()==null || form.getCacheDir().trim().length()==0)
+        {
+            errors.add(null, new ActionError("planetConfig.error.feedUrl"));
+        }
+        else
+        {
+            File file = new File(form.getCacheDir());
+            if (!file.isDirectory())
+            {
+                errors.add(null, new ActionError(
+                        "planetConfig.error.cacheDirNotFound"));
+            }
+            if (!file.canWrite())
+            {
+                errors.add(null, new ActionError(
+                        "planetConfig.error.cacheDirNotWritable"));
+            }
+        }
+        if (form.getProxyHost()!=null && form.getProxyHost().trim().length()>0)
+        {
+            if (form.getProxyPort()<1)
+            {
+                errors.add(null, new ActionError(
+                        "planetConfig.error.badProxyPort"));
+            }
+        }
+        return errors;
+    }
+}
+

Added: incubator/roller/trunk/src/org/roller/presentation/planet/PlanetGroupsAction.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/planet/PlanetGroupsAction.java?rev=189695&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/presentation/planet/PlanetGroupsAction.java (added)
+++ incubator/roller/trunk/src/org/roller/presentation/planet/PlanetGroupsAction.java Wed Jun  8 20:18:46 2005
@@ -0,0 +1,303 @@
+/*
+ * Copyright 2005 Sun Microsystems, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.roller.presentation.planet;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.action.ActionMessage;
+import org.apache.struts.action.ActionMessages;
+import org.apache.struts.actions.DispatchAction;
+import org.roller.RollerException;
+import org.roller.model.PlanetManager;
+import org.roller.model.Roller;
+import org.roller.pojos.PlanetGroupData;
+import org.roller.presentation.BasePageModel;
+import org.roller.presentation.RollerRequest;
+import org.roller.presentation.forms.PlanetGroupForm;
+
+
+/////////////////////////////////////////////////////////////////////////////
+/**
+ * Add, remove, and view user defined groups.
+ * 
+ * @struts.action name="planetGroupForm" path="/admin/planetGroups"
+ *                scope="request" parameter="method"
+ * 
+ * @struts.action-forward name="planetGroups.page" 
+ *                        path="/planet/PlanetGroups.jsp"
+ */
+public final class PlanetGroupsAction extends DispatchAction
+{
+    private static Log logger = LogFactory.getFactory().getInstance(
+            PlanetGroupsAction.class);
+
+    /** Populate page model and forward to subscription page */
+    public ActionForward getGroups(ActionMapping mapping,
+            ActionForm actionForm, HttpServletRequest request,
+            HttpServletResponse response) throws IOException, ServletException
+    {
+        ActionForward forward = mapping.findForward("planetGroups.page");
+        try
+        {
+            RollerRequest rreq = RollerRequest.getRollerRequest(request);
+            if (rreq.isUserAuthorizedToEdit())
+            {
+                Roller roller = rreq.getRoller();
+                PlanetManager planet = roller.getPlanetManager();
+                PlanetGroupForm form = (PlanetGroupForm)actionForm;
+                if (request.getParameter("groupHandle") != null)
+                {
+                    String feedUrl = request.getParameter("groupHandle");
+                    PlanetGroupData group = planet.getGroup(feedUrl);
+                    form.copyFrom(group, request.getLocale());
+                }
+                else 
+                {
+                    form.doReset(mapping, request);
+                }
+                request.setAttribute("model", 
+                    new GroupsPageModel(request, response, mapping));
+            }
+            else
+            {
+                forward = mapping.findForward("access-denied");
+            }
+        }
+        catch (Exception e)
+        {
+            request.getSession().getServletContext().log("ERROR", e);
+            throw new ServletException(e);
+        }
+        return forward;
+    }
+
+    /** Cancel editing, reset form */
+    public ActionForward cancelEditing(ActionMapping mapping,
+            ActionForm actionForm, HttpServletRequest request,
+            HttpServletResponse response) throws IOException, ServletException
+    {
+        ActionForward forward = mapping.findForward("planetGroups.page");
+        try
+        {
+            RollerRequest rreq = RollerRequest.getRollerRequest(request);
+            if (rreq.isUserAuthorizedToEdit())
+            {
+                Roller roller = rreq.getRoller();
+                PlanetManager planet = roller.getPlanetManager();
+                PlanetGroupForm form = (PlanetGroupForm)actionForm;
+                
+                form.doReset(mapping, request);
+                
+                request.setAttribute("model", 
+                    new GroupsPageModel(request, response, mapping));
+            }
+            else
+            {
+                forward = mapping.findForward("access-denied");
+            }
+        }
+        catch (Exception e)
+        {
+            request.getSession().getServletContext().log("ERROR", e);
+            throw new ServletException(e);
+        }
+        return forward;
+    }
+    
+    /** Delete subscription, reset form  */
+    public ActionForward deleteGroup(ActionMapping mapping,
+            ActionForm actionForm, HttpServletRequest request,
+            HttpServletResponse response) throws IOException, ServletException 
+    {
+        ActionForward forward = mapping.findForward("planetGroups.page");
+        try
+        {
+            RollerRequest rreq = RollerRequest.getRollerRequest(request);
+            if (rreq.isUserAuthorizedToEdit())
+            {
+                Roller roller = rreq.getRoller();
+                PlanetManager planet = roller.getPlanetManager();
+                PlanetGroupForm form = (PlanetGroupForm)actionForm;
+                if (form.getHandle() != null)
+                {
+                    PlanetGroupData group = planet.getGroup(form.getHandle());
+                    planet.deleteGroup(group);
+                    roller.commit();
+                    roller.release();
+                    
+                    roller.begin();
+                    form.doReset(mapping, request);
+                    
+                    request.setAttribute("model", 
+                        new GroupsPageModel(request, response, mapping));
+                    
+                    ActionMessages messages = new ActionMessages();
+                    messages.add(null, 
+                        new ActionMessage("planetSubscription.success.deleted"));
+                    saveMessages(request, messages);
+                }
+            }
+            else
+            {
+                forward = mapping.findForward("access-denied");
+            }
+        }
+        catch (Exception e)
+        {
+            ActionErrors errors = new ActionErrors();
+            errors.add(null, new ActionError("planetGroup.error.deleting"));
+            saveErrors(request, errors);       
+        }
+        return forward;
+    }
+
+    /** Save subscription, add to "external" group */
+    public ActionForward saveGroup(ActionMapping mapping,
+            ActionForm actionForm, HttpServletRequest request,
+            HttpServletResponse response) throws IOException, ServletException
+    {
+        ActionForward forward = mapping.findForward("planetGroups.page");
+        try
+        {
+            RollerRequest rreq = RollerRequest.getRollerRequest(request);
+            if (rreq.isUserAuthorizedToEdit())
+            {
+                PlanetGroupForm form = (PlanetGroupForm)actionForm;
+                Roller roller = rreq.getRoller();
+                PlanetManager planet = roller.getPlanetManager();
+                ActionErrors errors = validate(planet, form);
+                if (errors.isEmpty())
+                {
+                    PlanetGroupData group = null;
+                    if (form.getId() == null || form.getId().trim().length() == 0)
+                    {
+                        group = new PlanetGroupData();
+                    }
+                    else 
+                    {
+                        group = planet.getGroupById(form.getId());
+                    }                
+                    form.copyTo(group, request.getLocale());
+                    planet.saveGroup(group);           
+                    roller.commit();
+
+                    ActionMessages messages = new ActionMessages();
+                    messages.add(null, 
+                            new ActionMessage("planetGroups.success.saved"));
+                    saveMessages(request, messages);
+                    form.doReset(mapping, request);
+
+                    request.setAttribute("model", 
+                            new GroupsPageModel(request, response, mapping));
+                }
+                else
+                {
+                    saveErrors(request, errors);
+                }
+            }
+            else
+            {
+                forward = mapping.findForward("access-denied");
+            }
+        }
+        catch (RollerException e)
+        {
+            ActionErrors errors = new ActionErrors();
+            errors.add(null, new ActionError(
+              "planetSubscriptions.error.duringSave",e.getRootCauseMessage()));
+            saveErrors(request, errors);
+        }
+        return forward;
+    }
+    
+    /** Validate posted group */
+    private ActionErrors validate(
+            PlanetManager planet, PlanetGroupForm form)
+    {
+        ActionErrors errors = new ActionErrors();
+        if (form.getTitle()==null || form.getTitle().trim().length()==0)
+        {            
+            errors.add(null, new ActionError("planetGroups.error.title"));
+        }
+        if (form.getHandle()==null || form.getHandle().trim().length()==0)
+        {            
+            errors.add(null, new ActionError("planetGroups.error.handle"));
+        }
+        if (form.getHandle() != null && 
+        (form.getHandle().equals("all") || form.getHandle().equals("external")))
+        {
+           errors.add(null, new ActionError("planetGroups.error.nameReserved"));
+        }
+        return errors;
+    }
+
+    /** Page model */
+    public class GroupsPageModel extends BasePageModel
+    {
+        private List groups = new ArrayList();
+        private boolean unconfigured = false;
+        public GroupsPageModel(
+            HttpServletRequest request,
+            HttpServletResponse response,
+            ActionMapping mapping) throws RollerException
+        {
+            super(request, response, mapping);
+            RollerRequest rreq = RollerRequest.getRollerRequest(request);
+            Roller roller = rreq.getRoller();
+            PlanetManager planet = roller.getPlanetManager();            
+            PlanetGroupData externalGroup = planet.getGroup("external");
+            if (externalGroup != null) 
+            {
+                Iterator allgroups = planet.getGroups().iterator();
+                while (allgroups.hasNext()) 
+                {
+                    PlanetGroupData agroup = (PlanetGroupData)allgroups.next();
+                    if (    !agroup.getHandle().equals("external")
+                         && !agroup.getHandle().equals("all")) 
+                      {
+                          groups.add(agroup);
+                      }
+                }
+            }
+            else 
+            {
+                unconfigured = true;
+            }
+        }
+        public List getGroups()
+        {
+            return groups;
+        }
+        public boolean isUnconfigured()
+        {
+            return unconfigured;
+        }
+    }
+}

Added: incubator/roller/trunk/src/org/roller/presentation/planet/PlanetSubscriptionFormEx.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/planet/PlanetSubscriptionFormEx.java?rev=189695&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/presentation/planet/PlanetSubscriptionFormEx.java (added)
+++ incubator/roller/trunk/src/org/roller/presentation/planet/PlanetSubscriptionFormEx.java Wed Jun  8 20:18:46 2005
@@ -0,0 +1,28 @@
+package org.roller.presentation.planet;
+
+import java.util.Locale;
+import org.roller.pojos.PlanetSubscriptionData;
+import org.roller.presentation.forms.PlanetSubscriptionForm;
+
+/**
+ * @struts.form name="planetSubscriptionFormEx"
+ */
+public class PlanetSubscriptionFormEx 
+    extends    PlanetSubscriptionForm
+    implements java.io.Serializable
+{
+    private String groupHandle = null;
+    public PlanetSubscriptionFormEx() 
+    {
+        super();
+    }
+    public String getGroupHandle() 
+    {
+        return groupHandle;
+    }
+    public void setGroupHandle(String groupHandle) 
+    {
+        this.groupHandle = groupHandle;
+    }
+}
+

Added: incubator/roller/trunk/src/org/roller/presentation/planet/PlanetSubscriptionsAction.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/planet/PlanetSubscriptionsAction.java?rev=189695&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/presentation/planet/PlanetSubscriptionsAction.java (added)
+++ incubator/roller/trunk/src/org/roller/presentation/planet/PlanetSubscriptionsAction.java Wed Jun  8 20:18:46 2005
@@ -0,0 +1,385 @@
+/*
+ * Copyright 2005 Sun Microsystems, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.roller.presentation.planet;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+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.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.action.ActionMessage;
+import org.apache.struts.action.ActionMessages;
+import org.apache.struts.actions.DispatchAction;
+import org.roller.RollerException;
+import org.roller.model.PlanetManager;
+import org.roller.model.Roller;
+import org.roller.pojos.PlanetConfigData;
+import org.roller.pojos.PlanetGroupData;
+import org.roller.pojos.PlanetSubscriptionData;
+import org.roller.presentation.BasePageModel;
+import org.roller.presentation.RollerRequest;
+import org.roller.util.Technorati;
+
+
+/////////////////////////////////////////////////////////////////////////////
+/**
+ * Add, remove, and view existing subscriptions in a group.
+ * If no group is specified via the groupHandle parameter, then uses "external".
+ * 
+ * @struts.action name="planetSubscriptionFormEx" path="/admin/planetSubscriptions"
+ *                scope="request" parameter="method"
+ * 
+ * @struts.action-forward name="planetSubscriptions.page" 
+ *                        path="/planet/PlanetSubscriptions.jsp"
+ */
+public final class PlanetSubscriptionsAction extends DispatchAction
+{
+    private static Log logger = LogFactory.getFactory().getInstance(
+            PlanetSubscriptionsAction.class);
+
+    /** Populate page model and forward to subscription page */
+    public ActionForward getSubscriptions(ActionMapping mapping,
+            ActionForm actionForm, HttpServletRequest request,
+            HttpServletResponse response) throws IOException, ServletException
+    {
+        ActionForward forward = mapping.findForward("planetSubscriptions.page");
+        try
+        {
+            RollerRequest rreq = RollerRequest.getRollerRequest(request);
+            if (rreq.isUserAuthorizedToEdit())
+            {
+                Roller roller = rreq.getRoller();
+                PlanetManager planet = roller.getPlanetManager();
+                PlanetSubscriptionFormEx form = (PlanetSubscriptionFormEx)actionForm;
+                if (request.getParameter("feedUrl") != null)
+                {
+                    String feedUrl = request.getParameter("feedUrl");
+                    PlanetSubscriptionData sub = 
+                            planet.getSubscription(feedUrl);
+                    form.copyFrom(sub, request.getLocale());
+                }
+                else 
+                {
+                    form.doReset(mapping, request);
+                }
+                
+                String groupHandle = request.getParameter("groupHandle");
+                groupHandle = (groupHandle == null) ? form.getGroupHandle() : groupHandle;
+                groupHandle = (groupHandle == null) ? "external" : groupHandle;
+                
+                PlanetGroupData targetGroup = planet.getGroup(groupHandle);
+                form.setGroupHandle(groupHandle);
+                request.setAttribute("model", 
+                    new SubscriptionsPageModel(
+                            targetGroup, request, response, mapping));
+            }
+            else
+            {
+                forward = mapping.findForward("access-denied");
+            }
+        }
+        catch (Exception e)
+        {
+            request.getSession().getServletContext().log("ERROR", e);
+            throw new ServletException(e);
+        }
+        return forward;
+    }
+
+    /** Cancel editing, reset form */
+    public ActionForward cancelEditing(ActionMapping mapping,
+            ActionForm actionForm, HttpServletRequest request,
+            HttpServletResponse response) throws IOException, ServletException
+    {
+        ActionForward forward = mapping.findForward("planetSubscriptions.page");
+        try
+        {
+            RollerRequest rreq = RollerRequest.getRollerRequest(request);
+            if (rreq.isUserAuthorizedToEdit())
+            {
+                Roller roller = rreq.getRoller();
+                PlanetManager planet = roller.getPlanetManager();
+                PlanetSubscriptionFormEx form = (PlanetSubscriptionFormEx)actionForm;
+                
+                form.doReset(mapping, request);
+                
+                String groupHandle = request.getParameter("groupHandle");
+                groupHandle = (groupHandle == null) ? form.getGroupHandle() : groupHandle;
+                groupHandle = (groupHandle == null) ? "external" : groupHandle;
+
+                PlanetGroupData targetGroup = planet.getGroup(groupHandle);
+                form.setGroupHandle(groupHandle);
+                request.setAttribute("model", 
+                    new SubscriptionsPageModel(
+                            targetGroup, request, response, mapping));
+            }
+            else
+            {
+                forward = mapping.findForward("access-denied");
+            }
+        }
+        catch (Exception e)
+        {
+            request.getSession().getServletContext().log("ERROR", e);
+            throw new ServletException(e);
+        }
+        return forward;
+    }
+    
+    /** Delete subscription, reset form  */
+    public ActionForward deleteSubscription(ActionMapping mapping,
+            ActionForm actionForm, HttpServletRequest request,
+            HttpServletResponse response) throws IOException, ServletException 
+    {
+        ActionForward forward = mapping.findForward("planetSubscriptions.page");
+        try
+        {
+            RollerRequest rreq = RollerRequest.getRollerRequest(request);
+            if (rreq.isUserAuthorizedToEdit())
+            {
+                Roller roller = rreq.getRoller();
+                PlanetManager planet = roller.getPlanetManager();
+                PlanetSubscriptionFormEx form = (PlanetSubscriptionFormEx)actionForm;
+                if (form.getId() != null)
+                {
+                    PlanetSubscriptionData sub = 
+                            planet.getSubscriptionById(form.getId());                   
+
+                    String groupHandle = request.getParameter("groupHandle");
+                    groupHandle = (groupHandle == null) ? form.getGroupHandle() : groupHandle;
+                    groupHandle = (groupHandle == null) ? "external" : groupHandle;
+
+                    PlanetGroupData targetGroup = planet.getGroup(groupHandle);
+                    
+                    targetGroup.removeSubscription(sub);
+                    planet.deleteSubscription(sub);
+                    roller.commit();
+                    roller.release();
+                    
+                    roller.begin();
+                    form.doReset(mapping, request);
+
+                    form.setGroupHandle(groupHandle);
+                    request.setAttribute("model", 
+                        new SubscriptionsPageModel(
+                                targetGroup, request, response, mapping));
+                    
+                    ActionMessages messages = new ActionMessages();
+                    messages.add(null, 
+                        new ActionMessage("planetSubscription.success.deleted"));
+                    saveMessages(request, messages);
+                }
+            }
+            else
+            {
+                forward = mapping.findForward("access-denied");
+            }
+        }
+        catch (Exception e)
+        {
+            ActionErrors errors = new ActionErrors();
+            errors.add(null, new ActionError("planetSubscription.error.deleting"));
+            saveErrors(request, errors);
+        }
+        return forward;
+    }
+
+    /** Save subscription, add to "external" group */
+    public ActionForward saveSubscription(ActionMapping mapping,
+            ActionForm actionForm, HttpServletRequest request,
+            HttpServletResponse response) throws IOException, ServletException
+    {
+        ActionForward forward = mapping.findForward("planetSubscriptions.page");
+        try
+        {
+            RollerRequest rreq = RollerRequest.getRollerRequest(request);
+            Roller roller = rreq.getRoller();
+            PlanetManager planet = roller.getPlanetManager();
+            PlanetSubscriptionFormEx form = (PlanetSubscriptionFormEx)actionForm;
+            
+            String groupHandle = request.getParameter("groupHandle");
+            groupHandle = (groupHandle == null) ? form.getGroupHandle() : groupHandle;
+            groupHandle = (groupHandle == null) ? "external" : groupHandle;
+
+            PlanetGroupData targetGroup = planet.getGroup(groupHandle);
+
+            if (rreq.isUserAuthorizedToEdit())
+            {
+
+                PlanetSubscriptionData sub = null;
+                ActionErrors errors = validate(planet, form);
+                if (errors.isEmpty())
+                {
+                    if (form.getId() == null || form.getId().trim().length() == 0)
+                    {
+                        sub = new PlanetSubscriptionData();
+                        targetGroup.addSubscription(sub);
+                    }
+                    else 
+                    {
+                        sub = planet.getSubscriptionById(form.getId());
+                    }                
+                    form.copyTo(sub, request.getLocale());
+                    form.setGroupHandle(groupHandle);
+                    planet.saveSubscription(sub);
+                    planet.saveGroup(targetGroup);           
+                    roller.commit();
+
+                    ActionMessages messages = new ActionMessages();
+                    messages.add(null, 
+                            new ActionMessage("planetSubscription.success.saved"));
+                    saveMessages(request, messages);
+                    form.doReset(mapping, request);
+                }
+                else
+                {
+                    saveErrors(request, errors);
+                }
+            }
+            else
+            {
+                forward = mapping.findForward("access-denied");
+            }
+            request.setAttribute("model", 
+                new SubscriptionsPageModel(
+                        targetGroup, request, response, mapping));
+        }
+        catch (RollerException e)
+        {
+            ActionErrors errors = new ActionErrors();
+            errors.add(null, new ActionError(
+              "planetSubscriptions.error.duringSave",e.getRootCauseMessage()));
+            saveErrors(request, errors);
+        }
+        return forward;
+    }
+    
+    /** Validate posted subscription, fill in blanks via Technorati */
+    private ActionErrors validate(
+            PlanetManager planet, PlanetSubscriptionFormEx form)
+    {
+        String technoratiTitle = null;
+        String technoratiFeedUrl = null;
+        int inboundlinks = -1;
+        int inboundblogs = -1;
+        if (form.getSiteUrl()!=null && form.getSiteUrl().trim().length() > 0)
+        {
+            try 
+            {
+                PlanetConfigData config = planet.getConfiguration();
+                Technorati technorati = null;
+                if (config.getProxyHost()!=null && config.getProxyPort() > 0)
+                {
+                    technorati = new Technorati(
+                            config.getProxyHost(), config.getProxyPort());
+                }
+                else 
+                {
+                    technorati = new Technorati();
+                }
+                Technorati.Result result = 
+                        technorati.getBloginfo(form.getSiteUrl());
+                technoratiTitle = result.getWeblog().getName();
+                technoratiFeedUrl = result.getWeblog().getRssurl();
+                form.setInboundlinks(result.getWeblog().getInboundlinks());
+                form.setInboundblogs(result.getWeblog().getInboundblogs());
+            }
+            catch (Exception e)
+            {
+                logger.debug("Unable to contact Technorati", e);
+            }
+        }
+
+        ActionErrors errors = new ActionErrors();
+        if (form.getTitle()==null || form.getTitle().trim().length()==0)
+        {
+            if (technoratiTitle!=null && technoratiTitle.trim().length()>0)
+            {
+                form.setTitle(technoratiTitle);
+            }
+            else 
+            {
+                errors.add(null,
+                    new ActionError("planetSubscription.error.title"));
+            }
+        }
+        if (form.getFeedUrl()==null || form.getFeedUrl().trim().length()==0)
+        {
+            if (technoratiFeedUrl!=null && technoratiFeedUrl.trim().length()>0)
+            {
+                form.setFeedUrl(technoratiFeedUrl);
+            }
+            else 
+            {
+                errors.add(null,
+                    new ActionError("planetSubscription.error.feedUrl"));
+            }
+        }
+        if (form.getSiteUrl()==null || form.getSiteUrl().trim().length()==0)
+        {
+            errors.add(null,
+                new ActionError("planetSubscription.error.siteUrl"));
+        }
+        return errors;
+    }
+   
+    /** Page model, includes subscriptions in "external" group */
+    public class SubscriptionsPageModel extends BasePageModel
+    {
+        private List subscriptions = null;
+        private boolean unconfigured = false;
+        public SubscriptionsPageModel(
+                PlanetGroupData group,
+                HttpServletRequest request,
+                HttpServletResponse response,
+                ActionMapping mapping) throws RollerException
+        {
+            super(request, response, mapping);
+            RollerRequest rreq = RollerRequest.getRollerRequest(request);
+            Roller roller = rreq.getRoller();
+            PlanetManager planet = roller.getPlanetManager();
+            if (group != null) 
+            {
+                Set subsSet = group.getSubscriptions();
+                subscriptions = new ArrayList(subsSet);
+            }
+            else 
+            {
+                unconfigured = true;
+            }
+        }
+        public List getSubscriptions()
+        {
+            return subscriptions;
+        }
+        public boolean isUnconfigured()
+        {
+            return unconfigured;
+        }
+    }
+}
+

Added: incubator/roller/trunk/src/org/roller/presentation/planet/RefreshEntriesTask.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/planet/RefreshEntriesTask.java?rev=189695&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/presentation/planet/RefreshEntriesTask.java (added)
+++ incubator/roller/trunk/src/org/roller/presentation/planet/RefreshEntriesTask.java Wed Jun  8 20:18:46 2005
@@ -0,0 +1,50 @@
+package org.roller.presentation.planet;
+
+import java.util.TimerTask;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.roller.RollerException;
+import org.roller.model.Roller;
+import org.roller.model.RollerFactory;
+import org.roller.model.ScheduledTask;
+
+/**
+ * Run the Planet Roller refresh-entries method to fetch and parse newsfeeds.
+ * @author Dave Johnson
+ */
+public class RefreshEntriesTask extends TimerTask implements ScheduledTask
+{
+    private static Log logger = 
+        LogFactory.getFactory().getInstance(RefreshEntriesTask.class);
+    private Roller roller = null;
+    
+    /** Task may be run from the command line */
+    public static void main(String[] args) throws Exception
+    {
+        RollerFactory.setRoller(
+            "org.roller.business.hibernate.HibernateRollerImpl");
+        RefreshEntriesTask task = new RefreshEntriesTask();
+        task.init(RollerFactory.getRoller(), "dummy");
+        task.run();
+    }
+    public void init(Roller roller, String realPath) throws RollerException
+    {
+        this.roller = (Roller)roller;
+    }
+    public void run()
+    {
+        try
+        {
+            roller.begin();
+            roller.getPlanetManager().refreshEntries();
+            roller.commit();
+            roller.release();
+        }
+        catch (RollerException e)
+        {
+            logger.error("ERROR refreshing entries", e);
+        }
+    }
+}
+

Added: incubator/roller/trunk/src/org/roller/presentation/planet/SyncWebsitesTask.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/planet/SyncWebsitesTask.java?rev=189695&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/presentation/planet/SyncWebsitesTask.java (added)
+++ incubator/roller/trunk/src/org/roller/presentation/planet/SyncWebsitesTask.java Wed Jun  8 20:18:46 2005
@@ -0,0 +1,256 @@
+package org.roller.presentation.planet;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import java.util.TimerTask;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.roller.RollerException;
+import org.roller.model.PlanetManager;
+import org.roller.model.Roller;
+import org.roller.model.RollerFactory;
+import org.roller.model.ScheduledTask;
+import org.roller.model.UserManager;
+import org.roller.pojos.PlanetConfigData;
+import org.roller.pojos.PlanetGroupData;
+import org.roller.pojos.PlanetSubscriptionData;
+import org.roller.pojos.UserData;
+import org.roller.pojos.WebsiteData;
+import org.roller.util.Technorati;
+
+/**
+ * Ensure that every user is represented by a subscription in Planet Roller 
+ * database. Also "ranks" each subscription by populating Technorati inbound 
+ * blogs and links counts.
+ * @author Dave Johnson
+ */
+public class SyncWebsitesTask extends TimerTask implements ScheduledTask
+{
+    private static Log logger = 
+        LogFactory.getFactory().getInstance(SyncWebsitesTask.class);
+    private Roller roller = null;
+ 
+    /** Task may be run from the command line */
+    public static void main(String[] args) throws Exception 
+    {
+        RollerFactory.setRoller(
+            "org.roller.business.hibernate.HibernateRollerImpl");
+        SyncWebsitesTask task = new SyncWebsitesTask();
+        task.init(RollerFactory.getRoller(), "dummy");
+        task.run();
+    }
+    public void init(Roller roller, String realPath) throws RollerException
+    {
+        this.roller = roller;
+    }
+    public void run()
+    {
+        syncWebsites();
+        rankSubscriptions();
+    }
+    /** 
+     * Ensure there's a subscription in the "all" group for every Roller user.
+     */
+    private void syncWebsites()
+    {       
+        try
+        {
+            List liveUserFeeds = new ArrayList();            
+            String baseURL = 
+                roller.getConfigManager().getRollerConfig().getAbsoluteURL();
+            if (baseURL == null || baseURL.trim().length()==0)
+            {
+                logger.error("ERROR: cannot sync websites with Planet Roller - "
+                            +"absolute URL not specified in Roller Config");
+            }
+            else
+            {
+                roller.begin();
+                PlanetManager planet = roller.getPlanetManager();
+                UserManager userManager = roller.getUserManager();
+                PlanetGroupData group = planet.getGroup("all");
+                if (group == null)
+                {
+                    group = new PlanetGroupData();
+                    group.setHandle("all");
+                    group.setTitle("all");
+                    planet.saveGroup(group);
+                    roller.commit();
+                }
+                try 
+                {
+                    String baseFeedURL = baseURL + "/rss/";
+                    String baseSiteURL = baseURL + "/page/";
+                    Iterator users = roller.getUserManager().getUsers().iterator();
+                    while (users.hasNext())
+                    {
+                        UserData user = (UserData) users.next();
+                        
+                        StringBuffer sitesb = new StringBuffer();
+                        sitesb.append(baseSiteURL);
+                        sitesb.append(user.getUserName());
+                        String siteUrl = sitesb.toString();
+                        
+                        StringBuffer feedsb = new StringBuffer();
+                        feedsb.append(baseFeedURL);
+                        feedsb.append(user.getUserName());
+                        String feedUrl = feedsb.toString();
+                        
+                        liveUserFeeds.add(feedUrl);
+                        
+                        PlanetSubscriptionData sub = 
+                                planet.getSubscription(feedUrl);
+                        WebsiteData website = 
+                                userManager.getWebsite(user.getUserName());
+                        if (sub == null)
+                        {
+                            logger.info("ADDING feed: "+feedUrl);
+                            sub = new PlanetSubscriptionData();
+                            sub.setTitle(website.getName());
+                            sub.setFeedUrl(feedUrl);
+                            sub.setSiteUrl(siteUrl);
+                            sub.setAuthor(user.getUserName());
+                            planet.saveSubscription(sub);
+                            group.addSubscription(sub);
+                        }
+                        else
+                        {
+                            sub.setTitle(website.getName());
+                            sub.setAuthor(user.getUserName());
+                            planet.saveSubscription(sub);
+                        }
+                    }
+                    planet.saveGroup(group);
+                    roller.commit();
+                    roller.release();
+                    
+                    roller.begin();
+                    group = group = planet.getGroup("all");
+                    Iterator subs = group.getSubscriptions().iterator();
+                    while (subs.hasNext())
+                    {
+                        PlanetSubscriptionData sub = 
+                                (PlanetSubscriptionData)subs.next();
+                        if (!liveUserFeeds.contains(sub.getFeedUrl()))
+                        {
+                            logger.info("DELETING feed: "+sub.getFeedUrl());
+                            planet.deleteSubscription(sub);
+                        }
+                    }
+                    roller.commit();                   
+                }
+                finally
+                {
+                    roller.release();
+                }
+            }
+        }
+        catch (RollerException e)
+        {
+            logger.error("ERROR refreshing entries", e);
+        }
+    }
+    
+    /** 
+     * Loop through all subscriptions get get Technorati rankings for each 
+     */
+    private void rankSubscriptions()
+    {       
+        int count = 0;
+        int errorCount = 0;
+        try
+        {
+            roller.begin();
+            PlanetManager planet = roller.getPlanetManager();
+            PlanetConfigData config = planet.getConfiguration();
+            Technorati technorati = null;
+            if (config.getProxyHost()!=null && config.getProxyPort() != -1)
+            {
+                technorati = new Technorati(
+                        config.getProxyHost(), config.getProxyPort());
+            }
+            else 
+            {
+                technorati = new Technorati();
+            }                
+            UserManager userManager = roller.getUserManager();
+            try 
+            {
+                // Technorati API allows only 500 queries per-day
+                int limit = 500;
+                int userCount = planet.getSubscriptionCount();
+                int mod = (userCount / limit) + 1;
+                
+                Calendar cal = Calendar.getInstance();
+                cal.setTime(new Date());
+                int day = cal.get(Calendar.DAY_OF_YEAR);
+                
+                int start = (day % mod) * limit;
+                int end = start + limit;
+                end = end > userCount ? userCount : end; 
+                logger.info("Updating subscriptions ["+start+":"+end+"]");
+                
+                Iterator subs = planet.getAllSubscriptions();
+                while (subs.hasNext())
+                {
+                    PlanetSubscriptionData sub = 
+                            (PlanetSubscriptionData)subs.next();
+                    if (count >= start && count < end)
+                    {
+                        try
+                        {
+                            Technorati.Result result = 
+                                    technorati.getBloginfo(sub.getSiteUrl());
+                            if (result != null && result.getWeblog() != null)
+                            {
+                              sub.setInboundblogs(
+                                      result.getWeblog().getInboundblogs());
+                              sub.setInboundlinks(
+                                      result.getWeblog().getInboundlinks());
+                              logger.debug("Adding rank for "
+                                      +sub.getFeedUrl()+" ["+count+"|"
+                                      +sub.getInboundblogs()+"|"
+                                      +sub.getInboundlinks()+"]");
+                            }
+                            else 
+                            {
+                              logger.debug(
+                                "No ranking available for "
+                                      +sub.getFeedUrl()+" ["+count+"]");
+                              sub.setInboundlinks(0);
+                              sub.setInboundblogs(0);
+                            }
+                            planet.saveSubscription(sub);
+                        }
+                        catch (Exception e) 
+                        {
+                            logger.warn("WARN ranking subscription ["
+                                        + count + "]: " + e.getMessage());
+                            if (errorCount++ > 5)
+                            {
+                                logger.warn(
+                                    "   Stopping ranking, too many errors");
+                                break;
+                            }
+                        }
+                    }
+                    count++;
+                }
+                roller.commit();
+            }
+            finally
+            {
+                roller.release();
+            }
+        }
+        catch (Exception e)
+        {
+            logger.error("ERROR ranking subscriptions", e);
+        }
+    }
+}
+

Added: incubator/roller/trunk/src/org/roller/presentation/tags/DateTag.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/tags/DateTag.java?rev=189695&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/presentation/tags/DateTag.java (added)
+++ incubator/roller/trunk/src/org/roller/presentation/tags/DateTag.java Wed Jun  8 20:18:46 2005
@@ -0,0 +1,121 @@
+package org.roller.presentation.tags;
+
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.tagext.TagSupport;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.struts.Globals;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.util.RequestUtils;
+
+/**
+ * Struts-based date field tag that wraps Matt Kruze's JavaScript data chooser.
+ * @jsp.tag name="Date"
+ */
+public class DateTag extends TagSupport
+{
+    static final long serialVersionUID = 1485100916981692535L;
+    
+    // Unique key prefix keeps us from colliding with other tags and user data
+    public static final String KEY_PREFIX = "ZZZ_DATETAG_ZZZ";
+
+    private String property = null;
+    private String dateFormat = null;
+    private Boolean readOnly = Boolean.FALSE;
+
+    private static Log mLogger =
+        LogFactory.getFactory().getInstance(DateTag.class);
+
+    /**
+     * Renders date field by calling a JSP page.
+     */
+    public int doStartTag() throws JspException
+    {
+
+        // Get form name
+        ActionMapping mapping =
+            (ActionMapping) pageContext.getRequest().getAttribute(
+                Globals.MAPPING_KEY);
+        String formName = mapping.getName();
+
+        // Get value of form field
+        Object value =
+            RequestUtils.lookup(pageContext, formName, property, null);
+        if (value == null)
+            value = "";
+
+        // put variables into request scope for view page
+        pageContext.getRequest().setAttribute(
+            KEY_PREFIX + "_formName",
+            formName);
+        pageContext.getRequest().setAttribute(
+            KEY_PREFIX + "_property",
+            property);
+        pageContext.getRequest().setAttribute(
+            KEY_PREFIX + "_dateFormat",
+            dateFormat);
+        pageContext.getRequest().setAttribute(
+            KEY_PREFIX + "_readOnly",
+            readOnly);
+        pageContext.getRequest().setAttribute(KEY_PREFIX + "_value", value);
+
+        // dispatch to view page
+        try
+        {
+            pageContext.include("/tags/date.jsp");
+        }
+        catch (Exception e)
+        {
+            // can't handle this here
+            throw new JspException("ERROR including date.jsp");
+        }
+
+        // Don't evaluate content of tag, just continue processing this page
+        return (SKIP_BODY);
+    }
+
+    /**
+     * Date format string to be used.
+     * 
+     * @jsp.attribute required="true" rtexprvalue="true" type="java.lang.String"
+     */
+    public String getDateFormat()
+    {
+        return dateFormat;
+    }
+
+    /**
+     * Name of form property represented. 
+     * @jsp.attribute required="true" rtexprvalue="true" type="java.lang.String"
+     */
+    public String getProperty()
+    {
+        return property;
+    }
+
+    /**
+     * True if field should be readOnly. 
+     * @jsp.attribute required="false" rtexprvalue="true" type="java.lang.Boolean"
+     */
+    public Boolean getReadOnly()
+    {
+        return readOnly;
+    }
+
+    public void setDateFormat(String dateFormat)
+    {
+        this.dateFormat = dateFormat;
+    }
+
+    public void setProperty(String property)
+    {
+        this.property = property;
+    }
+
+    public void setReadOnly(Boolean readOnly)
+    {
+        this.readOnly = readOnly;
+    }
+
+}

Added: incubator/roller/trunk/src/org/roller/presentation/tags/HybridTag.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/tags/HybridTag.java?rev=189695&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/presentation/tags/HybridTag.java (added)
+++ incubator/roller/trunk/src/org/roller/presentation/tags/HybridTag.java Wed Jun  8 20:18:46 2005
@@ -0,0 +1,79 @@
+/*
+ * HybridTag.java
+ *
+ * Created on February 10, 2002, 11:12 PM
+ */
+
+package org.roller.presentation.tags;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.tagext.TagSupport;
+
+/**
+ * JSP tag designed to be used from JSP page or from Velocity page.
+ * Tag must be a standalone tag, design precludes contents.
+ * @author David M Johnson
+ */
+public abstract class HybridTag extends TagSupport 
+{
+    private static Log mLogger = 
+        LogFactory.getFactory().getInstance(HybridTag.class);
+
+    public HybridTag() 
+    {
+    }
+
+	public String toString()
+	{
+        String ret = null;
+        try 
+        {
+            StringWriter sw = new StringWriter();
+            doStartTag( new PrintWriter( sw, true ));
+			// See, design precludes contents 
+            doEndTag( new PrintWriter( sw, true ));
+            ret = sw.toString();
+        }
+        catch (Exception e)
+        {
+            ret = "Exception in tag";
+            mLogger.error(ret,e);
+        }
+        return ret;
+	}
+    
+	public String emit()
+	{
+		return toString();
+	}
+
+	public int doStartTag() throws JspException 
+	{
+		return doStartTag( new PrintWriter( pageContext.getOut(), true) );
+	}
+
+
+	public int doEndTag() throws JspException 
+	{
+		return doEndTag( new PrintWriter( pageContext.getOut(), true) );
+	}
+
+	/** Default processing of the end tag returning SKIP_BODY. */
+	public int doStartTag( PrintWriter pw ) throws JspException
+	{
+		return SKIP_BODY;
+	}
+
+	/** Default processing of the end tag returning EVAL_PAGE. */
+	public int doEndTag( PrintWriter pw ) throws JspException
+	{
+		return EVAL_PAGE;
+	}
+
+}

Added: incubator/roller/trunk/src/org/roller/presentation/tags/LinkParamTag.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/tags/LinkParamTag.java?rev=189695&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/presentation/tags/LinkParamTag.java (added)
+++ incubator/roller/trunk/src/org/roller/presentation/tags/LinkParamTag.java Wed Jun  8 20:18:46 2005
@@ -0,0 +1,192 @@
+/*
+ * $Header: /cvs/roller/roller/src/org/roller/presentation/tags/LinkParamTag.java,v 1.2 2004/08/18 03:19:34 lavandowska Exp $
+ * $Revision: 1.2 $
+ * $Date: 2004/08/18 03:19:34 $
+ *
+ * ====================================================================
+ */
+
+
+package org.roller.presentation.tags;
+
+import org.apache.log4j.Category;
+import org.apache.struts.util.RequestUtils;
+
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.tagext.BodyContent;
+import javax.servlet.jsp.tagext.BodyTagSupport;
+
+/**
+ * Implements a custom tag to add parameter to a href request.<br/>
+ * This tag is intended to be nested in a <code>hm:link</code> tag.
+ *
+ * Title:        BSquare
+ * Description:  Bsquare Projects
+ * Copyright:    Copyright (c) 2001
+ * Company:      HubMethods
+ * @author Eric Fesler
+ * @version 1.0
+ */
+
+public class LinkParamTag extends BodyTagSupport {
+    // ----------------------------------------------------- Logging
+    static Category cat = Category.getInstance(LinkParamTag.class);
+
+    // ----------------------------------------------------- Instance variables
+    /**
+     * The name of the request parameter
+     */
+    private String id = null;
+
+    /**
+     * The value of the request parameter
+     */
+    private String value = null;
+
+    /**
+     * The source bean
+     */
+    private String name = null;
+
+    /**
+     * The source bean property
+     */
+    private String property = null;
+
+    /**
+     * The scope of the source bean
+     */
+    private String scope = null;
+
+
+    // ----------------------------------------------------- Properties
+
+    /**
+     * Sets the request parameter tag name
+     *
+     * @param name the request parameter tag name
+     */
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    /**
+     * Returns the request parameter name
+     *
+     * @return the request parameter name
+     */
+    public String getId() {
+        return id;
+    }
+
+    /**
+     * Sets the request parameter value
+     *
+     * @param value the request parameter value
+     */
+    public void setValue(String value) {
+        this.value = value;
+    }
+
+    /**
+     * Returns the request parameter value
+     *
+     * @return the request parameter value
+     */
+    public String getValue() {
+        return value;
+    }
+
+    /**
+     * Sets the source bean name
+     * @param sourceBean the source bean name
+     */
+    public void setName ( String sourceBean) {
+        this.name = sourceBean;
+    }
+
+    /**
+     * Returns the source bean name
+     * @return the source bean name
+     */
+    public String getName () {
+        return this.name;
+    }
+
+    /**
+     * Sets the source bean property
+     * @param sourceProperty the source property
+     */
+    public void setProperty(String sourceProperty) {
+        this.property = sourceProperty;
+    }
+
+    /**
+     * Returns the source bean property
+     * @return the source property
+     */
+    public String getProperty() {
+        return property;
+    }
+
+    /**
+     * Set the source bean scope.
+     * @param sourceScope the source bean scope
+     */
+    public void setScope(String sourceScope) {
+        this.scope = sourceScope;
+    }
+
+    /**
+     * Returns the source bean scope
+     * @return the source bean scope
+     */
+    public String getScope() {
+        return this.scope;
+    }
+
+
+    // ------------------------------------------------------ Public Methods
+    /**
+     * Add the parameter and its value to the link tag
+     */
+    public int doEndTag() throws JspException {
+        // parent tag must be a LinkTag, gives access to methods in parent
+    LinkTag myparent = (LinkTag)javax.servlet.jsp.tagext.TagSupport.findAncestorWithClass(this, LinkTag.class);
+
+    if (myparent == null)
+        throw new JspException("linkparam tag not nested within link tag");
+        else {
+            BodyContent bodyContent = getBodyContent();
+            if (bodyContent != null && !bodyContent.getString().equals("")) {
+                setValue(bodyContent.getString());
+            }
+            else if (getValue() == null) setValue("null");
+//                throw new JspException("Unable to assign a value to the parameter: '" + getId() + "'");
+        myparent.addRequestParameter(getId(), getValue());
+    }
+    return SKIP_BODY;
+    }
+
+    /**
+     * Process the start tag
+     */
+    public int doStartTag() throws javax.servlet.jsp.JspException {
+
+        // Look up the requested property value
+        if (name != null) {
+            Object beanValue =
+                RequestUtils.lookup(pageContext, name, property, scope);
+            if (cat.isDebugEnabled()) cat.debug("Value is : '" + beanValue + "'");
+            if (beanValue == null)
+                return (EVAL_BODY_TAG);
+
+        // set the property as value
+            setValue(beanValue.toString());
+        }
+
+    // Continue processing this page
+    return (EVAL_BODY_TAG);
+
+    }
+}

Added: incubator/roller/trunk/src/org/roller/presentation/tags/LinkTag.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/tags/LinkTag.java?rev=189695&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/presentation/tags/LinkTag.java (added)
+++ incubator/roller/trunk/src/org/roller/presentation/tags/LinkTag.java Wed Jun  8 20:18:46 2005
@@ -0,0 +1,304 @@
+/*
+ * $Header: /cvs/roller/roller/src/org/roller/presentation/tags/LinkTag.java,v 1.3 2004/09/23 02:15:36 snoopdave Exp $
+ * $Revision: 1.3 $
+ * $Date: 2004/09/23 02:15:36 $
+ *
+ * ====================================================================
+ */
+package org.roller.presentation.tags;
+import java.lang.reflect.InvocationTargetException;
+import java.net.MalformedURLException;
+import java.net.URLEncoder;
+import java.util.Map;
+import javax.servlet.jsp.JspException;
+import org.apache.commons.beanutils.PropertyUtils;
+import org.apache.log4j.Category;
+import org.apache.struts.taglib.TagUtils;
+import org.apache.struts.util.RequestUtils;
+import org.apache.struts.util.ResponseUtils;
+
+/**
+ * Generates an HTML link. This class adds the parameter feature to the Struts
+ * html link tag. <br/>It should be use as follow:
+ * 
+ * <pre>
+ * 
+ *   &lt;hm:link href=&quot;http://java.sun.com&quot;&gt;
+ *     &lt;hm:linkparam name=&quot;action&quot; value=&quot;submit&quot; /&gt;
+ *     &lt;hm:linkparam name=&quot;ref&quot; value=&quot;144532&quot; /&gt;
+ *   &lt;/hm:linl&gt;
+ *   
+ * </pre>
+ * 
+ * This will produce the equivalent of
+ * 
+ * <pre>
+ * &lt;href=&quot;http://java.sun.com?_action=submit&amp;ref=144532&quot;&gt;
+ * </pre>
+ * 
+ * Title: BSquare Description: Bsquare Projects Copyright: Copyright (c) 2001
+ * Company: HubMethods
+ * 
+ * @author $Author: snoopdave $
+ * @version $Revision: 1.3 $
+ */
+public class LinkTag extends org.apache.struts.taglib.html.LinkTag
+{
+    // ------------------------------------------------------ Logging
+    static Category cat = Category.getInstance(LinkTag.class);
+    // ------------------------------------------------------ Instance Vartables
+    /**
+     * The full HREF URL
+     */
+    private StringBuffer hrefURL = new StringBuffer();
+
+    // ------------------------------------------------------------- Properties
+    //--------------------------------------------------------- Public Methods
+    /**
+     * Intialize the hyperlink.
+     * 
+     * @exception JspException
+     *                if a JSP exception has occurred
+     */
+    public int doStartTag() throws JspException
+    {
+        // Special case for name anchors
+        if (linkName != null)
+        {
+            StringBuffer results = new StringBuffer("<a name=\"");
+            results.append(linkName);
+            results.append("\">");
+            return (EVAL_BODY_TAG);
+        }
+        // Generate the hyperlink URL
+        Map params = RequestUtils.computeParameters(pageContext, paramId,
+                        paramName, paramProperty, paramScope, name, property,
+                        scope, transaction);
+        String url = null;
+        try
+        {
+            url = RequestUtils.computeURL(pageContext, forward, href, page,
+                            params, anchor, false);
+        }
+        catch (MalformedURLException e)
+        {
+            RequestUtils.saveException(pageContext, e);
+            throw new JspException(messages.getMessage("rewrite.url", e
+                            .toString()));
+        }
+        // Generate the opening anchor element
+        hrefURL = new StringBuffer("<a href=\"");
+        hrefURL.append(url);
+        if (cat.isDebugEnabled())
+            cat.debug("hrefURL = '" + hrefURL.toString());
+        // Evaluate the body of this tag
+        this.text = null;
+        return (EVAL_BODY_TAG);
+    }
+
+    /**
+     * Add a new parameter to the request
+     * 
+     * @param name
+     *            the name of the request parameter
+     * @param value
+     *            the value of the request parameter
+     */
+    public void addRequestParameter(String name, String value)
+    {
+        if (cat.isDebugEnabled())
+            cat.debug("Adding '" + name + "' with value '" + value + "'");
+        boolean question = (hrefURL.toString().indexOf('?') >= 0);
+        if (question)
+        { // There are request parameter already
+            hrefURL.append('&');
+        }
+        else
+            hrefURL.append('?');
+        hrefURL.append(name);
+        hrefURL.append('=');
+        hrefURL.append(URLEncoder.encode(value));
+        if (cat.isDebugEnabled())
+            cat.debug("hrefURL = '" + hrefURL.toString() + "'");
+    }
+
+    /**
+     * Render the href reference
+     * 
+     * @exception JspException
+     *                if a JSP exception has occurred
+     */
+    public int doEndTag() throws JspException
+    {
+        hrefURL.append("\"");
+        if (target != null)
+        {
+            hrefURL.append(" target=\"");
+            hrefURL.append(target);
+            hrefURL.append("\"");
+        }
+        hrefURL.append(prepareStyles());
+        hrefURL.append(prepareEventHandlers());
+        hrefURL.append(">");
+        if (text != null)
+            hrefURL.append(text);
+        hrefURL.append("</a>");
+        if (cat.isDebugEnabled())
+            cat.debug("Total request is = '" + hrefURL.toString() + "'");
+        // Print this element to our output writer
+        ResponseUtils.write(pageContext, hrefURL.toString());
+        return (EVAL_PAGE);
+    }
+
+    /**
+     * Release any acquired resources.
+     */
+    public void release()
+    {
+        super.release();
+        forward = null;
+        href = null;
+        name = null;
+        property = null;
+        target = null;
+    }
+
+    // ----------------------------------------------------- Protected Methods
+    /**
+     * Return the specified hyperlink, modified as necessary with optional
+     * request parameters.
+     * 
+     * @exception JspException
+     *                if an error occurs preparing the hyperlink
+     */
+    protected String hyperlink() throws JspException
+    {
+        String href = this.href;
+        // If "forward" was specified, compute the "href" to forward to
+//        if (forward != null)
+//        {
+//            ActionForwards forwards = (ActionForwards) pageContext
+//                            .getAttribute(Action.FORWARDS_KEY,
+//                                            PageContext.APPLICATION_SCOPE);
+//            ActionMapping mapping = (ActionMapping)pageContext.getAttribute(ActionConfig.)            
+//            if (forwards == null)
+//                throw new JspException(messages.getMessage("linkTag.forwards"));
+//            ActionForward forward = forwards.findForward(this.forward);
+//            if (forward == null)
+//                throw new JspException(messages.getMessage("linkTag.forward"));
+//            HttpServletRequest request = (HttpServletRequest) pageContext
+//                            .getRequest();
+//            href = request.getContextPath() + forward.getPath();
+//        }
+        // Just return the "href" attribute if there is no bean to look up
+        if ((property != null) && (name == null))
+            throw new JspException(messages.getMessage("getter.name"));
+        if (name == null)
+            return (href);
+        // Look up the map we will be using
+        Object bean = pageContext.findAttribute(name);
+        if (bean == null)
+            throw new JspException(messages.getMessage("getter.bean", name));
+        Map map = null;
+        if (property == null)
+        {
+            try
+            {
+                map = (Map) bean;
+            }
+            catch (ClassCastException e)
+            {
+                throw new JspException(messages.getMessage("linkTag.type"));
+            }
+        }
+        else
+        {
+            try
+            {
+                map = (Map) PropertyUtils.getProperty(bean, property);
+                if (map == null)
+                    throw new JspException(messages.getMessage(
+                                    "getter.property", property));
+            }
+            catch (IllegalAccessException e)
+            {
+                throw new JspException(messages.getMessage("getter.access",
+                                property, name));
+            }
+            catch (InvocationTargetException e)
+            {
+                Throwable t = e.getTargetException();
+                throw new JspException(messages.getMessage("getter.result",
+                                property, t.toString()));
+            }
+            catch (ClassCastException e)
+            {
+                throw new JspException(messages.getMessage("linkTag.type"));
+            }
+            catch (NoSuchMethodException e)
+            {
+                throw new JspException(messages.getMessage("getter.method",
+                                property, name));
+            }
+        }
+            // Append the required query parameters
+//        StringBuffer sb = new StringBuffer(href);
+//        boolean question = (href.indexOf("?") >= 0);
+//        Iterator keys = map.keySet().iterator();
+//        while (keys.hasNext())
+//        {
+//            String key = (String) keys.next();
+//            Object value = map.get(key);
+//            if (value instanceof String[])
+//            {
+//                String values[] = (String[]) value;
+//                for (int i = 0; i < values.length; i++)
+//                {
+//                    if (question)
+//                        sb.append('&');
+//                    else
+//                    {
+//                        sb.append('?');
+//                        question = true;
+//                    }
+//                    sb.append(key);
+//                    sb.append('=');
+//                    sb.append(URLEncoder.encode(values[i]));
+//                }
+//            }
+//            else
+//            {
+//                if (question)
+//                    sb.append('&');
+//                else
+//                {
+//                    sb.append('?');
+//                    question = true;
+//                }
+//                sb.append(key);
+//                sb.append('=');
+//                sb.append(URLEncoder.encode(value.toString()));
+//            }
+//        }
+//        // Return the final result
+//        return (sb.toString());
+        try
+        {
+            return TagUtils.getInstance().computeURL(
+                            pageContext,
+                            forward,
+                            href,
+                            null, 
+                            null,
+                            null,
+                            map,
+                            null,
+                            false
+                            );
+        }
+        catch (MalformedURLException e)
+        {
+            throw new JspException(e);
+        }
+    }
+}

Added: incubator/roller/trunk/src/org/roller/presentation/tags/RowTag.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/presentation/tags/RowTag.java?rev=189695&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/presentation/tags/RowTag.java (added)
+++ incubator/roller/trunk/src/org/roller/presentation/tags/RowTag.java Wed Jun  8 20:18:46 2005
@@ -0,0 +1,368 @@
+package org.roller.presentation.tags;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.struts.taglib.logic.IterateTag;
+
+import java.io.IOException;
+
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.JspWriter;
+import javax.servlet.jsp.tagext.Tag;
+
+
+/**
+ * <p>This tag generates table rows (i.e. &lt;tr&gt;....&lt;/tr&gt; elements) 
+ * with the background color set differently for alternating odd and even 
+ * rows. This tag only operates properly if embedded in an IterateTag.</p>
+ *
+ * <p>The following parameters can be specified for this Tag:</p>
+ * <ul>
+ * <li><code>oddColor </code> - The color for Odd numbered rows
+ * <li><code>evenColor</code> - The color for Even numbered rows
+ * <li><code>oddStyleClass</code> - The style class for Odd numbered rows
+ * <li><code>evenStyleClass</code> - The style class for Even numbered rows
+ * <li><code>align</code> - The alignment for the table row
+ * <li><code>valign</code> - The vertical alignment for the table row
+ * </ul>
+ *
+ * <p>Additionally this tag inherits the Event Handler and Style attributes
+ * from the BaseHandlerTag which can also be specified</p>
+ * 
+ * TODO Make RowTag work with STL's forEach tag too.
+ * 
+ * @jsp.tag name="row"
+ *
+ * @author Amarda Business Systems Ltd
+ * @version 1.0
+ */
+public class RowTag extends org.apache.struts.taglib.html.BaseHandlerTag
+{
+    // ----------------------------------------------------- Instance Variables
+    private static Log mLogger = LogFactory.getFactory()
+                                           .getInstance(RowTag.class);
+    protected final static String QUOTE = "\"";
+
+    /**
+     * Color of Odd rows in a table
+     */
+    protected String oddColor = null;
+
+    /**
+     *  Color of Even rows in a table
+     */
+    protected String evenColor = null;
+
+    /**
+     *  StyleClass of Odd rows in a table
+     */
+    protected String oddStyleClass = null;
+
+    /**
+     *  Style Class of Even rows in a table
+     */
+    protected String evenStyleClass = null;
+
+    /**
+     *  Alignment of the table row
+     */
+    protected String align = null;
+
+    /**
+     *  Vertical Alignment of the table row
+     */
+    protected String valign = null;
+
+    /**
+     * Return the color of Odd rows
+     * @jsp.attribute
+     */
+    public String getOddColor()
+    {
+        return (this.oddColor);
+    }
+
+    /**
+     * Set the color of Odd rows
+     *
+     * @param color HTML bgcolor value for Odd rows
+     */
+    public void setOddColor(String color)
+    {
+        this.oddColor = color;
+    }
+
+    /**
+     *  Return the color of Even rows
+     * @jsp.attribute
+     */
+    public String getEvenColor()
+    {
+        return (this.evenColor);
+    }
+
+    /**
+     * Set the color of Even rows
+     *
+     * @param color HTML bgcolor value for Even rows
+     */
+    public void setEvenColor(String color)
+    {
+        this.evenColor = color;
+    }
+
+    /**
+     * Return the Style Class of Odd rows
+     * @jsp.attribute
+     */
+    public String getOddStyleClass()
+    {
+        return (this.oddStyleClass);
+    }
+
+    /**
+     * Set the Style Class of Odd rows
+     *
+     * @param styleClass HTML Style Class value for Odd rows
+     */
+    public void setOddStyleClass(String styleClass)
+    {
+        this.oddStyleClass = styleClass;
+    }
+
+    /**
+     *  Return the Style Class of Even rows
+     * @jsp.attribute
+     */
+    public String getEvenStyleClass()
+    {
+        return (this.evenStyleClass);
+    }
+
+    /**
+     * Set the styleClass of Even rows
+     *
+     * @param styleClass HTML Style Class value for Even rows
+     */
+    public void setEvenStyleClass(String styleClass)
+    {
+        this.evenStyleClass = styleClass;
+    }
+
+    /**
+     *  Return the Alignment
+     * @jsp.attribute
+     */
+    public String getAlign()
+    {
+        return (this.align);
+    }
+
+    /**
+     * Set the Alignment
+     *
+     * @param Value for Alignment
+     */
+    public void setAlign(String align)
+    {
+        this.align = align;
+    }
+
+    /**
+     *  Return the Vertical Alignment
+     * @jsp.attribute
+     */
+    public String getValign()
+    {
+        return (this.valign);
+    }
+
+    /**
+     * Set the Vertical Alignment
+     *
+     * @param Value for Vertical Alignment
+     */
+    public void setValign(String valign)
+    {
+        this.valign = valign;
+    }
+
+    // ----------------------------------------------------- Public Methods
+
+    /**
+     * Start of Tag processing
+     *
+     * @exception JspException if a JSP exception occurs
+     */
+    public int doStartTag() throws JspException
+    {
+        // Continue processing this page
+        return EVAL_BODY_BUFFERED;
+    }
+
+    /**
+     * End of Tag Processing
+     *
+     * @exception JspException if a JSP exception occurs
+     */
+    public int doEndTag() throws JspException
+    {
+        StringBuffer buffer = new StringBuffer();
+
+
+        // Create a <tr> element based on the parameters
+        buffer.append("<tr");
+
+
+        // Prepare this HTML elements attributes
+        prepareAttributes(buffer);
+
+        buffer.append(">");
+
+        // Add Body Content
+        if (bodyContent != null)
+        {
+            buffer.append(bodyContent.getString().trim());
+        }
+
+        buffer.append("</tr>");
+
+        // Render this element to our writer
+        JspWriter writer = pageContext.getOut();
+
+        try
+        {
+            writer.print(buffer.toString());
+        }
+        catch (IOException e)
+        {
+            mLogger.error("ERROR in tag", e);
+            throw new JspException("Exception in RowTag doEndTag():" + 
+                                   e.toString());
+        }
+
+        return EVAL_PAGE;
+    }
+
+    /**
+     * Prepare the attributes of the HTML element
+     */
+    protected void prepareAttributes(StringBuffer buffer)
+    {
+        // Determine if it is an "Odd" or "Even" row
+        boolean evenNumber = ((getRowNumber() % 2) == 0)                     
+                             ? true : false;
+
+
+        // Append bgcolor parameter
+        buffer.append(prepareBgcolor(evenNumber));
+
+
+        // Append CSS class parameter
+        buffer.append(prepareClass(evenNumber));
+
+
+        // Append "align" parameter
+        buffer.append(prepareAttribute("align", align));
+
+
+        // Append "valign" parameter
+        buffer.append(prepareAttribute("valign", valign));
+
+
+        // Append Event Handler details
+        buffer.append(prepareEventHandlers());
+
+        try
+        {
+            // Append Style details
+            buffer.append(prepareStyles());
+        }
+        catch (Exception e)
+        {
+            mLogger.error("Unexpected exception", e);
+        }
+    }
+
+    /**
+     * Format attribute="value" from the specified attribute & value
+     */
+    protected String prepareAttribute(String attribute, String value)
+    {
+        return (value == null)       
+               ? "" : " " + attribute + "=" + QUOTE + value + QUOTE;
+    }
+
+    /**
+     * Format the bgcolor attribute depending on whether
+     * the row is odd or even.
+     *
+     * @param evenNumber Boolean set to true if an even numbered row
+     *
+     */
+    protected String prepareBgcolor(boolean evenNumber)
+    {
+        if (evenNumber)
+        {
+            return prepareAttribute("bgcolor", evenColor);
+        }
+        else
+        {
+            return prepareAttribute("bgcolor", oddColor);
+        }
+    }
+
+    /**
+     * Format the Style sheet class attribute depending on whether
+     * the row is odd or even.
+     *
+     * @param evenNumber Boolean set to true if an even numbered row
+     *
+     */
+    protected String prepareClass(boolean evenNumber)
+    {
+        if (evenNumber)
+        {
+            return prepareAttribute("class", evenStyleClass);
+        }
+        else
+        {
+            return prepareAttribute("class", oddStyleClass);
+        }
+    }
+
+    /**
+     * Determine the Row Number - from the IterateTag
+     */
+    protected int getRowNumber()
+    {
+        // Determine if embedded in an IterateTag
+        Tag tag = findAncestorWithClass(this, IterateTag.class);
+
+        if (tag == null)
+        {
+            return 1;
+        }
+
+        // Determine the current row number
+        IterateTag iterator = (IterateTag) tag;
+
+        //        return iterator.getLengthCount() + 1;
+        return iterator.getIndex() + 1;
+    }
+
+    /**
+     * Release resources after Tag processing has finished.
+     */
+    public void release()
+    {
+        super.release();
+
+        oddColor = null;
+        evenColor = null;
+        oddStyleClass = null;
+        evenStyleClass = null;
+        align = null;
+        valign = null;
+    }
+}
\ No newline at end of file