You are viewing a plain text version of this content. The canonical link for it is here.
Posted to jetspeed-dev@portals.apache.org by ta...@apache.org on 2006/11/13 20:20:34 UTC

svn commit: r474445 - in /portals/jetspeed-2/trunk/applications/j2-admin/src: java/org/apache/jetspeed/portlets/ java/org/apache/jetspeed/portlets/selector/ java/org/apache/jetspeed/portlets/selector/resources/ webapp/WEB-INF/ webapp/WEB-INF/view/selec...

Author: taylor
Date: Mon Nov 13 11:20:33 2006
New Revision: 474445

URL: http://svn.apache.org/viewvc?view=rev&rev=474445
Log:
https://issues.apache.org/jira/browse/JS2-610
Standard features completed for Category Portlet Selector:
* categorization view of portlets
* search
* icons associated with portlets
* administration edit mode to configure categories by keyword
* add portlets to page 
Still need to complete advanced features, and update all the portlet.xml files to better support categorization by keyword

Added:
    portals/jetspeed-2/trunk/applications/j2-admin/src/java/org/apache/jetspeed/portlets/CategoryInfo.java
    portals/jetspeed-2/trunk/applications/j2-admin/src/java/org/apache/jetspeed/portlets/selector/CategoryPortletSelector.java
    portals/jetspeed-2/trunk/applications/j2-admin/src/java/org/apache/jetspeed/portlets/selector/resources/
    portals/jetspeed-2/trunk/applications/j2-admin/src/java/org/apache/jetspeed/portlets/selector/resources/PortletSelector.properties
    portals/jetspeed-2/trunk/applications/j2-admin/src/webapp/WEB-INF/view/selectors/category-edit-selector.vm
    portals/jetspeed-2/trunk/applications/j2-admin/src/webapp/WEB-INF/view/selectors/category-portlet-header.vm
    portals/jetspeed-2/trunk/applications/j2-admin/src/webapp/WEB-INF/view/selectors/category-portlet-selector.vm
    portals/jetspeed-2/trunk/applications/j2-admin/src/webapp/css/category-portlet-selector.css
Modified:
    portals/jetspeed-2/trunk/applications/j2-admin/src/java/org/apache/jetspeed/portlets/PortletInfo.java
    portals/jetspeed-2/trunk/applications/j2-admin/src/webapp/WEB-INF/portlet.xml

Added: portals/jetspeed-2/trunk/applications/j2-admin/src/java/org/apache/jetspeed/portlets/CategoryInfo.java
URL: http://svn.apache.org/viewvc/portals/jetspeed-2/trunk/applications/j2-admin/src/java/org/apache/jetspeed/portlets/CategoryInfo.java?view=auto&rev=474445
==============================================================================
--- portals/jetspeed-2/trunk/applications/j2-admin/src/java/org/apache/jetspeed/portlets/CategoryInfo.java (added)
+++ portals/jetspeed-2/trunk/applications/j2-admin/src/java/org/apache/jetspeed/portlets/CategoryInfo.java Mon Nov 13 11:20:33 2006
@@ -0,0 +1,77 @@
+/* Copyright 2004 Apache Software Foundation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package org.apache.jetspeed.portlets;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Category Info
+ * 
+ * @author <a href="mailto:taylor@apache.org">David Sean Taylor</a>
+ * @version $Id: $
+ */
+public class CategoryInfo implements Serializable 
+{
+    String name;
+    String keywords;
+    List portlets = new ArrayList();
+    
+    public CategoryInfo(String name)
+    {
+        this.name = name;
+    }
+    public CategoryInfo(String name, String keywords)
+    {
+        this.name = name;
+        this.keywords = keywords;
+    }    
+    /**
+     * @return Returns the name.
+     */
+    public String getName()
+    {
+        return name;
+    }
+    
+    public void addPortlet(PortletInfo portlet)
+    {
+        portlets.add(portlet);
+    }
+    
+    public List getPortlets()
+    {
+        return portlets;
+    }
+    
+    /**
+     * @return Returns the keywords.
+     */
+    public String getKeywords()
+    {
+        return keywords;
+    }
+    
+    /**
+     * @param keywords The keywords to set.
+     */
+    public void setKeywords(String keywords)
+    {
+        this.keywords = keywords;
+    }
+    
+}
\ No newline at end of file

Modified: portals/jetspeed-2/trunk/applications/j2-admin/src/java/org/apache/jetspeed/portlets/PortletInfo.java
URL: http://svn.apache.org/viewvc/portals/jetspeed-2/trunk/applications/j2-admin/src/java/org/apache/jetspeed/portlets/PortletInfo.java?view=diff&rev=474445&r1=474444&r2=474445
==============================================================================
--- portals/jetspeed-2/trunk/applications/j2-admin/src/java/org/apache/jetspeed/portlets/PortletInfo.java (original)
+++ portals/jetspeed-2/trunk/applications/j2-admin/src/java/org/apache/jetspeed/portlets/PortletInfo.java Mon Nov 13 11:20:33 2006
@@ -12,7 +12,6 @@
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
-
 package org.apache.jetspeed.portlets;
 
 import java.io.Serializable;
@@ -31,6 +30,7 @@
     String name;
     String displayName;
     String description;
+    String image;
     
     public PortletInfo(String name, String displayName, String description)
     {
@@ -38,6 +38,15 @@
         this.displayName = displayName;
         this.description = description;
     }
+
+    public PortletInfo(String name, String displayName, String description, String image)
+    {
+        this.name = name;
+        this.displayName = displayName;
+        this.description = description;
+        this.image = image;
+    }
+    
     /**
      * @return Returns the description.
      */
@@ -58,5 +67,23 @@
     public String getName()
     {
         return name;
+    }
+
+    
+    /**
+     * @return Returns the image.
+     */
+    public String getImage()
+    {
+        return image;
+    }
+
+    
+    /**
+     * @param image The image to set.
+     */
+    public void setImage(String image)
+    {
+        this.image = image;
     }
 }

Added: portals/jetspeed-2/trunk/applications/j2-admin/src/java/org/apache/jetspeed/portlets/selector/CategoryPortletSelector.java
URL: http://svn.apache.org/viewvc/portals/jetspeed-2/trunk/applications/j2-admin/src/java/org/apache/jetspeed/portlets/selector/CategoryPortletSelector.java?view=auto&rev=474445
==============================================================================
--- portals/jetspeed-2/trunk/applications/j2-admin/src/java/org/apache/jetspeed/portlets/selector/CategoryPortletSelector.java (added)
+++ portals/jetspeed-2/trunk/applications/j2-admin/src/java/org/apache/jetspeed/portlets/selector/CategoryPortletSelector.java Mon Nov 13 11:20:33 2006
@@ -0,0 +1,545 @@
+/*
+ * Copyright 2000-2001,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jetspeed.portlets.selector;
+
+import java.io.IOException;
+import java.security.AccessControlException;
+import java.security.AccessController;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import javax.portlet.ActionRequest;
+import javax.portlet.ActionResponse;
+import javax.portlet.PortletConfig;
+import javax.portlet.PortletContext;
+import javax.portlet.PortletException;
+import javax.portlet.PortletMode;
+import javax.portlet.PortletPreferences;
+import javax.portlet.PortletSession;
+import javax.portlet.RenderRequest;
+import javax.portlet.RenderResponse;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.jetspeed.CommonPortletServices;
+import org.apache.jetspeed.JetspeedActions;
+import org.apache.jetspeed.components.portletregistry.PortletRegistry;
+import org.apache.jetspeed.headerresource.HeaderResource;
+import org.apache.jetspeed.om.common.portlet.MutablePortletApplication;
+import org.apache.jetspeed.om.common.portlet.PortletDefinitionComposite;
+import org.apache.jetspeed.om.common.preference.PreferenceComposite;
+import org.apache.jetspeed.portlets.CategoryInfo;
+import org.apache.jetspeed.portlets.PortletInfo;
+import org.apache.jetspeed.search.ParsedObject;
+import org.apache.jetspeed.search.SearchEngine;
+import org.apache.jetspeed.security.PortletPermission;
+import org.apache.pluto.om.common.Parameter;
+import org.apache.portals.gems.dojo.AbstractDojoVelocityPortlet;
+import org.apache.velocity.context.Context;
+
+/**
+ * CategoryPortletSelector selects categories organized by categories
+ * 
+ * @author <a href="mailto:taylor@apache.org">David Sean Taylor</a>
+ * @version $Id: $
+ */
+public class CategoryPortletSelector extends AbstractDojoVelocityPortlet implements Comparator
+{
+    protected final Log logger = LogFactory.getLog(this.getClass());
+    public final static String PORTLET_ICON = "portlet-icon";
+    protected final static String PORTLETS = "category.selector.portlets";
+    protected final static String CATEGORIES = "category.selector.categories";
+    protected final static String PAGE = "category.selector.page";
+    public final static String JSPAGE = "jspage";
+    
+    protected PortletRegistry registry;
+    protected SearchEngine searchEngine;
+       
+    public void init(PortletConfig config)
+    throws PortletException 
+    {
+        super.init(config);
+        PortletContext context = getPortletContext();                
+        registry = (PortletRegistry)context.getAttribute(CommonPortletServices.CPS_REGISTRY_COMPONENT);
+        if (null == registry)
+        {
+            throw new PortletException("Failed to find the Portlet Registry on portlet initialization");
+        }        
+        searchEngine = (SearchEngine)context.getAttribute(CommonPortletServices.CPS_SEARCH_COMPONENT);
+        if (null == searchEngine)
+        {
+            throw new PortletException("Failed to find the Search Engine on portlet initialization");
+        }                
+    }
+    
+    public void doView(RenderRequest request, RenderResponse response)
+            throws PortletException, IOException
+    {
+        PortletPreferences prefs = request.getPreferences();
+        this.getContext(request).put("Columns", prefs.getValue("Columns", "4"));
+        this.getContext(request).put("Rows", prefs.getValue("Rows", "6"));
+        this.getContext(request).put("portlets", retrievePortlets(request, null));
+        this.getContext(request).put("categories", retrieveCategories(request));
+        processPage(request);
+        super.doView(request, response);
+    }
+
+    protected void processPage(RenderRequest request)
+    {
+        String page = request.getParameter(JSPAGE);
+        if (page == null || page.equals(""))
+        {
+            page = (String)request.getPortletSession().getAttribute(PAGE);
+        }
+        else
+        {
+            request.getPortletSession().setAttribute(PAGE, page);
+        }
+        this.getContext(request).put(JSPAGE, page);
+        
+    }
+    
+    public List retrieveCategories(RenderRequest request)
+    throws PortletException
+    {
+        List categories = (List)request.getPortletSession().getAttribute(CATEGORIES);
+        if (categories != null)
+        {
+            return categories;
+        }
+        Locale locale = request.getLocale();        
+        categories = new ArrayList();
+        PortletPreferences prefs = request.getPreferences();
+        String cats = prefs.getValue("Categories", null);
+        if (cats == null)
+        {
+            throw new PortletException("No categories defined, please add categories via edit mode.");
+        }
+        StringTokenizer catTokenizer = new StringTokenizer(cats, ",");
+        while (catTokenizer.hasMoreTokens())
+        {
+            String name = catTokenizer.nextToken().trim();
+            CategoryInfo cat = new CategoryInfo(name);
+            String keywords = prefs.getValue("Keywords:" + name, null);
+            if (keywords != null)
+            {
+                StringTokenizer keyTokenizer = new StringTokenizer(keywords, ",");
+                StringBuffer searchString = new StringBuffer();
+                int count = 0;
+                while (keyTokenizer.hasMoreTokens())
+                {
+                    String keyword = keyTokenizer.nextToken().trim();
+                    if (count > 0)
+                    {
+                        searchString.append(" | ");
+                    }
+                    searchString.append(keyword);
+                    count++;
+                }
+                if (count > 0)
+                {
+                    Iterator portlets = searchEngine.search(searchString.toString()).getResults().iterator();
+                    while (portlets.hasNext())
+                    {
+                        PortletDefinitionComposite portlet = 
+                            getPortletFromParsedObject((ParsedObject)portlets.next());
+                        PortletInfo portletInfo = filterPortlet(portlet, locale);
+                        if (portletInfo != null)
+                        {
+                            cat.addPortlet(portletInfo);
+                        }                    
+                    }                
+                    Collections.sort(cat.getPortlets(), this);
+                    categories.add(cat);
+                }
+            }
+        }
+        request.getPortletSession().setAttribute(CATEGORIES, categories);        
+        return categories;
+    }
+    
+    public List retrievePortlets(RenderRequest request, String filter)
+    {
+        List portletsList = (List)request.getPortletSession().getAttribute(PORTLETS);
+        if (portletsList != null)
+        {
+            return portletsList;
+        }        
+        Iterator portlets = null;
+        List list = new ArrayList();
+        Locale locale = request.getLocale();                
+        if (filter == null)
+            portlets = registry.getAllPortletDefinitions().iterator();
+        else
+            portlets = searchEngine.search(filter).getResults().iterator();
+        
+        while (portlets.hasNext())
+        {
+            PortletDefinitionComposite portlet = null;
+            if (filter == null)
+                portlet = (PortletDefinitionComposite)portlets.next();
+            else
+                portlet = getPortletFromParsedObject((ParsedObject)portlets.next());
+            
+            PortletInfo portletInfo = filterPortlet(portlet, locale);
+            if (portletInfo != null)
+            {
+                list.add(portletInfo);
+            }
+        }            
+        Collections.sort(list, this);
+        request.getPortletSession().setAttribute(PORTLETS, list);
+        return list;
+    }
+
+    /**
+     * Filters portlets being added to the based on security checks and layout criteria
+     * 
+     * @param portlet
+     * @return null if filtered, otherwise PortletInfo to be added to list
+     */
+    protected PortletInfo filterPortlet(PortletDefinitionComposite portlet, Locale locale)
+    {
+        if (portlet == null)
+            return null;
+        
+        MutablePortletApplication muta = (MutablePortletApplication)portlet.getPortletApplicationDefinition();
+        String appName = muta.getName();
+        if (appName != null && appName.equals("jetspeed-layouts"))
+            return null;                
+        
+        // SECURITY filtering
+        String uniqueName = appName + "::" + portlet.getName();
+        try
+        {
+            AccessController.checkPermission(new PortletPermission(portlet.getUniqueName(), JetspeedActions.MASK_VIEW));
+            Parameter param = portlet.getInitParameterSet().get(PORTLET_ICON);
+            String image;
+            if (param != null)
+            {
+                String relativeImagePath = param.getValue();
+                String context = muta.getWebApplicationDefinition().getContextRoot();
+                image = context + relativeImagePath;
+            }
+            else
+            {
+                // default TODO: assign image by category
+                image = "images/portlets/preferences-desktop-locale.png";
+            }
+            return new PortletInfo(uniqueName, portlet.getDisplayNameText(locale), portlet.getDescriptionText(locale), image);
+        }
+        catch (AccessControlException ace)
+        {
+            return null;
+        }
+        
+    }
+    
+    protected PortletDefinitionComposite getPortletFromParsedObject(ParsedObject po)
+    {
+        boolean found = false;
+        String name = "";
+        Map fields = po.getFields();
+        if(fields != null)
+        {
+            Object id = fields.get("ID");
+    
+            if(id != null)
+            {
+                if(id instanceof Collection)
+                {
+                    Collection coll = (Collection)id;
+                    name = (String) coll.iterator().next();
+                }
+                else
+                {
+                    name = (String)id;
+                }
+            }
+            
+            if(po.getType().equals("portlet"))
+            {
+                Object pa = fields.get("portlet_application");
+                String paName = "";
+                if(pa != null)
+                {
+                    if(id instanceof Collection)
+                    {
+                        Collection coll = (Collection)pa;
+                        paName = (String) coll.iterator().next();
+                    }
+                    else
+                    {
+                        paName = (String)pa;
+                    }
+                }
+                name = paName + "::" + name;
+                found = true;
+            }
+        }
+        if (found == false)
+            return null;
+        
+        return registry.getPortletDefinitionByUniqueName(name);
+    }
+        
+    
+    public void processAction(ActionRequest request,
+            ActionResponse actionResponse) throws PortletException, IOException
+    {
+        if (request.getPortletMode() == PortletMode.EDIT)
+        {
+            String removes = request.getParameter("jsRemovedCats");
+            String modifiedCats = request.getParameter("jsModifiedCats");
+            String modifiedKeys = request.getParameter("jsModifiedKeys");
+            String addedCats = request.getParameter("jsAddedCats");
+            String addedKeys = request.getParameter("jsAddedKeys");
+            String columns = request.getParameter("Columns");
+            String rows = request.getParameter("Rows");
+            String cats = request.getParameter("jsCats");
+            String page = request.getParameter(JSPAGE);
+            MutablePortletApplication pa = registry.getPortletApplication("j2-admin");
+            String portletName = this.getPortletName();
+            PortletDefinitionComposite portlet = (PortletDefinitionComposite) pa.getPortletDefinitionByName(portletName);
+            boolean updated = updateNumericPref("Columns", columns, 10, portlet);
+            updated = updated | updateNumericPref("Rows", rows, 100, portlet);
+                        
+            // process removes first
+            if (!isEmpty(removes))
+            {
+                StringTokenizer tokenizer = new StringTokenizer(removes, ",");
+                while (tokenizer.hasMoreTokens())
+                {
+                    String name = tokenizer.nextToken().trim();
+                    updated = updated | removePref("Keywords:" + name, portlet);
+                }                
+            }
+            
+            // process adds
+            if (!isEmpty(addedCats))
+            {
+                StringTokenizer keyTokenizer = new StringTokenizer(addedKeys, "|");
+                StringTokenizer tokenizer = new StringTokenizer(addedCats, ",");
+                while (tokenizer.hasMoreTokens() && keyTokenizer.hasMoreTokens())
+                {
+                    String name = tokenizer.nextToken().trim();
+                    String keys = keyTokenizer.nextToken().trim();
+                    updated = updated | addPref("Keywords:" + name, keys, portlet);
+                }                
+            }
+            
+            // process updates
+            if (!isEmpty(modifiedCats))
+            {
+                StringTokenizer keyTokenizer = new StringTokenizer(modifiedKeys, "|");
+                StringTokenizer tokenizer = new StringTokenizer(modifiedCats, ",");
+                while (tokenizer.hasMoreTokens())
+                {
+                    String name = tokenizer.nextToken().trim();
+                    String keys = keyTokenizer.nextToken().trim();                    
+                    updated = updated | modifyPref("Keywords:" + name, keys, portlet);
+                }                
+            }
+                                    
+            try
+            {
+                if (updated)
+                {
+                    // process category string list
+                    // sort it first
+                    StringTokenizer catTokenizer = new StringTokenizer(cats, ",");
+                    List sorted = new ArrayList();
+                    while (catTokenizer.hasMoreTokens())
+                    {
+                        String name = catTokenizer.nextToken().trim();
+                        sorted.add(name);
+                    }
+                    Collections.sort(sorted);
+                    Iterator si = sorted.iterator();
+                    StringBuffer temp = new StringBuffer();
+                    int count = 0;
+                    while (si.hasNext())
+                    {
+                        String name = (String)si.next();
+                        if (count > 0)
+                        {
+                            temp.append(",");
+                        }
+                        temp.append(name);
+                        count++;
+                    }
+                    cats = temp.toString();
+                    System.out.println("cats = [" + cats + "]");
+                    modifyPref("Categories", cats, portlet);
+                    
+                    // finally save it all
+                    registry.savePortletDefinition(portlet);
+                }
+            }
+            catch (Exception e)
+            {
+                throw new PortletException("Failed to update portlet", e);
+            }
+            PortletSession session = request.getPortletSession();
+            session.removeAttribute(PORTLETS);
+            session.removeAttribute(CATEGORIES);
+            actionResponse.setPortletMode(PortletMode.VIEW);
+            actionResponse.setRenderParameter(JSPAGE, page);
+        }
+        else
+        {
+            String reset = request.getParameter("reset");        
+            if (reset != null && reset.equals("true"))
+            {
+                PortletSession session = request.getPortletSession();
+                session.removeAttribute(PORTLETS);
+                session.removeAttribute(CATEGORIES);
+            }
+        }
+    }
+
+    private boolean addPref(String prefName, String keywords, PortletDefinitionComposite portlet)
+    {
+        PreferenceComposite pref = (PreferenceComposite) portlet.getPreferenceSet().get(prefName);        
+        if (pref == null)
+        {
+            portlet.addPreference(prefName, new String[] { keywords });
+        }
+        else
+        {
+            return modifyPref(prefName, keywords, portlet);           
+        }        
+        return true;
+    }
+    
+    private boolean modifyPref(String prefName, String keywords, PortletDefinitionComposite portlet)
+    {
+        PreferenceComposite prefComp = (PreferenceComposite) portlet.getPreferenceSet().get(prefName);
+        String[] values = prefComp.getValueArray();
+        if(!values[0].equals(keywords))
+        {
+            prefComp.setValueAt(0, keywords);
+            return true;
+        }
+        return false;
+    }
+    
+    private boolean removePref(String prefName, PortletDefinitionComposite portlet)
+    {
+        Iterator prefIter = portlet.getPreferenceSet().iterator();
+        while (prefIter.hasNext())
+        {
+            PreferenceComposite pref = (PreferenceComposite) prefIter.next();
+            String name = pref.getName();
+            if (name.equals(prefName))
+            {
+                    prefIter.remove();
+                    return true;
+            }
+        }
+        return false;
+    }
+    
+    private boolean updateNumericPref(String prefName, String param, int max, PortletDefinitionComposite portlet)
+    {
+        if (!isEmpty(param))
+        {
+            int val = 4;
+            try
+            {
+                val = Integer.parseInt(param);
+            }
+            catch (NumberFormatException e)
+            {
+                return false;                
+            }
+            if (val > max)
+                return false;
+            PreferenceComposite pref = (PreferenceComposite) portlet.getPreferenceSet().get(prefName);
+            String[] values = pref.getValueArray();
+            if(!values[0].equals(param))
+            {
+                pref.setValueAt(0, param);
+                return true;
+            }                           
+        }
+        return false;
+    }
+    private boolean isEmpty(String param)
+    {
+        if (param == null)
+            return true;
+        param = param.trim();
+        if (param.length() == 0)
+            return true;
+        return false;
+    }
+
+    public int compare(Object obj1, Object obj2)
+    {
+        PortletInfo portlet1 = (PortletInfo)obj1;
+        PortletInfo portlet2 = (PortletInfo)obj2;
+        String name1 = portlet1.getDisplayName();
+        String name2 = portlet2.getDisplayName();
+        name1 = (name1 == null) ? "unknown" : name1;
+        name2 = (name2 == null) ? "unknown" : name2;
+        return name1.compareTo(name2);
+    }
+        
+    protected void includeHeaderContent(HeaderResource headerResource)
+    {
+        headerResource.dojoAddCoreLibraryRequire( "dojo.widget.Dialog" );
+        headerResource.dojoAddCoreLibraryRequire( "dojo.widget.Button" );
+        headerResource.dojoAddCoreLibraryRequire( "dojo.widget.ContentPane" );
+        headerResource.dojoAddCoreLibraryRequire( "dojo.widget.LayoutContainer" );
+        headerResource.dojoAddModuleLibraryRequire( "jetspeed.desktop.core" );        
+    }
+
+    /* (non-Javadoc)
+     * @see org.apache.portals.bridges.velocity.GenericVelocityPortlet#doEdit(javax.portlet.RenderRequest, javax.portlet.RenderResponse)
+     */
+    public void doEdit(RenderRequest request, RenderResponse response) throws PortletException, IOException
+    {
+        Context context = getContext(request);
+        PortletPreferences prefs = request.getPreferences();
+        String cats = prefs.getValue("Categories", null);
+        List categories = new ArrayList();
+        if (cats != null)
+        {
+            StringTokenizer catTokenizer = new StringTokenizer(cats, ",");
+            while (catTokenizer.hasMoreTokens())
+            {
+                String name = catTokenizer.nextToken().trim();
+                CategoryInfo cat = new CategoryInfo(name);
+                String keys = prefs.getValue("Keywords:" + name, "");
+                cat.setKeywords(keys);
+                categories.add(cat);
+            }
+        }
+        context.put("categories", categories);
+        context.put("Rows", prefs.getValue("Rows", "5"));
+        context.put("Columns", prefs.getValue("Columns", "4"));
+        processPage(request);
+        super.doEdit(request, response);
+    }
+}

Added: portals/jetspeed-2/trunk/applications/j2-admin/src/java/org/apache/jetspeed/portlets/selector/resources/PortletSelector.properties
URL: http://svn.apache.org/viewvc/portals/jetspeed-2/trunk/applications/j2-admin/src/java/org/apache/jetspeed/portlets/selector/resources/PortletSelector.properties?view=auto&rev=474445
==============================================================================
--- portals/jetspeed-2/trunk/applications/j2-admin/src/java/org/apache/jetspeed/portlets/selector/resources/PortletSelector.properties (added)
+++ portals/jetspeed-2/trunk/applications/j2-admin/src/java/org/apache/jetspeed/portlets/selector/resources/PortletSelector.properties Mon Nov 13 11:20:33 2006
@@ -0,0 +1,19 @@
+# Copyright 2004 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#
+
+select=Select
+category=Category
+

Modified: portals/jetspeed-2/trunk/applications/j2-admin/src/webapp/WEB-INF/portlet.xml
URL: http://svn.apache.org/viewvc/portals/jetspeed-2/trunk/applications/j2-admin/src/webapp/WEB-INF/portlet.xml?view=diff&rev=474445&r1=474444&r2=474445
==============================================================================
--- portals/jetspeed-2/trunk/applications/j2-admin/src/webapp/WEB-INF/portlet.xml (original)
+++ portals/jetspeed-2/trunk/applications/j2-admin/src/webapp/WEB-INF/portlet.xml Mon Nov 13 11:20:33 2006
@@ -41,7 +41,7 @@
     <portlet-info>
       <title>Portlet Application Lifecycle Manager</title>
       <short-title>PALM</short-title>
-      <keywords>applications,apps,PALM</keywords>
+      <keywords>applications,apps,PALM,management,admin</keywords>
     </portlet-info>
   </portlet>
 
@@ -67,6 +67,7 @@
         <portlet-info>
             <title>Login</title>
             <short-title>Login</short-title>
+            <keywords>login,security,management,admin</keywords>            
         </portlet-info>
     </portlet>
     <portlet id="LoginPortletForXHTMLBasic">
@@ -91,6 +92,7 @@
         <portlet-info>
             <title>Login</title>
             <short-title>Login</short-title>
+            <keywords>login,security,management,admin</keywords>                        
         </portlet-info>
     </portlet>
     <!-- *********************** -->
@@ -123,7 +125,7 @@
         <portlet-info>
             <title>Change Password</title>
             <short-title>Change Password</short-title>
-            <keywords>security,user,password</keywords>
+            <keywords>security,user,password,admin,login</keywords>            
         </portlet-info>
     </portlet>
 
@@ -157,7 +159,7 @@
         <portlet-info>
             <title>User Browser</title>
             <short-title>Users</short-title>
-            <keywords>admin,security,users</keywords>
+            <keywords>admin,security,users</keywords>            
         </portlet-info>
         <portlet-preferences>
             <preference>
@@ -264,7 +266,7 @@
         <portlet-info>
             <title>User Detail Information</title>
             <short-title>User</short-title>
-            <keywords>admin,security,users</keywords>
+            <keywords>admin,security,users</keywords>            
         </portlet-info>
     </portlet>
 
@@ -298,7 +300,7 @@
         <portlet-info>
             <title>Group Detail Information</title>
             <short-title>Group</short-title>
-            <keywords>admin,security,group</keywords>
+            <keywords>admin,security,group</keywords>            
         </portlet-info>
     </portlet>
 
@@ -438,7 +440,7 @@
         <portlet-info>
             <title>User Select</title>
             <short-title>Users</short-title>
-            <keywords>admin,security,users</keywords>
+            <keywords>users,selector,chooser</keywords>
         </portlet-info>
     </portlet>
 
@@ -476,7 +478,7 @@
         <portlet-info>
             <title>Users Select</title>
             <short-title>Users</short-title>
-            <keywords>admin,security,users</keywords>
+            <keywords>users,chooser,selector,multi</keywords>
         </portlet-info>
     </portlet>
 
@@ -514,7 +516,7 @@
         <portlet-info>
             <title>Role Select</title>
             <short-title>Roles</short-title>
-            <keywords>admin,security,roles</keywords>
+            <keywords>roles,selector,chooser</keywords>
         </portlet-info>
     </portlet>
 
@@ -552,7 +554,7 @@
       <portlet-info>
         <title>Roles Select</title>
         <short-title>Roles</short-title>
-        <keywords>admin,security,roles</keywords>
+        <keywords>roles,selector,chooser,multi</keywords>
       </portlet-info>
     </portlet>
     
@@ -590,7 +592,7 @@
         <portlet-info>
             <title>Group Select</title>
             <short-title>Groups</short-title>
-            <keywords>admin,security,groups</keywords>
+            <keywords>groups,selector,chooser</keywords>
         </portlet-info>
     </portlet>
 
@@ -688,7 +690,7 @@
     <portlet-info>
       <title>Locale Selector</title>
       <short-title>This is a portlet to select your preferred locale</short-title>
-      <keywords>Locale,Language</keywords>
+      <keywords>locale,language,tools</keywords>
     </portlet-info>
 </portlet>
 
@@ -719,7 +721,7 @@
     <portlet-info>
       <title>Portlet Application Browser</title>
       <short-title>Apps</short-title>
-      <keywords>applications,apps,PAM</keywords>
+      <keywords>admin,registry,applications,apps,PAM</keywords>
     </portlet-info>
   </portlet>
 
@@ -750,7 +752,7 @@
     <portlet-info>
       <title>Portlet Application Detail</title>
       <short-title>PAD</short-title>
-      <keywords>applications,apps,PAM</keywords>
+      <keywords>admin,registry,applications,apps,PAM</keywords>
     </portlet-info>
   </portlet>
 <!--
@@ -804,7 +806,7 @@
     <portlet-info>
       <title>Portal Site Browser</title>
       <short-title>Site</short-title>
-      <keywords>admin,site</keywords>
+      <keywords>admin,site,portal,pages,folders</keywords>
     </portlet-info>
 </portlet>
 
@@ -841,7 +843,7 @@
     <portlet-info>
       <title>Portal Site Detail</title>
       <short-title>Site</short-title>
-      <keywords>admin,site</keywords>
+      <keywords>admin,site,portal,pages,folders</keywords>
     </portlet-info>
 	
 </portlet>
@@ -876,10 +878,10 @@
     <portlet-info>
       <title>Profiler Admin</title>
       <short-title>Profiler</short-title>
-      <keywords>admin,profiler</keywords>
+      <keywords>admin,profiler,profile</keywords>
     </portlet-info>
 </portlet>
-
+<!--
 <portlet id="JetspeedUserAdmin">
     <init-param>
         <description>This parameter sets the template used in view mode.</description>
@@ -906,10 +908,10 @@
     <portlet-info>
       <title>User Administration</title>
       <short-title>Users</short-title>
-      <keywords>admin,security,users</keywords>
+      <keywords>admin,security,users</keywords>        
     </portlet-info>
 </portlet>
-
+-->
 <portlet id="PortletSelector">
     <init-param>
     	<description>This parameter sets the template used in view mode.</description>
@@ -944,7 +946,7 @@
     <portlet-info>
       <title>Portlet Selector</title>
       <short-title>Portlets</short-title>
-      <keywords>portlet,selector,customizer,chooser,add</keywords>
+      <keywords>admin,portlet,selector,customizer,chooser,add</keywords>
     </portlet-info>
 </portlet>
 <portlet id="PortletEntityBrowserPortlet">
@@ -972,7 +974,7 @@
     <portlet-info>
       <title>Portlet Entity Browser</title>
       <short-title>Portlet Entity Browser</short-title>
-      <keywords>applications,apps,entity,manager</keywords>
+      <keywords>admin,applications,apps,entity,manager</keywords>
     </portlet-info>
   </portlet>
   <portlet id="PortletEntityEditorPortlet">
@@ -986,7 +988,7 @@
     <display-name xml:lang="ja">ポートレットエンティティブラウザ</display-name>
     <display-name xml:lang="zh">编辑Portlet实例</display-name>
     <display-name xml:alang="zh-TW">編輯Portlet實例</display-name>
-    <description>Provides for editing of portlet enitites</description>
+    <description>Provides for editing of portlet entites</description>
     <description xml:lang="ja">ポートレットエンティティの編集する機能を提供します。</description>
     <description xml:lang="zh">编辑Portlet实例。</description>
     <description xml:lang="zh-TW">編輯Portlet實例。</description>
@@ -1000,7 +1002,7 @@
     <portlet-info>
       <title>Portlet Entity Editor</title>
       <short-title>Portlet Entity Editor</short-title>
-      <keywords>applications,apps,entity,manager</keywords>
+      <keywords>admin,applications,apps,entity,manager</keywords>
     </portlet-info>
   </portlet>
 
@@ -1029,7 +1031,7 @@
     <portlet-info>
       <title>Portal Statistics</title>
       <short-title>Statistics</short-title>
-      <keywords>statistics,portlets,pages,access</keywords>
+      <keywords>admin,statistics,portlets,pages,access</keywords>
     </portlet-info>
   </portlet>
 
@@ -1083,7 +1085,7 @@
     <portlet-info>
       <title>Forgotten Password</title>
       <short-title>Forgotten Password</short-title>
-      <keywords>password, forgotten, lost, passwd </keywords>
+      <keywords>tools,users,password,forgotten,lost,passwd</keywords>
     </portlet-info>
   </portlet>
 
@@ -1113,7 +1115,7 @@
     <portlet-info>
       <title>Forgotten Password Return</title>
       <short-title>forgot PS return</short-title>
-      <keywords> </keywords>
+      <keywords>password</keywords>
     </portlet-info>
   </portlet>
   
@@ -1188,10 +1190,10 @@
     <portlet-info>
       <title>User Registration </title>
       <short-title>User Registration</short-title>
-      <keywords>password, register, signup, new user </keywords>
+      <keywords>admin,password,register,signup,user</keywords>
     </portlet-info>
   </portlet>
-
+<!--
   <portlet id="CustomizerPortlet">
     <init-param>
         <description>This parameter sets the template used in view mode.</description>
@@ -1220,6 +1222,7 @@
       <keywords>customizer, site</keywords>
     </portlet-info>
   </portlet>
+-->    
     <portlet id="PortalLoginPortlet">
         <init-param>
             <name>ViewPage</name>
@@ -1242,6 +1245,7 @@
         <portlet-info>
             <title>Portal Login</title>
             <short-title>Login</short-title>
+            <keywords>admin,security,users,login</keywords>                        
         </portlet-info>
     </portlet>
     <portlet id="SecurityPermissionsPortlet">
@@ -1263,6 +1267,7 @@
         <portlet-info>
             <title>Security Permissions</title>
             <short-title>Permissions</short-title>
+            <keywords>admin,security,permission,policy,JAAS</keywords>                        
         </portlet-info>
     </portlet>
 
@@ -1292,6 +1297,7 @@
         <portlet-info>
             <title>Security Constraints</title>
             <short-title>Constraints</short-title>
+            <keywords>admin,security,constraint,policy,page</keywords>                        
         </portlet-info>
     </portlet>
 
@@ -1313,8 +1319,93 @@
     <portlet-info>
       <title>Date Time</title>
       <short-title>Time</short-title>
+      <keywords>tools,date,time</keywords>                    
     </portlet-info>
   </portlet>
-         
+
+    <portlet id="CategoryPortletSelector">
+		<init-param>
+			<name>ViewPage</name>
+			<value>/WEB-INF/view/selectors/category-portlet-selector.vm</value>
+		</init-param>        
+		<init-param>
+			<name>EditPage</name>
+			<value>/WEB-INF/view/selectors/category-edit-selector.vm</value>
+		</init-param>                
+		<init-param>
+			<name>HeaderPage</name>
+			<value>/WEB-INF/view/selectors/category-portlet-header.vm</value>
+		</init-param>                
+		<init-param>
+			<name>dojo.requires.core</name>
+			<value>dojo.lang.*;dojo.event.*;dojo.io.*;dojo.widget.*;dojo.widget.Tree;dojo.widget.Button;dojo.widget.Checkbox;dojo.widget.Dialog;dojo.widget.TabContainer;dojo.widget.ContentPane;dojo.widget.LayoutContainer</value>
+		</init-param>        
+        <portlet-name>CategoryPortletSelector</portlet-name>
+    	<display-name>Portlet Selector</display-name>        
+    	<display-name xml:lang="ja">セキュリティ制限</display-name>        
+        <portlet-class>org.apache.jetspeed.portlets.selector.CategoryPortletSelector</portlet-class>
+        <supports>
+            <mime-type>text/html</mime-type>
+            <portlet-mode>view</portlet-mode>
+            <portlet-mode>edit</portlet-mode>
+        </supports>
+        <resource-bundle>org.apache.jetspeed.portlets.selector.resources.PortletSelector</resource-bundle>
+        <portlet-info>
+            <title>Portlet Selector</title>
+            <short-title>Select</short-title>
+            <keywords>admin,registry,selector,choose,portlets,add,customize</keywords>            
+        </portlet-info>
+        <portlet-preferences>
+            <preference>
+                <name>Categories</name>
+                <value>Administration,Finance,Fun,News,Security,Sports,Technology,Tools,Tutorial</value>
+            </preference>
+            <preference>
+                <name>Keywords:Administration</name>
+                <value>admin,management</value>
+            </preference>
+            <preference>
+                <name>Keywords:Finance</name>
+                <value>finance,stock,market,money,quote</value>
+            </preference>
+            <preference>
+                <name>Keywords:Fun</name>
+                <value>fun,game,cool</value>
+            </preference>
+            <preference>
+                <name>Keywords:News</name>
+                <value>news,RSS,feed,headline</value>
+            </preference>
+            <preference>
+                <name>Keywords:Security</name>
+                <value>security,user,role,group</value>
+            </preference>
+            <preference>
+                <name>Keywords:Sports</name>
+                <value>sport,football,baseball,hockey,soccer,golf,tennis</value>
+            </preference>
+            <preference>
+                <name>Keywords:Technology</name>
+                <value>technology,computer,web</value>
+            </preference>
+            <preference>
+                <name>Keywords:Tools</name>
+                <value>tools,desktop,weather</value>
+            </preference>            
+            <preference>
+                <name>Keywords:Tutorial</name>
+                <value>demo,learn,tutorial,programming</value>
+            </preference>    
+            <preference>
+                <name>Columns</name>                    
+                <value>4</value>
+            </preference>
+            <preference>
+                <name>Rows</name>                    
+                <value>5</value>
+            </preference>
+        </portlet-preferences>        
+    </portlet>
+             
 </portlet-app>
 

Added: portals/jetspeed-2/trunk/applications/j2-admin/src/webapp/WEB-INF/view/selectors/category-edit-selector.vm
URL: http://svn.apache.org/viewvc/portals/jetspeed-2/trunk/applications/j2-admin/src/webapp/WEB-INF/view/selectors/category-edit-selector.vm?view=auto&rev=474445
==============================================================================
--- portals/jetspeed-2/trunk/applications/j2-admin/src/webapp/WEB-INF/view/selectors/category-edit-selector.vm (added)
+++ portals/jetspeed-2/trunk/applications/j2-admin/src/webapp/WEB-INF/view/selectors/category-edit-selector.vm Mon Nov 13 11:20:33 2006
@@ -0,0 +1,295 @@
+#*
+Copyright 2004 The Apache Software Foundation
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*#
+<div dojoType="dialog" id="AddCategoryDialog" bgColor="grey" bgOpacity="0.5" toggle="fade" toggleDuration="250">
+  <form onsubmit="return false;">
+  <input type="hidden" id='theNewCategory'>
+    <div id="AddCategoryPane" dojoType="ContentPane" label="Add Category" class="constraints">
+      <div style="height:21px; padding: 2px"><b>Category Name:&nbsp;</b><span id="categoryName"></span></div>
+      <div class="categoryTableDiv">
+      <table>
+      <tr>
+        <td>Category Name:</td>
+        <td><input id='jsCatName' type="text"></td>
+      </tr>
+      </table>
+      </div>
+      <div dojoType="LayoutContainer"
+                     widgetId="constraintsEditorBottom"
+                     minHeight="20"
+                     layoutAlign="bottom"
+                     style="padding: 4px; width: 100%; height: 30px">
+            <div dojoType="LayoutContainer"
+                 widgetId="constraintsEditorBottomLeft"
+                 minHeight="20"
+                 layoutAlign="left"
+                 style="padding: 4px; width: 18%; height: 30px">
+            </div>
+            <div dojoType="LayoutContainer"
+                 widgetId="constraintsEditorBottomRight"
+                 minHeight="20"
+                 layoutAlign="right"
+                 style="padding: 4px; border-right: thin inset grey; width: 78%; height: 30px">
+                <div class="buttonbox">
+                    <button dojoType="Button" onClick="dojo.widget.byId( 'AddCategoryDialog' ).hide();" >Cancel</button>
+                    <button dojoType="Button" onClick="addCategory();">OK</button>
+                </div>
+            </div>
+      </div>
+    </div>
+  </form>
+</div>
+#set ($cancelLink = $renderResponse.createRenderURL())
+$cancelLink.setPortletMode($MODE_VIEW)
+<!-- Search Form -->
+<form id='jsEditSelectorForm' onSubmit='return false;' method="post" style="height:250px; padding: 2px; margin:10px">
+<table>
+<tr>
+<td nowrap class="portlet-section-alternate" align="right">Categories:&nbsp;</td>
+<td nowrap>
+<select id='jsSelectCat' size='10' onChange='javascript:handleSelect();'>
+</select>
+<td nowrap class="portlet-section-alternate" align="right">Keywords:&nbsp;</td>
+<td nowrap>
+<input type="text" name="keywords" id="keywords" size="60" value="" class="portlet-form-field-label"/>    
+</tr>
+<tr>
+<td nowrap>
+<input type="submit" value="Add Category" onClick='javascript:handleAdd();'/>
+</td>
+<td nowrap>
+<input type="submit" value="Remove Category" onClick='javascript:handleRemove();'/>
+</td>
+</tr>
+<tr>
+<td nowrap class="portlet-section-alternate" align="right">Rows:&nbsp;</td>
+<td nowrap>         
+<input type="text" name="Rows" id="Rows" size="6" value="$!Rows" class="portlet-form-field-label"/>    
+</td>
+<td nowrap class="portlet-section-alternate" align="right">Columns:&nbsp;</td>
+<td nowrap>
+<input type="text" name="Columns" id="Columns" size="6" value="$!Columns" class="portlet-form-field-label"/>    
+</td>
+</tr>
+<tr>
+<td nowrap>
+<input type="submit" value="Save" onClick='javascript:handleSave();'/>
+<input type="submit" value="Cancel" onClick='javascript:handleCancel();'/>
+<input type="hidden" name='jsModifiedCats' id='jsModifiedCats' value=""/>
+<input type="hidden" name='jsModifiedKeys' id='jsModifiedKeys' value=""/>
+<input type="hidden" name='jsAddedCats' id='jsAddedCats' value=""/>
+<input type="hidden" name='jsAddedKeys' id='jsAddedKeys' value=""/>
+<input type="hidden" name='jsRemovedCats' id='jsRemovedCats' value=""/>
+<input type="hidden" name='jsCats' id='jsCats' value=""/>
+<input type="hidden" name='jspage' id='jspage' value="$jspage"/>
+</td>
+</tr>
+</table>
+</form>
+
+<script language="JavaScript" type="text/javascript">
+function JsCategory(name, keywords)
+{
+   this.name = name;
+   this.keywords = keywords;
+   this.state = "clean";
+}
+function saveOffKeywords()
+{
+   var keywords = document.getElementById('keywords');
+   if (window.jsLastIndex > -1)
+   {
+      if (jsCategories[window.jsLastIndex].keywords != keywords.value)
+      {
+        jsCategories[window.jsLastIndex].keywords = keywords.value; 
+        if (jsCategories[window.jsLastIndex].state != "new")
+        {
+           jsCategories[window.jsLastIndex].state = "modified";
+        }
+      }
+   }
+}
+function handleSelect()
+{
+   var catList = document.getElementById('jsSelectCat');
+   saveOffKeywords();
+   var keywords = document.getElementById('keywords');   
+   keywords.value = jsCategories[catList.selectedIndex].keywords;
+   window.jsLastIndex = catList.selectedIndex;
+}
+function handleAdd()
+{ 
+   dojo.widget.byId("AddCategoryDialog").show();   
+}
+function addCategory()
+{
+    saveOffKeywords();
+    var newIndex = jsCategories.length;
+    var catName = document.getElementById('jsCatName').value;    
+    catName = trim(catName);
+    if (catName.length > 0)
+    {    
+        // check for dups
+        for (i = 0; i < jsCategories.length; i++)
+        {
+           if (catName.toLowerCase() == jsCategories[i].name.toLowerCase())
+           {
+              dojo.widget.byId("AddCategoryDialog").hide();   
+              return;
+           }
+        }
+        jsCategories[newIndex] = new JsCategory(catName, "");
+        jsCategories[newIndex].state = "new";
+        var catList = document.getElementById('jsSelectCat');
+        var opt = new Option(catName, catName);
+        catList.add(opt, null); 
+    }
+    dojo.widget.byId("AddCategoryDialog").hide();   
+}
+function handleRemove()
+{
+    var catList = document.getElementById('jsSelectCat');
+    if (catList.selectedIndex != -1)
+    {
+        window.jsLastIndex = -1;
+        var keywords = document.getElementById('keywords');   
+        keywords.value = "";
+        jsRemoves[jsRemoves.length] = jsCategories[catList.selectedIndex].name;
+        // shift up
+        var temp = new Array();
+        var j = 0;
+        for (i = 0; i < jsCategories.length; i++)
+        {
+            if (i != catList.selectedIndex)
+            {
+                temp[j] = jsCategories[i];
+                j = j + 1;
+            }
+        }
+        delete jsCategories;
+        jsCategories = temp;
+        catList.remove(catList.selectedIndex);
+    }
+    
+}
+function handleSave()
+{
+   saveOffKeywords();
+   processDeletes(window.jsRemoves, 'jsRemovedCats');
+   var modifiedCats = document.getElementById("jsModifiedCats");  
+   var modifiedKeys = document.getElementById("jsModifiedKeys");  
+   var addedCats = document.getElementById("jsAddedCats");  
+   var addedKeys = document.getElementById("jsAddedKeys");  
+   var cats = document.getElementById("jsCats");
+   if (window.jsCategories.length == 0)
+   {
+       modifiedCats.value = "";
+       modifiedKeys.value = "";
+       addedCats.value = "";
+       addedKeys.value = "";
+       cats.value = "";      
+   }
+   else
+   {
+       var newCount = 0;
+       var modCount = 0;
+       for (i = 0; i < window.jsCategories.length; i++)
+       {
+          if (i == 0)
+          {
+              cats.value = window.jsCategories[i].name;
+          }
+          else
+          {
+              cats.value = cats.value + "," + window.jsCategories[i].name;
+          }
+          if (window.jsCategories[i].state == "new")
+          {
+             if (newCount == 0)
+             {
+                addedCats.value = window.jsCategories[i].name;
+                addedKeys.value = window.jsCategories[i].keywords;
+             }
+             else
+             {
+                addedCats.value = addedCats.value + "," + window.jsCategories[i].name;
+                addedKeys.value = addedKeys.value + "|" + window.jsCategories[i].keywords;
+             }
+             newCount = newCount + 1;
+          }
+          else if (window.jsCategories[i].state == "modified")
+          {
+             if (modCount == 0)
+             {
+                modifiedCats.value = window.jsCategories[i].name;
+                modifiedKeys.value = window.jsCategories[i].keywords;
+             }
+             else
+             {
+                modifiedCats.value = modifiedCats.value + "," + window.jsCategories[i].name;
+                modifiedKeys.value = modifiedKeys.value + "|" + window.jsCategories[i].keywords;
+             }
+             modCount = modCount + 1;
+          }
+       }
+
+   }
+   var editForm = document.getElementById('jsEditSelectorForm');
+   editForm.action = "$renderResponse.createActionURL()";
+   editForm.submit();
+}
+function handleCancel()
+{
+   var editForm = document.getElementById('jsEditSelectorForm');
+   editForm.action = "$cancelLink";
+   editForm.submit();
+}
+
+function processDeletes(arr, fieldName)
+{
+   var inputField = document.getElementById(fieldName);  
+   if (arr.length == 0)
+   {
+       inputField.value = "";
+       return;
+   }
+   var temp = arr[0]; 
+   for (i = 1; i < arr.length; i++)
+   {
+      temp = temp + "," + arr[i];               
+   }
+   inputField.value = temp;
+}
+function trim(str)
+{
+   return str.replace(/(^\s*|\s*$)/g, "");
+}
+function initializeEditPortletSelector()
+{
+    #foreach ( $cat in $categories)
+    #set ($icount = $velocityCount - 1)   
+    jsCategories[$icount] = new JsCategory("$cat.Name", "$!cat.Keywords");
+    #end
+    var catList = document.getElementById('jsSelectCat');
+    for (i = 0; i < jsCategories.length; i++)
+    {
+       catList.options[i] = new Option(jsCategories[i].name,jsCategories[i].name);
+    }
+}
+window.jsCategories = new Array();
+window.jsRemoves = new Array();
+window.jsLastIndex = -1;
+dojo.addOnLoad( window.initializeEditPortletSelector );
+</script>

Added: portals/jetspeed-2/trunk/applications/j2-admin/src/webapp/WEB-INF/view/selectors/category-portlet-header.vm
URL: http://svn.apache.org/viewvc/portals/jetspeed-2/trunk/applications/j2-admin/src/webapp/WEB-INF/view/selectors/category-portlet-header.vm?view=auto&rev=474445
==============================================================================
--- portals/jetspeed-2/trunk/applications/j2-admin/src/webapp/WEB-INF/view/selectors/category-portlet-header.vm (added)
+++ portals/jetspeed-2/trunk/applications/j2-admin/src/webapp/WEB-INF/view/selectors/category-portlet-header.vm Mon Nov 13 11:20:33 2006
@@ -0,0 +1,121 @@
+<style>
+.portlet-selector-title
+{    
+    font-size: 9pt;
+        color: blue;
+}
+.portlet-selector-text
+{    
+    font-size: 8pt;
+        color: green;
+}
+.dojoDialog {
+    background : #eee;
+    border : 1px solid #999;
+    -moz-border-radius : 5px;
+    padding : 4px;
+    width: 420px;
+}
+
+.buttonbox {
+    display: block;
+    text-align: center;
+}
+.buttonbox .dojoButton {
+    float: left;
+    margin-right: 10px;
+    margin-bottom: 6px;
+}
+
+.buttonboxright {
+    display: block;
+    text-align: center;
+}
+.buttonboxright .dojoButton {
+    float: right;
+    margin-left: 10px;
+    margin-bottom: 6px;
+}
+
+.constraints {
+    font-family:Lucida Grande, Verdana;
+    font-size:0.8em;
+    width:100%;
+    border:1px solid #ccc;
+    cursor:default;
+}
+
+#mainTabContainer {
+    width: 100%;
+    height: 304px;
+}
+
+* html div.tableContainer {    /* IE only hack */
+    width: 95%;
+    border: 1px solid #ccc;
+    height: 285px;
+    overflow-x: hidden;
+    overflow-y: auto;
+}
+
+.constraints td,
+.constraints th{
+    border-right: 1px solid #999;
+    padding: 2px;
+    font-weight: normal;
+    font-size: small;
+}
+
+.constraints thead td, .constraints thead th {
+    background-image:url(/j2-admin/images/st-head.gif);
+    background-repeat:no-repeat;
+    background-position:top right;
+    font-size: small;
+    font-weight: bold;
+    text-align: left;
+}
+.constraints thead td.selectedUp, .constraints thead th.selectedUp {
+    background-image:url(/j2-admin/images/st-headup.gif);
+}
+.constraints thead td.selectedDown, .constraints thead th.selectedDown {
+    background-image:url(/j2-admin/images/st-headdown.gif);
+}
+
+* html div.tableContainer table thead tr td,
+* html div.tableContainer table thead tr th{
+    /* IE Only hacks */
+    position:relative;
+    top:expression(dojo.html.getFirstAncestorByTag(this,'table').parentNode.scrollTop-2);
+}
+
+
+html>body tbody.scrollContent {
+    height: 262px;
+    overflow-x: hidden;
+    overflow-y: auto;
+}
+
+tbody.scrollContent td, tbody.scrollContent tr td {
+    background: #FFF;
+    padding: 2px;
+}
+
+tbody.scrollContent tr.alternateRow td {
+    background: #e3edfa;
+    padding: 2px;
+}
+
+tbody.scrollContent tr.selected td {
+    background: yellow;
+    padding: 2px;
+}
+tbody.scrollContent tr:hover td {
+    background: #a6c2e7;
+    padding: 2px;
+}
+tbody.scrollContent tr.selected:hover td {
+    background: #ff3;
+    padding: 2px;
+}
+
+</style>

Added: portals/jetspeed-2/trunk/applications/j2-admin/src/webapp/WEB-INF/view/selectors/category-portlet-selector.vm
URL: http://svn.apache.org/viewvc/portals/jetspeed-2/trunk/applications/j2-admin/src/webapp/WEB-INF/view/selectors/category-portlet-selector.vm?view=auto&rev=474445
==============================================================================
--- portals/jetspeed-2/trunk/applications/j2-admin/src/webapp/WEB-INF/view/selectors/category-portlet-selector.vm (added)
+++ portals/jetspeed-2/trunk/applications/j2-admin/src/webapp/WEB-INF/view/selectors/category-portlet-selector.vm Mon Nov 13 11:20:33 2006
@@ -0,0 +1,274 @@
+#*
+Copyright 2004 The Apache Software Foundation
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*#
+<div dojoType="dialog" id="AddPortletDialog" bgColor="grey" bgOpacity="0.5" toggle="fade" toggleDuration="250">
+  <form onsubmit="return false;">
+  <input type="hidden" id='theNewPortlet'>
+    <div id="AddPortletPane" dojoType="ContentPane" label="Portlet Info" class="constraints">
+      <div style="height:21px; padding: 2px"><b>Portlet Name:&nbsp;</b><span id="portletDefName"></span></div>
+      <div class="portletTableDiv">
+      <table>
+      <tr>
+        <td>Pref one:</td>
+        <td><input type="text" value="UNDER CONSTRUCTION!"></td>
+      </tr>
+      <tr>
+        <td>Pref Two:</td>
+        <td><input type="text"></td>
+      </tr>
+      </table>
+      </div>
+      <div dojoType="LayoutContainer"
+                     widgetId="constraintsEditorBottom"
+                     minHeight="20"
+                     layoutAlign="bottom"
+                     style="padding: 4px; width: 100%; height: 30px">
+            <div dojoType="LayoutContainer"
+                 widgetId="constraintsEditorBottomLeft"
+                 minHeight="20"
+                 layoutAlign="left"
+                 style="padding: 4px; width: 18%; height: 30px">
+            </div>
+            <div dojoType="LayoutContainer"
+                 widgetId="constraintsEditorBottomRight"
+                 minHeight="20"
+                 layoutAlign="right"
+                 style="padding: 4px; border-right: thin inset grey; width: 78%; height: 30px">
+                <div class="buttonbox">
+                    <button dojoType="Button" onClick="dojo.widget.byId( 'AddPortletDialog' ).hide();" >Cancel</button>
+                    <button dojoType="Button" onClick="addPortletToPage();">OK</button>
+                </div>
+            </div>
+      </div>
+    </div>
+  </form>
+</div>
+#set ($refreshLink = $renderResponse.createActionURL())
+$refreshLink.setParameter("reset","true")
+<!-- Search Form -->
+<form id='jsSelectorForm' onSubmit='return false;' style="height:21px; padding: 2px; margin:10px">
+<table>
+<tr>
+<td nowrap class="portlet-section-alternate" align="right">Search for portlets:&nbsp;</td>
+<td nowrap>
+<input type="text" name="searchString" id="searchString" size="40" value="$!searchString" class="portlet-form-field-label">    
+<input type="submit" value="Search" onClick='javascript:performSearch();'>
+<input type="submit" value="Refresh" onClick="javascript:performRefresh()">
+</td>
+</tr>
+</table>
+</form>
+
+<table id='ctable' cellspacing="5" cellpadding="5" style="margin:10px">
+</table>
+<table id='ptable' border="1" cellspacing="10" cellpadding="1">
+</table>
+<script language="JavaScript" type="text/javascript">
+function PortletInfo(name, display, desc, image)
+{
+  this.name = name;
+  this.display = display;
+  this.desc = desc;
+  this.image = image;
+  this.count = 0;
+}
+function CategoryInfo(name)
+{
+   this.name = name;
+   this.portlets = new Array();
+}
+function performRefresh()
+{
+   var searchForm = document.getElementById('jsSelectorForm');
+   searchForm.action = "$refreshLink";
+   searchForm.submit();
+}
+function displayPortlets(cat)
+{                                             
+   if (cat == null)
+   {
+      cat = "All"; // TODO: localize
+   }
+   var currentPortlets = selectorPortlets;
+
+   for (i=0; i < this.categories.length; i++)
+   {
+       if (this.categories[i].name == cat)
+       {
+          catPortlets = this.categories[i].portlets;
+          break;
+       }
+   }
+    var ptable = document.getElementById('ptable');
+    while (ptable.hasChildNodes())
+    {
+      ptable.removeChild(ptable.lastChild);
+    }
+    var portletCount = 0;
+    var rowCount = 0;
+    var columns = $Columns;
+    var outerRow = null;
+    // TODO: rows
+    for (iz = 0; iz <  catPortlets.length; iz++)
+    {         
+         var cellIndex = portletCount % columns;
+         if (cellIndex == 0)
+         {
+             outerRow = ptable.insertRow(rowCount);
+             rowCount = rowCount + 1;
+         }
+         var cell = outerRow.insertCell(cellIndex);            
+         var pt1 = document.createElement("table");
+         var row0 = pt1.insertRow(0);
+         var c0 = row0.insertCell(0);
+         var c1 = row0.insertCell(1);
+         c1.setAttribute("style","font-size: 9pt; color: blue;");
+         //c1.setAttribute("class","portlet-selector-title");
+         var image = new Image();
+
+         image.src = currentPortlets[catPortlets[iz]].image;
+
+         c0.appendChild(image);
+         c1.innerHTML = currentPortlets[catPortlets[iz]].display;
+        
+         var pt2 = document.createElement("table");
+         var row1 = pt2.insertRow(0);
+         c0 = row1.insertCell(0);
+         c0.setAttribute("style","font-size: 8pt; color: green;");
+         //c0.setAttribute("class","portlet-selector-text");
+         c0.innerHTML = currentPortlets[catPortlets[iz]].desc;
+         
+         var pt3 = document.createElement("table");
+         var row3 = pt3.insertRow(0);
+         c0 = row3.insertCell(0);
+         c0.setAttribute("style","font-size: 8pt; color: blue;");
+         var a = document.createElement("a");
+         a.innerHTML = "Add"; // TODO: localize
+         a.href = "javascript:displayAddPortletDialog(" + "\"" + currentPortlets[catPortlets[iz]].name + "\"" + ")";
+         c0.appendChild(a);
+         c1 = row3.insertCell(1);
+         c1.innerHTML = "Count: " + currentPortlets[catPortlets[iz]].count; // TODO: localize
+         c1.setAttribute("style","font-size: 8pt; color: blue;");
+         c1.setAttribute("id", currentPortlets[catPortlets[iz]].name);
+
+         cell.appendChild(pt1);
+         cell.appendChild(pt2);
+         cell.appendChild(pt3);
+         portletCount = portletCount + 1;
+    }
+    highlightSelectedCategory(cat);
+}
+function displayAddPortletDialog(portletName)
+{
+   var nameSpan = dojo.byId( "portletDefName" );   
+   var theNewPortlet = document.getElementById('theNewPortlet');
+   theNewPortlet.value = portletName;
+   nameSpan.innerHTML = selectorPortlets[portletName].display;   
+   dojo.widget.byId("AddPortletDialog").show();   
+}
+function addPortletToPage()
+{                                                      
+   var theNewPortlet = document.getElementById('theNewPortlet').value;
+   selectorPortlets[theNewPortlet].count = selectorPortlets[theNewPortlet].count + 1;
+   var cell = document.getElementById(theNewPortlet);
+   if (cell != null)
+   {
+      cell.innerHTML = "Count: " + selectorPortlets[theNewPortlet].count; // TODO: localize
+   }
+   dojo.widget.byId("AddPortletDialog").hide();   
+   var portletDef = new jetspeed.om.PortletDef( theNewPortlet );   
+   var pagePath = jetspeed.url.basePortalUrl() + jetspeed.url.path.AJAX_API + "$jspage";
+   jetspeed.addNewPortletDefinition( portletDef, null, pagePath );
+}
+//   dojo.lang.setTimeout(window, "performSearch2", 20);
+function performSearch()
+{
+   var ix = this.categories.length-1;
+   this.categories[ix].portlets = new Array();    
+   var searchString = document.getElementById("searchString").value;
+   jetspeed.searchForPortletDefinitions(searchString, searchFinishedFunction);
+}
+function searchFinishedFunction(domainObjects, portletList)
+{
+   var searchIndex = categories.length-1; 
+   for ( var i = 0 ; i < portletList.length ; i++ )
+   {
+       categories[searchIndex].portlets[i] = portletList[i].portletName;
+   }
+   displayPortlets("Search");   
+}
+function highlightSelectedCategory(cat)
+{
+    var ctable = document.getElementById('ctable');
+    var row = ctable.rows[0];
+    for (i = 0; i < row.childNodes.length; i++)
+    {
+        var a = row.childNodes[i].firstChild;
+        var atext = a.innerHTML;
+        var style = row.childNodes[i].firstChild.attributes.getNamedItem("style");
+        if (atext == cat)
+             a.setAttribute("style", "font-weight:bold");
+        else
+             a.setAttribute("style", "font-weight:normal");
+    }
+}
+function initializePortletSelector()
+{
+    categories[0] = new CategoryInfo("All");
+    #foreach ( $portlet in $portlets )
+    selectorPortlets["$portlet.Name"] = new PortletInfo("$portlet.Name", "$!portlet.DisplayName", "$!portlet.Description", "$!portlet.Image");
+    #set ($count = $velocityCount - 1)
+    categories[0].portlets[$count] = "$portlet.Name";
+    #end
+    #foreach ( $cat in $categories)
+    categories[$velocityCount] = new CategoryInfo("$cat.Name");
+    #set ($icount = $velocityCount)
+    #foreach ( $portlet in $cat.Portlets )
+    #set ($jcount = $velocityCount - 1)
+    categories[$icount].portlets[$jcount] = "$portlet.Name";
+    #end
+    #end
+    var ctable = document.getElementById('ctable');
+    var row = ctable.insertRow(0);
+    for(i=0; i<categories.length; i++)
+    {    
+         var c1 = row.insertCell(i);
+         var a = document.createElement("a");
+         a.innerHTML = categories[i].name;
+         a.href = "javascript:displayPortlets(" + "\"" + categories[i].name + "\"" + ")";
+         a.setAttribute("style", "font-weight:normal");
+         c1.appendChild(a);
+    }
+     categories[categories.length] = new CategoryInfo("Search"); // TODO: localize
+     var c1 = row.insertCell(i);
+     var a = document.createElement("a");
+     a.innerHTML = categories[i].name;
+     a.href = "javascript:displayPortlets(" + "\"" + categories[i].name + "\"" + ")";
+     a.setAttribute("style", "font-weight:normal");
+     c1.appendChild(a);
+     var c2 = row.insertCell(i+1);
+     var a2 = document.createElement("a");
+     a2.innerHTML = "Back to Page";  // TODO: localize
+     // TODO: determine if JETSPEED or DESKTOP 
+     a2.href = jetspeed.url.basePortalUrl() + jetspeed.url.path.JETSPEED + "/portal" + "$jspage";
+     a2.setAttribute("style", "font-weight:normal");
+     c2.appendChild(a2);
+
+     displayPortlets("All");
+}
+window.selectorPortlets = new Array();
+window.categories = new Array();
+dojo.addOnLoad( window.initializePortletSelector );
+</script>

Added: portals/jetspeed-2/trunk/applications/j2-admin/src/webapp/css/category-portlet-selector.css
URL: http://svn.apache.org/viewvc/portals/jetspeed-2/trunk/applications/j2-admin/src/webapp/css/category-portlet-selector.css?view=auto&rev=474445
==============================================================================
--- portals/jetspeed-2/trunk/applications/j2-admin/src/webapp/css/category-portlet-selector.css (added)
+++ portals/jetspeed-2/trunk/applications/j2-admin/src/webapp/css/category-portlet-selector.css Mon Nov 13 11:20:33 2006
@@ -0,0 +1,108 @@
+        .dojoDialog {
+            background : #eee;
+            border : 1px solid #999;
+            -moz-border-radius : 5px;
+            padding : 4px;
+            width: 920px;
+        }
+
+        .buttonbox {
+            display: block;
+            text-align: center;
+        }
+        .buttonbox .dojoButton {
+            float: left;
+            margin-right: 10px;
+            margin-bottom: 6px;
+		}
+
+        .buttonboxright {
+            display: block;
+            text-align: center;
+        }
+        .buttonboxright .dojoButton {
+            float: right;
+            margin-left: 10px;
+            margin-bottom: 6px;
+		}
+
+		.constraints {
+			font-family:Lucida Grande, Verdana;
+			font-size:0.8em;
+			width:100%;
+			border:1px solid #ccc;
+			cursor:default;
+		}
+
+        #mainTabContainer {
+            width: 100%;
+            height: 304px;
+        }
+
+		* html div.tableContainer {	/* IE only hack */
+			width: 95%;
+			border: 1px solid #ccc;
+			height: 285px;
+			overflow-x: hidden;
+			overflow-y: auto;
+		}
+
+		.constraints td,
+		.constraints th{
+			border-right: 1px solid #999;
+			padding: 2px;
+			font-weight: normal;
+            font-size: small;
+		}
+		
+		.constraints thead td, .constraints thead th {
+			background-image:url(/j2-admin/images/st-head.gif);
+			background-repeat:no-repeat;
+			background-position:top right;
+			font-size: small;
+            font-weight: bold;
+            text-align: left;
+		}
+		.constraints thead td.selectedUp, .constraints thead th.selectedUp {
+			background-image:url(/j2-admin/images/st-headup.gif);
+		}
+		.constraints thead td.selectedDown, .constraints thead th.selectedDown {
+			background-image:url(/j2-admin/images/st-headdown.gif);
+		}
+		
+		* html div.tableContainer table thead tr td,
+		* html div.tableContainer table thead tr th{
+			/* IE Only hacks */
+			position:relative;
+			top:expression(dojo.html.getFirstAncestorByTag(this,'table').parentNode.scrollTop-2);
+		}
+		
+        
+		html>body tbody.scrollContent {
+			height: 262px;
+			overflow-x: hidden;
+			overflow-y: auto;
+		}
+
+		tbody.scrollContent td, tbody.scrollContent tr td {
+			background: #FFF;
+			padding: 2px;
+		}
+
+		tbody.scrollContent tr.alternateRow td {
+			background: #e3edfa;
+			padding: 2px;
+		}
+
+		tbody.scrollContent tr.selected td {
+			background: yellow;
+			padding: 2px;
+		}
+		tbody.scrollContent tr:hover td {
+			background: #a6c2e7;
+			padding: 2px;
+		}
+		tbody.scrollContent tr.selected:hover td {
+			background: #ff3;
+			padding: 2px;
+		}



---------------------------------------------------------------------
To unsubscribe, e-mail: jetspeed-dev-unsubscribe@portals.apache.org
For additional commands, e-mail: jetspeed-dev-help@portals.apache.org