You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jspwiki.apache.org by aj...@apache.org on 2008/12/23 01:45:08 UTC

svn commit: r728835 - in /incubator/jspwiki/trunk: ./ etc/i18n/templates/ src/com/ecyrd/jspwiki/ src/com/ecyrd/jspwiki/action/ src/com/ecyrd/jspwiki/attachment/ src/com/ecyrd/jspwiki/auth/authorize/ src/com/ecyrd/jspwiki/preferences/ src/com/ecyrd/jspw...

Author: ajaquith
Date: Mon Dec 22 16:45:07 2008
New Revision: 728835

URL: http://svn.apache.org/viewvc?rev=728835&view=rev
Log:
GroupActionBean finished, with unit tests. JSPs not yet. Fixed most of the ActionBean unit tests that were failing elsewhere in the .action package; down to just 4. Significant overhaul of the .action package.html Javadoc.

Added:
    incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/ui/stripes/LineDelimitedTypeConverter.java
Modified:
    incubator/jspwiki/trunk/ChangeLog
    incubator/jspwiki/trunk/etc/i18n/templates/default.properties
    incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/Release.java
    incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/AbstractActionBean.java
    incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/AbstractPageActionBean.java
    incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/AdministerProfilesActionBean.java
    incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/EditActionBean.java
    incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/GroupActionBean.java
    incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/RenameActionBean.java
    incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/UserPreferencesActionBean.java
    incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/UserProfileActionBean.java
    incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/ViewActionBean.java
    incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/package.html
    incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/attachment/Attachment.java
    incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/attachment/package.html
    incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/auth/authorize/GroupManager.java
    incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/preferences/Preferences.java
    incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/providers/FileSystemProvider.java
    incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/ui/stripes/GroupTypeConverter.java
    incubator/jspwiki/trunk/src/webdocs/Preview.jsp
    incubator/jspwiki/trunk/tests/com/ecyrd/jspwiki/action/GroupActionBeanTest.java
    incubator/jspwiki/trunk/tests/com/ecyrd/jspwiki/action/ViewActionBeanTest.java

Modified: incubator/jspwiki/trunk/ChangeLog
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/ChangeLog?rev=728835&r1=728834&r2=728835&view=diff
==============================================================================
--- incubator/jspwiki/trunk/ChangeLog (original)
+++ incubator/jspwiki/trunk/ChangeLog Mon Dec 22 16:45:07 2008
@@ -1,3 +1,11 @@
+2008-12-22  Andrew Jaquith <ajaquith AT apache DOT org>
+
+        * 3.0.0-svn-39
+
+        * GroupActionBean finished, with unit tests. JSPs not yet. Fixed most of
+        the ActionBean unit tests that were failing elsewhere in the .action
+        package; down to just 4. Significant overhaul of the .action package.html Javadoc.
+
 2008-12-21  Janne Jalkanen <ja...@apache.org>
 
         * 3.0.0-svn-38

Modified: incubator/jspwiki/trunk/etc/i18n/templates/default.properties
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/etc/i18n/templates/default.properties?rev=728835&r1=728834&r2=728835&view=diff
==============================================================================
--- incubator/jspwiki/trunk/etc/i18n/templates/default.properties (original)
+++ incubator/jspwiki/trunk/etc/i18n/templates/default.properties Mon Dec 22 16:45:07 2008
@@ -99,6 +99,7 @@
     called <strong>{0}</strong>. You can specify this \
     name in page access control lists (ACLs).
 editgroup.submit.save=Save group
+editgroup.illegalname=The group name {1} is illegal. Choose another.
 
 # Favorites.jsp
 

Modified: incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/Release.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/Release.java?rev=728835&r1=728834&r2=728835&view=diff
==============================================================================
--- incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/Release.java (original)
+++ incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/Release.java Mon Dec 22 16:45:07 2008
@@ -77,7 +77,7 @@
      *  <p>
      *  If the build identifier is empty, it is not added.
      */
-    public static final String     BUILD         = "38";
+    public static final String     BUILD         = "39";
     
     /**
      *  This is the generic version string you should use

Modified: incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/AbstractActionBean.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/AbstractActionBean.java?rev=728835&r1=728834&r2=728835&view=diff
==============================================================================
--- incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/AbstractActionBean.java (original)
+++ incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/AbstractActionBean.java Mon Dec 22 16:45:07 2008
@@ -13,7 +13,7 @@
  * 
  * @author Andrew Jaquith
  */
-public abstract class AbstractActionBean implements WikiActionBean
+abstract class AbstractActionBean implements WikiActionBean
 {
     private WikiActionBeanContext m_context = null;
 
@@ -33,7 +33,7 @@
      * 
      * @throws IllegalStateException
      */
-    public WikiActionBeanContext getContext()
+    public final WikiActionBeanContext getContext()
     {
         return m_context;
     }
@@ -42,7 +42,7 @@
      * Sets the Stripes ActionBeanContext associated with this WikiContext. It
      * will also update the cached HttpRequest.
      */
-    public void setContext( ActionBeanContext context )
+    public final void setContext( ActionBeanContext context )
     {
         m_context = ((WikiActionBeanContext) context);
     }

Modified: incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/AbstractPageActionBean.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/AbstractPageActionBean.java?rev=728835&r1=728834&r2=728835&view=diff
==============================================================================
--- incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/AbstractPageActionBean.java (original)
+++ incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/AbstractPageActionBean.java Mon Dec 22 16:45:07 2008
@@ -1,17 +1,20 @@
 package com.ecyrd.jspwiki.action;
 
-import org.apache.jspwiki.api.WikiPage;
-
 import net.sourceforge.stripes.validation.Validate;
 
+import org.apache.jspwiki.api.WikiPage;
 
 /**
- * Abstract WikiActionBean subclass used by all ActionBeans that use and process
- * WikiPages using the <code>page</code> request parameter. In particular,
- * this subclass contains special processing logic that sets the page property
- * of the underlying {@link com.ecyrd.jspwiki.ui.stripes.WikiActionBeanContext}
- * when the {@link #setPage(WikiPage)} method is called by the Stripes
- * controller.
+ * Abstract {@link WikiActionBean} subclass used by all ActionBeans that use and
+ * process {@link org.apache.jspwiki.api.WikiPage} objects bound to the
+ * <code>page</code> request parameter. In particular, this subclass contains
+ * special processing logic that ensures that, the <code>page</code>
+ * properties of this object and its related
+ * {@link com.ecyrd.jspwiki.WikiContext} are set to the same value. When
+ * {@link #setPage(WikiPage)} is called by, for example, the Stripes controller,
+ * the underlying
+ * {@link com.ecyrd.jspwiki.ui.stripes.WikiActionBeanContext#setPage(WikiPage)}
+ * method is called also.
  */
 public class AbstractPageActionBean extends AbstractActionBean
 {

Modified: incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/AdministerProfilesActionBean.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/AdministerProfilesActionBean.java?rev=728835&r1=728834&r2=728835&view=diff
==============================================================================
--- incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/AdministerProfilesActionBean.java (original)
+++ incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/AdministerProfilesActionBean.java Mon Dec 22 16:45:07 2008
@@ -103,6 +103,6 @@
             }
         }
 
-        return new RedirectResolution( "/AdministerProfiles.jsp" );
+        return new RedirectResolution( AdministerProfilesActionBean.class );
     }
 }

Modified: incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/EditActionBean.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/EditActionBean.java?rev=728835&r1=728834&r2=728835&view=diff
==============================================================================
--- incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/EditActionBean.java (original)
+++ incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/EditActionBean.java Mon Dec 22 16:45:07 2008
@@ -1,26 +1,33 @@
 package com.ecyrd.jspwiki.action;
 
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
 import net.sourceforge.stripes.action.*;
 
+import com.ecyrd.jspwiki.WikiContext;
 import com.ecyrd.jspwiki.auth.permissions.PagePermission;
+import com.ecyrd.jspwiki.filters.SpamFilter;
+import com.ecyrd.jspwiki.ui.EditorManager;
 import com.ecyrd.jspwiki.ui.stripes.HandlerPermission;
 import com.ecyrd.jspwiki.ui.stripes.WikiRequestContext;
 
-@HttpCache(allow=false)
-@UrlBinding("/Edit.jsp")
+@HttpCache( allow = false )
+@UrlBinding( "/Edit.jsp" )
 public class EditActionBean extends AbstractPageActionBean
 {
     @DefaultHandler
-    @HandlesEvent("edit")
-    @HandlerPermission(permissionClass=PagePermission.class, target="${page.qualifiedName}", actions=PagePermission.EDIT_ACTION)
-    @WikiRequestContext("edit")
+    @HandlesEvent( "edit" )
+    @HandlerPermission( permissionClass = PagePermission.class, target = "${page.qualifiedName}", actions = PagePermission.EDIT_ACTION )
+    @WikiRequestContext( "edit" )
     public Resolution edit()
     {
         return null;
     }
-    
+
     /**
-     * Event that extracts the current state of the edited page and forwards the user to the previewer JSP.
+     * Event that extracts the current state of the edited page from the HTTP
+     * session and redirects the user to the previewer JSP.
      * 
      * @return a forward resolution back to the preview page.
      */
@@ -29,17 +36,27 @@
     @WikiRequestContext( "preview" )
     public Resolution preview()
     {
+        WikiContext context = getContext();
+        HttpServletRequest request = context.getHttpRequest();
+        HttpSession session = request.getSession();
+
+        request.setAttribute( EditorManager.ATTR_EDITEDTEXT, session.getAttribute( EditorManager.REQ_EDITEDTEXT ) );
+
+        String lastchange = SpamFilter.getSpamHash( context.getPage(), request );
+        request.setAttribute( "lastchange", lastchange );
+
         return new ForwardResolution( "/Preview.jsp" );
     }
 
     /**
-     * Event that diffs the current state of the edited page and forwards the user to the diff JSP.
+     * Event that diffs the current state of the edited page and forwards the
+     * user to the diff JSP.
      * 
      * @return a forward resolution back to the preview page.
      */
-    @WikiRequestContext("diff")
-    @HandlesEvent("diff")
-    @HandlerPermission(permissionClass=PagePermission.class, target="${page.qualifiedName}", actions=PagePermission.VIEW_ACTION)
+    @WikiRequestContext( "diff" )
+    @HandlesEvent( "diff" )
+    @HandlerPermission( permissionClass = PagePermission.class, target = "${page.qualifiedName}", actions = PagePermission.VIEW_ACTION )
     public Resolution diff()
     {
         return new ForwardResolution( "/Diff.jsp" );

Modified: incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/GroupActionBean.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/GroupActionBean.java?rev=728835&r1=728834&r2=728835&view=diff
==============================================================================
--- incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/GroupActionBean.java (original)
+++ incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/GroupActionBean.java Mon Dec 22 16:45:07 2008
@@ -15,21 +15,92 @@
 import com.ecyrd.jspwiki.auth.authorize.Role;
 import com.ecyrd.jspwiki.auth.permissions.GroupPermission;
 import com.ecyrd.jspwiki.auth.permissions.WikiPermission;
+import com.ecyrd.jspwiki.ui.stripes.LineDelimitedTypeConverter;
 import com.ecyrd.jspwiki.ui.stripes.HandlerPermission;
 import com.ecyrd.jspwiki.ui.stripes.WikiRequestContext;
 
+/**
+ * <p>
+ * Views, creates, modifies and deletes
+ * {@link com.ecyrd.jspwiki.auth.authorize.Group} objects. The event handler
+ * methods are the following:
+ * </p>
+ * <ul>
+ * <li>{@link #create()} - redirects to the <code>/CreateGroup.jsp</code>
+ * display page</li>
+ * <li>{@link #view()} - views a Group</li>
+ * <li>{@link #save()} - saves an existing Group</li>
+ * <li>{@link #save()} - saves a new Group</li>
+ * <li>{@link #delete()} - deletes a Group</li>
+ * </ul>
+ * <p>
+ * The Group that the <code>view</code>, <code>save</code> and
+ * <code>delete</code> event handler methods operate on is set by
+ * {@link #setGroup(Group)}. Normally, this is set automatically by the Stripes
+ * controller, which extracts the parameter <code>group</code> from the HTTP
+ * request and converts it into a Group via
+ * {@link com.ecyrd.jspwiki.ui.stripes.GroupTypeConverter}.
+ * </p>
+ * <p>
+ * All of the event handlers require the user to possess an appropriate
+ * {@link com.ecyrd.jspwiki.auth.permissions.GroupPermission} to execute. See
+ * each handler method's {@link com.ecyrd.jspwiki.ui.stripes.HandlerPermission}
+ * for details.
+ * </p>
+ */
 @UrlBinding( "/Group.action" )
 public class GroupActionBean extends AbstractActionBean
 {
     private Group m_group = null;
 
     private List<Principal> m_members = new ArrayList<Principal>();
-    
+
+    /**
+     * Forwards the user to the <code>/CreateGroup.jsp</code> display JSP.
+     * 
+     * @return {@link net.sourceforge.stripes.action.ForwardResolution} to the
+     *         display JSP
+     */
+    @HandlesEvent( "create" )
+    @HandlerPermission( permissionClass = WikiPermission.class, target = "*", actions = WikiPermission.CREATE_GROUPS_ACTION )
+    @WikiRequestContext( "createGroup" )
+    public Resolution create()
+    {
+        return new ForwardResolution( "/CreateGroup.jsp" );
+    }
+
+    /**
+     * Handler method for that deletes the group supplied by {@link #getGroup()}.
+     * If the GroupManager throws a WikiSecurityException because it cannot
+     * delete the group for some reason, the exception is re-thrown as a
+     * StripesRuntimeException. <code>/Group.jsp</code>.
+     * 
+     * @throws StripesRuntimeException if the group cannot be deleted for any
+     *             reason
+     */
+    @HandlesEvent( "delete" )
+    @HandlerPermission( permissionClass = GroupPermission.class, target = "${group.name}", actions = GroupPermission.DELETE_ACTION )
+    @WikiRequestContext( "deleteGroup" )
+    public Resolution delete() throws StripesRuntimeException
+    {
+        try
+        {
+            WikiEngine engine = getContext().getEngine();
+            GroupManager groupMgr = engine.getGroupManager();
+            groupMgr.removeGroup( m_group.getName() );
+        }
+        catch( WikiSecurityException e )
+        {
+            throw new StripesRuntimeException( e );
+        }
+        return new RedirectResolution( ViewActionBean.class );
+    }
+
     public Group getGroup()
     {
         return m_group;
     }
-    
+
     public List<Principal> getMembers()
     {
         return m_members;
@@ -40,6 +111,48 @@
         return(m_group.getCreated() == null);
     }
 
+    /**
+     * Saves an existing wiki Group. The members of the group to be saved are
+     * returned by the method {@link #getMembers()}; these members are usually
+     * set automatically by the
+     * {@link com.ecyrd.jspwiki.ui.stripes.PrincipalTypeConverter}.
+     * 
+     * @return {@link net.sourceforge.stripes.action.ForwardResolution} to the
+     *         view page.
+     * @throws WikiSecurityException if the group cannot be saved for any reason
+     */
+    @HandlesEvent( "save" )
+    @HandlerPermission( permissionClass = GroupPermission.class, target = "${group.name}", actions = GroupPermission.EDIT_ACTION )
+    @WikiRequestContext( "editGroup" )
+    public Resolution save() throws WikiSecurityException
+    {
+        GroupManager mgr = getContext().getEngine().getGroupManager();
+        mgr.setGroup( getContext().getWikiSession(), m_group );
+        return new RedirectResolution( GroupActionBean.class, "view" ).addParameter( "group", m_group.getName() );
+    }
+
+    /**
+     * Saves a new wiki Group. The members of the group to be saved are returned
+     * by the method {@link #getMembers()}; these members are usually set
+     * automatically by the
+     * {@link com.ecyrd.jspwiki.ui.stripes.PrincipalTypeConverter}. This method
+     * functions identically to {@link #save()} except that the
+     * {@link com.ecyrd.jspwiki.auth.permissions.GroupPermission} required to
+     * execute it is different.
+     * 
+     * @return {@link net.sourceforge.stripes.action.ForwardResolution} to the
+     *         view page.
+     * @throws WikiSecurityException if the group cannot be saved for any reason
+     */
+    @HandlesEvent( "saveNew" )
+    @HandlerPermission( permissionClass = WikiPermission.class, target = "*", actions = WikiPermission.CREATE_GROUPS_ACTION )
+    public Resolution saveNew() throws WikiSecurityException
+    {
+        GroupManager mgr = getContext().getEngine().getGroupManager();
+        mgr.setGroup( getContext().getWikiSession(), m_group );
+        return new RedirectResolution( GroupActionBean.class, "view" ).addParameter( "group", m_group.getName() );
+    }
+
     @Validate( required = true )
     public void setGroup( Group group )
     {
@@ -56,7 +169,7 @@
      * 
      * @param members the members, separated by carriage returns
      */
-    @Validate( required = true, converter = OneToManyTypeConverter.class )
+    @Validate( required = true, on = "save,saveNew,delete", converter = LineDelimitedTypeConverter.class )
     public void setMembers( List<Principal> members )
     {
         m_members = members;
@@ -67,76 +180,84 @@
         }
     }
 
-    @ValidationMethod( on = "save" )
+    @ValidationMethod( on = "save, saveNew" )
     public void validateBeforeSave( ValidationErrors errors )
     {
         // Name cannot be one of the restricted names either
         String name = m_group.getName();
         if( Role.isReservedName( name ) )
-            ;
-        {
-            errors.add( "group", new SimpleError( "The group name '" + name + "' is illegal. Choose another." ) );
-        }
-    }
-
-    /**
-     * Handler method for that deletes the group supplied by {@link #getGroup()}.
-     * If the GroupManager throws a WikiSecurityException because it cannot
-     * delete the group for some reason, the exception is re-thrown as a
-     * StripesRuntimeException. <code>/Group.jsp</code>.
-     * 
-     * @throws StripesRuntimeException if the group cannot be deleted for any
-     *             reason
-     */
-    @HandlesEvent( "delete" )
-    @HandlerPermission( permissionClass = GroupPermission.class, target = "${group.name}", actions = GroupPermission.DELETE_ACTION )
-    @WikiRequestContext( "deleteGroup" )
-    public Resolution delete() throws StripesRuntimeException
-    {
-        try
         {
-            WikiEngine engine = getContext().getEngine();
-            GroupManager groupMgr = engine.getGroupManager();
-            groupMgr.removeGroup( m_group.getName() );
-        }
-        catch( WikiSecurityException e )
-        {
-            throw new StripesRuntimeException( e );
+            errors.add( "group", new LocalizableError( "editgroup.illegalname" ) );
         }
-        return new RedirectResolution( ViewActionBean.class );
-    }
-
-    @HandlesEvent( "save" )
-    @HandlerPermission( permissionClass = GroupPermission.class, target = "${group.name}", actions = GroupPermission.EDIT_ACTION )
-    @WikiRequestContext( "editGroup" )
-    public Resolution save() throws WikiSecurityException
-    {
-        GroupManager mgr = getContext().getEngine().getGroupManager();
-        mgr.setGroup( getContext().getWikiSession(), m_group );
-        RedirectResolution r = new RedirectResolution( "/Group.jsp" );
-        r.addParameter( "group", m_group.getName() );
-        return r;
     }
 
-    @HandlesEvent( "create" )
-    @HandlerPermission( permissionClass = WikiPermission.class, target = WikiPermission.CREATE_GROUPS_ACTION, actions = "" )
-    @WikiRequestContext( "createGroup" )
-    public Resolution create()
-    {
-        return new ForwardResolution( "/CreateGroup.jsp" );
-    }
-    
     /**
      * Default handler method for "view" events that simply forwards the user to
-     * <code>/Group.jsp</code>.
+     * <code>/Group.jsp</code> if the group requested by the user exists. If
+     * not, it will redirect the user to the {@link #create()} event. If the
+     * group supplied by the user in the request was illegal -- which can be
+     * determined by the presence of a validation error
+     * {@link net.sourceforge.stripes.validation.LocalizableError} with key
+     * <code>editgroup.illegalname</code> -- this method forwards to the error
+     * page. Lastly, if {@link #getGroup()} returns <code>null</code> because
+     * the user supplied no <code>group</code> parameter, this method
+     * redirects to {@link #create()} with a suggested sample group name
+     * <code>Group<em>x</em></code>, where <em>x</em> is an integer
+     * value.
      */
     @DefaultHandler
+    @DontValidate
     @HandlesEvent( "view" )
     @HandlerPermission( permissionClass = GroupPermission.class, target = "${group.name}", actions = GroupPermission.VIEW_ACTION )
     @WikiRequestContext( "group" )
     public Resolution view()
     {
-        return new ForwardResolution( "/Group,jsp" );
-    }
+        GroupManager mgr = getContext().getEngine().getGroupManager();
+
+        // User supplied a group, and it already exists
+        if( m_group != null && mgr.findRole( m_group.getName() ) != null )
+        {
+            return new ForwardResolution( "/Group.jsp" );
+        }
 
+        // It is an error if we see LocalizableError with key
+        // editgroup.illegalname!
+        ValidationErrors errors = getContext().getValidationErrors();
+        List<ValidationError> fieldErrors = errors.get( "group" );
+        if( fieldErrors != null )
+        {
+            for( ValidationError fieldError : fieldErrors )
+            {
+                if( fieldError instanceof LocalizableError )
+                {
+                    LocalizableError error = (LocalizableError) fieldError;
+                    if( "editgroup.illegalname".equals( error.getMessageKey() ) )
+                    {
+                        return new ForwardResolution( ErrorActionBean.class );
+                    }
+                }
+            }
+        }
+
+        // User didn't bother to supply a group at all, so suggest one
+        if( m_group == null )
+        {
+            int suffix = 1;
+            while ( mgr.findRole( "Group" + suffix ) != null )
+            {
+                suffix++;
+            }
+            try
+            {
+                m_group = mgr.getGroup( "Group" + suffix, true );
+            }
+            catch( Exception e )
+            {
+                return new RedirectResolution( ErrorActionBean.class ).flash( this );
+            }
+        }
+
+        // Redirect to create-group page
+        return new RedirectResolution( GroupActionBean.class, "create" ).addParameter( "group", m_group.getName() );
+    }
 }

Modified: incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/RenameActionBean.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/RenameActionBean.java?rev=728835&r1=728834&r2=728835&view=diff
==============================================================================
--- incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/RenameActionBean.java (original)
+++ incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/RenameActionBean.java Mon Dec 22 16:45:07 2008
@@ -23,7 +23,7 @@
 
 /**
  * <p>
- * Renames a wiki page. This ActionBean parses and extracts two request
+ * Renames a {@link org.apache.jspwiki.api.WikiPage}. This ActionBean parses and extracts two request
  * parameters:
  * </p>
  * <h3>Request parameters</h3>
@@ -113,9 +113,8 @@
                   + request.getRemoteAddr() + " by " + request.getRemoteUser() );
         String renamedTo = engine.renamePage( getContext(), renameFrom, m_renameTo, m_changeReferences );
         log.info( "Page successfully renamed to '" + renamedTo + "'" );
-        RedirectResolution r = new RedirectResolution( ViewActionBean.class );
-        r.addParameter( "page", renamedTo );
-        return r;
+        
+        return new RedirectResolution( ViewActionBean.class ).addParameter( "page", renamedTo );
     }
 
     /**

Modified: incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/UserPreferencesActionBean.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/UserPreferencesActionBean.java?rev=728835&r1=728834&r2=728835&view=diff
==============================================================================
--- incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/UserPreferencesActionBean.java (original)
+++ incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/UserPreferencesActionBean.java Mon Dec 22 16:45:07 2008
@@ -63,7 +63,7 @@
     {
         HttpServletResponse response = getContext().getResponse();
         CookieAssertionLoginModule.clearUserCookie( response );
-        return new RedirectResolution( "/Logout.jsp" );
+        return new RedirectResolution( LoginActionBean.class, "logout" );
     }
 
     /**
@@ -87,12 +87,10 @@
         }
         if( m_redirect != null )
         {
-            RedirectResolution r = new RedirectResolution( ViewActionBean.class );
-            r.addParameter( "page", m_redirect );
             log.info( "Redirecting user to wiki page " + m_redirect );
-            return r;
+            return new RedirectResolution( ViewActionBean.class ).addParameter( "page", m_redirect );
         }
-        return new RedirectResolution( "/" );
+        return new RedirectResolution( ViewActionBean.class );
     }
 
     /**
@@ -105,7 +103,7 @@
     public Resolution editFavorites()
     {
         Principal principal = getContext().getCurrentUser();
-        return new RedirectResolution( "/Edit.jsp?" + principal.getName() + "Favorites" );
+        return new RedirectResolution( EditActionBean.class ).addParameter( "page", principal.getName() + "Favorites" );
     }
 
     /**

Modified: incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/UserProfileActionBean.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/UserProfileActionBean.java?rev=728835&r1=728834&r2=728835&view=diff
==============================================================================
--- incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/UserProfileActionBean.java (original)
+++ incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/UserProfileActionBean.java Mon Dec 22 16:45:07 2008
@@ -4,7 +4,6 @@
 
 import net.sourceforge.stripes.action.*;
 import net.sourceforge.stripes.controller.LifecycleStage;
-import net.sourceforge.stripes.util.UrlBuilder;
 import net.sourceforge.stripes.validation.*;
 
 import com.ecyrd.jspwiki.WikiEngine;
@@ -107,9 +106,7 @@
         // Not so fast, Swifty Lazar! What if someone must approve the profile?
         catch( DecisionRequiredException e )
         {
-            UrlBuilder builder = new UrlBuilder( this.getContext().getLocale(), ViewActionBean.class, false );
-            builder.addParameter( "page", "ApprovalRequiredForUserProfiles" );
-            return new RedirectResolution( builder.toString() );
+            return new RedirectResolution( ViewActionBean.class ).addParameter( "page", "ApprovalRequiredForUserProfiles" );
         }
 
         // Any other errors are either UI or config problems, so let the user
@@ -125,18 +122,21 @@
             // Set user cookie
             Principal principal = getContext().getWikiSession().getUserPrincipal();
             CookieAssertionLoginModule.setUserCookie( getContext().getResponse(), principal.getName() );
-            UrlBuilder builder = new UrlBuilder( getContext().getLocale(), "/Wiki.jsp", false );
+            RedirectResolution r = new RedirectResolution( ViewActionBean.class );
             if( m_redirect != null )
             {
-                builder.addParameter( "page", m_redirect );
+                r.addParameter( "page", m_redirect );
             }
-            return new RedirectResolution( builder.toString() );
+            return r;
         }
 
         // Otherwise, send user to source page
-        UrlBuilder builder = new UrlBuilder( this.getContext().getLocale(), context.getSourcePage(), false );
-        builder.addParameter( "tab", "profile" );
-        return new RedirectResolution( builder.toString() );
+        RedirectResolution r = new RedirectResolution( ViewActionBean.class );
+        if ( m_redirect != null )
+        {
+            r.addParameter( "page", m_redirect );
+        }
+        return r;
     }
 
     public void setPasswordAgain( String password )

Modified: incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/ViewActionBean.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/ViewActionBean.java?rev=728835&r1=728834&r2=728835&view=diff
==============================================================================
--- incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/ViewActionBean.java (original)
+++ incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/ViewActionBean.java Mon Dec 22 16:45:07 2008
@@ -72,9 +72,7 @@
 
                     // Ok, it really doesn't exist. Send 'em to the "Create new page?" JSP
                     log.info("User supplied page name '" + pageName + "' that doesn't exist; redirecting to create pages JSP." );
-                    resolution = new RedirectResolution(NewPageActionBean.class);
-                    resolution.addParameter("page", pageName);
-                    return resolution;
+                    return new RedirectResolution(NewPageActionBean.class).addParameter("page", pageName);
                 }
             }
         }

Modified: incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/package.html
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/package.html?rev=728835&r1=728834&r2=728835&view=diff
==============================================================================
--- incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/package.html (original)
+++ incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/action/package.html Mon Dec 22 16:45:07 2008
@@ -9,43 +9,98 @@
 <p>The Stripes MVC framework enforces separation of presentation and
 page markup (JSPs) from processing logic. By "presentation" we mean
 anything that generates valid HTML or related markup, like cascading
-stylesheets. By "processing logic" we mean things like:</p>
+stylesheets. By "processing logic" we mean things like extracting
+request parameters, validating submitted form values, request flow
+processing, and localizing resources. In versions of JSPWiki prior to
+3.0, JSPs did most of these things. In 3.0, JSPs do very little, and
+instead delegate most of the heavy lifting to the Stripes framework and
+to special JavaBeans called {@link
+net.sourceforge.stripes.action.ActionBean}s.</p>
+
+<p>This Description page provides an overview of how Stripes is used
+in JSPWiki:</p>
 <ul>
-	<li>Extracting request parameters</li>
-	<li>Validating submitted form values</li>
-	<li>Looking, changing, or saving domain objects (<em>e.g.</em>,
-	WikiPages, Groups, UserProfiles)</li>
-	<li>Request flow processing (<em>e.g.</em>, redirects)</li>
-	<li>Localization of resources</li>
+	<li><a href="#actionbeans">ActionBeans and ActionBeanContexts</a></li>
+	<li><a href="#params">Request parameter binding</a></li>
+	<li><a href="#bindings">Bindings ActionBeans to URLs</a></li>
+	<li><a href="#events">ActionBean events</a></li>
+	<li><a href="#validation">Form validation</a></li>
+	<li><a href="#flow">Pageflow and redirection</a></li>
 </ul>
+<p>For the impatient, here is the developer cheat sheet:</p>
+<ol>
+	<li>Move scriptlet code that extracts request parameters</a> into
+	{@link net.sourceforge.stripes.action.ActionBean} getters and
+	setters.&nbsp;<a href="#1"><small>More...</small></a></li>
+	<li>Add <code><a
+		href="http://stripes.sourceforge.net/docs/current/taglib/stripes/useActionBean.html">&lt;stripes:useActionBean&gt;</a></code>
+	tags to every top-level JSP. Retrieve the ActionBean later in content
+	JSPs via {@link
+	com.ecyrd.jspwiki.ui.stripes.WikiInterceptor#findActionBean(ServletRequest)}.&nbsp;<a
+		href="#2"><small>More...</small></a></li>
+	<li>In JSPs, use EL syntax to access properties of the current
+	WikiActionBean; for example, <code>&lt;c:out
+	value="${wikiActionBean.context.wikiSession.loginName}"&gt;</code>.&nbsp;<a
+		href="#4"><small>More...</small></a></li>
+	<li>Move code that handles form POSTs into WikiActionBean event
+	handler methods.&nbsp;<a href="#5"><small>More...</small></a></li>
+	<li>Use Stripes forms tags instead of HTML form tags. Stripes <code><a
+		href="http://stripes.sourceforge.net/docs/current/taglib/stripes/form.html">&lt;stripes:form&gt;</a></code>&nbsp;
+	tags should always specify the <code>beanclass</code> attribute so that
+	they POST correctly to the ActionBean.<a href="#6"><small>More...</small></a></li>
+	<li>Add {@link net.sourceforge.stripes.validation.Validate}
+	annotations to all ActionBean setter methods that are bound to request
+	parameters.&nbsp;<a href="#7"><small>More...</small></a></li>
+	<li>Handler methods should return {@link
+	net.sourceforge.stripes.action.ForwardResolution} when the target is a
+	"display JSP", or {@link
+	net.sourceforge.stripes.action.RedirectResolution} that specify the
+	ActionBean plus event handler.&nbsp;<a href="#8"><small>More...</small></a></li>
+	<li>Client-side code that need to retrieve AJAX or JSON data from
+	JSPWiki should POST to a WikiActionBean event handler, which should in
+	turn return a {@link net.sourceforge.stripes.ajax.JavaScriptResolution}
+	or {@link net.sourceforge.stripes.action.StreamingResolution}&nbsp;<a
+		href="#9"><small>More...</small></a></li>
+	<li>Event handlers that need to ensure that the current
+	WikiActionBean is accessible by the next one in the request cycle
+	should add themselves to "flash scope."&nbsp;<a href="#10"><small>More...</small></a></li>
+</ol>
 
-<p>In versions of JSPWiki prior to 3.0, JSPs did most of these
-things. In 3.0, JSPs do very little, and instead delegate most of the
-heavy lifting to the Stripes framework and to special JavaBeans called <a
-	href="http://stripes.sourceforge.net/docs/current/javadoc/net/sourceforge/stripes/action/ActionBean.html">ActionBeans</a>.
-These ActionBeans contain getters/setters that are used to store
+<h3><a name="actionbeans">ActionBeans and ActionBeanContexts</a></h3>
+<p>ActionBeans contain getters/setters that are used to store
 parameter values extracted from the HTTP request, and additional methods
 called <em>events</em> that correspond to the values that old-style
-JSPWiki templates stored in their "action" form parameters.</p>
-<p>Every ActionBean contains a reference to the related object
-called the <a
-	href="http://stripes.sourceforge.net/docs/current/javadoc/net/sourceforge/stripes/action/ActionBeanContext.html">ActionBeanContext</a>,
-which allows callers to access the HTTP request and response objects,
-user's Locale, and other objects related to the context in which the
-ActionBean was invoked.</p>
-<p>In JSPWiki 3.0, we provide a subclass called {@link
-com.ecyrd.jspwiki.action.WikiActionBean} whose methods {@link
-com.ecyrd.jspwiki.action.WikiActionBean#getContext()} and {@link
-com.ecyrd.jspwiki.action.WikiActionBean#setContext(ActionBeanContext)}
-that correctly return (and set) {@link
-com.ecyrd.jspwiki.ui.stripes.WikiActionBeanContext}, a subclass of
-ActionBeanContext that contains accessors for things needed by JSPWiki,
+JSPWiki templates stored in their "action" form parameters. JSPWiki
+provides an implementation of ActionBean called {@link
+com.ecyrd.jspwiki.action.WikiActionBean}. Every ActionBean used in
+JSPWiki subclasses it.</p>
+
+<p>In the Stripes framework, every ActionBean contains a reference
+to a related object called the {@link
+net.sourceforge.stripes.action.ActionBeanContext}, which allows callers
+to access the HTTP request and response objects, validation errors and
+messges, and other objects related to the context in which the
+ActionBean was invoked. The ActionBeanContext is obtained by calling
+{@link net.sourceforge.stripes.action.ActionBean#getContext()}. JSPWiki
+3.0 provides an implementation of ActionBeanContext called {@link
+com.ecyrd.jspwiki.ui.stripes.WikiActionBeanContext}.
+WikiActionBeanContext contains accessors for things needed by JSPWiki,
 such as {@link
 com.ecyrd.jspwiki.ui.stripes.WikiActionBeanContext#getEngine()} and
 {@link
 com.ecyrd.jspwiki.ui.stripes.WikiActionBeanContext#getWikiSession()}.</p>
+WikiActionBeanContext also implements the {@link
+org.apache.jspwiki.api.WikiContext} interface, so any methods that
+normally operate on WikiContext will work with WikiActionBeanContext
+also.
+<em>Note: WikiActionBean method {@link
+com.ecyrd.jspwiki.action.WikiActionBean#getContext()} uses a covariant
+return type to indicate a WikiActionBeanContext is always returned.
+Thus, callers do not need to typecheck the return value because the
+ActionBeanContext will always be a WikiActionBeanContext.</em>
+</p>
 
-<h3>Request parameter binding</h3>
+<h3><a name="params">Request parameter binding</a></h3>
 <p>Stripes includes a very cool, easy-to-understand technique for <a
 	href="http://stripesframework.org/display/stripes/Quick+Start+Guide">automatically
 populating WikiActionBeans</a> with submitted request parameters. Basically
@@ -94,13 +149,12 @@
 method and passes the String "Finished". This is all done completely
 automatically -- there is no code to write.</p>
 
-<blockquote><strong>Guideline #1 for JSPWiki
-developers</strong>: processing code that extracts request parameters should be
-moved into WikiActionBean getter and setter fields. For example, if the
-parameter <code>foo</code> is needed, add methods <code>setFoo</code>
-and <code>getFoo</code> to the ActionBean). The field types can be <a
-	href="http://stripes.sourceforge.net/docs/current/javadoc/net/sourceforge/stripes/validation/TypeConverter.html">any
-type that Stripes knows how to convert</a>, such as <code>int</code>, <code>long</code>,
+<blockquote><a name="1" /><strong>Guideline #1
+for JSPWiki developers</strong>: processing code that extracts request parameters
+should be moved into WikiActionBean getter and setter fields. For
+example, if the parameter <code>foo</code> is needed, add methods <code>setFoo</code>
+and <code>getFoo</code> to the ActionBean). The field types can be any
+type that Stripes knows how to convert, such as <code>int</code>, <code>long</code>,
 <code>Date</code>, <code>String</code>, <code>BigDecimal</code> and
 others, or JSPWiki-specific types we've built converters for: WikiPage
 (via {@link com.ecyrd.jspwiki.ui.stripes.WikiPageTypeConverter}), Group
@@ -108,7 +162,7 @@
 Principal (via {@link
 com.ecyrd.jspwiki.ui.stripes.PrincipalTypeConverter}).</blockquote>
 
-<h3>Bindings ActionBeans to URLs</h3>
+<h3><a name="bindings">Bindings ActionBeans to URLs</a></h3>
 <p>All of this sounds great, but all of this assumes that somebody
 (us, Stripes, or God) knows how to bind the JSP <code>Edit.jsp</code> to
 our magic EditActionBean. How do we do this? Binding is done in two
@@ -136,8 +190,8 @@
 JSPWiki 3.0. By convention, every top-level JSP in JSPWiki 3.0 <em>must</em>
 include a <code>&lt;stripes:useActionBean&gt;</code> tag at the top of
 their pages. This does three things: first, it guarantees that the
-specified WikiActionBean will be injected into into the PageContext's
-request scope with the well-known name {@link
+specified {@link com.ecyrd.jspwiki.action.EditActionBean} will be
+injected into into PageContext scope with the well-known name {@link
 com.ecyrd.jspwiki.ui.stripes.WikiInterceptor#ATTR_ACTIONBEAN}
 ("wikiActionBean"). Second, it also means that JSTL expression language
 (EL) markup can access the WikiActionBean directly, for example, <code>${wikiActionBean.changenote}</code>.
@@ -152,29 +206,27 @@
 wiki.createContext( request, WikiContext.EDIT );</code></blockquote>
 
 <p>...although of course in 3.0 we do things via Stripes JSP tags
-rather than scriptlet code. WikiContexts aren't forgotten though; in
-fact, WikiContext is <em>implemented</em> by WikiActionBeanContext.
-That's right: we've refactored WikiContext so that it is now an
-interface whose methods can be accessed via a WikiActionBeanContext that
-is compatible with Stripes. Because we store the WikiActionBean as a
+rather than scriptlet code. Because we store the WikiActionBean as a
 request attribute, JSP authors can use and evaluate expressions like
 this:</p>
 
 <blockquote><code>${wikiActionBean.context.wikiSession.loginName}</code></blockquote>
 
-<p>...where the WikiContext is denoted by the EL expression <code>context</code>,
-a property of <code>wikiActionBean</code>. EL syntax can be used to
-navigate much more complicated object graphs than this, but you get the
-idea. What could be easier?</p>
-
-<blockquote><strong>Guideline #2 for JSPWiki
-developers</strong>: JSPs should not attempt to instantiate WikiContexts
-directly. Instead, they <em>must</em> include a <code>&lt;stripes:useActionBean
-beanclass="com.ecyrd.jspwiki.action.<em>foo</em>Bean"/&gt;</code> element that
-tells JSPWiki which WikiActionBean to use. For example, <code>&lt;stripes:useActionBean
-beanclass="com.ecyrd.jspwiki.action.EditActionBean"/&gt;</code> tells a JSPWiki
-to automatically instantiate the wiki context EditActionBean and place
-it in request scope. To retrieve the WikiActionBean later, call {@link
+<p>...where the WikiActionBeanContext is denoted by the EL
+expression <code>context</code>, a property of <code>wikiActionBean</code>.
+EL syntax can be used to navigate much more complicated object graphs
+than this, but you get the idea. What could be easier?</p>
+
+<blockquote><a name="2" /><strong>Guideline #2
+for JSPWiki developers</strong>: JSPs should not attempt to instantiate
+WikiContexts directly. Instead, they <em>must</em> include a <code>&lt;stripes:useActionBean
+beanclass="com.ecyrd.jspwiki.action.<em>foo</em>Bean" event="foo"
+id="wikiActionBean" /&gt;</code> element that tells JSPWiki which
+WikiActionBean to use, and the event it should fire. For example, <code>&lt;stripes:useActionBean
+beanclass="com.ecyrd.jspwiki.action.EditActionBean" event="edit"
+id="wikiActionBean"/&gt;</code> tells a JSPWiki to automatically instantiate an
+EditActionBean and place it in page scope. To retrieve the
+WikiActionBean later, call {@link
 com.ecyrd.jspwiki.ui.stripes.WikiInterceptor#findActionBean(ServletRequest)}.</blockquote>
 
 <p><strong>Automatic binding to <code>.action</code> URLs</strong></p>
@@ -190,10 +242,10 @@
 parameters.</p>
 
 <p>Stripes also attempts to locate ActionBeans by looking for a
-special class-level annotation, <code><a
-	href="http://stripes.sourceforge.net/docs/current/javadoc/net/sourceforge/stripes/action/UrlBinding.html">@UrlBinding</a></code>,
-that specifies the mapping. By convention, this is how we do things in
-JSPWiki 3.0. For example, the ViewActionBean class contains this
+special class-level annotation, {@link
+net.sourceforge.stripes.action.UrlBinding}, that specifies the mapping.
+By convention, this is how we do things in JSPWiki 3.0. For example, the
+{@link com.ecyrd.jspwiki.action.ViewActionBean} class contains this
 annotation:</p>
 
 <blockquote><code>@UrlBinding("/Wiki.action")</code></blockquote>
@@ -208,20 +260,21 @@
 URLs. For this reason, automatic binding also causes fields to be
 validated as well (more on this shortly).</p>
 
-<blockquote><strong>Guideline #3 for JSPWiki
-developers</strong>: every WikiActionBean subclass should contain a class-level <code>@UrlBinding</code>
-annotation that tells Stripes how to locate the bean when user submit
-forms.</blockquote>
-
-<blockquote><strong>Guideline #4 for JSPWiki
-developers</strong>: JSPs can -- and should -- use JSP 2.0 EL syntax to access
-properties of the current WikiActionBean. JSPWiki guarantees that when
-the <code>&lt;useActionBean&gt;</code> tag is present, the ActionBean
-will be made available as the page attibute <code>wikiActionBean</code>.
-For example, <code>${wikiActionBean.context.wikiSession.loginName}</code>
-prints the user's login name.</blockquote>
+<blockquote><a name="3" /><strong>Guideline #3
+for JSPWiki developers</strong>: every WikiActionBean subclass should contain a
+class-level <code>@UrlBinding</code> annotation that tells Stripes how
+to locate the bean when user submit forms.</blockquote>
+
+<blockquote><a name="4" /><strong>Guideline #4
+for JSPWiki developers</strong>: JSPs can -- and should -- use JSP 2.0 EL syntax
+to access properties of the current WikiActionBean. JSPWiki guarantees
+that when the <code>&lt;useActionBean&gt;</code> tag is present, the
+ActionBean will be made available as the page attibute <code>wikiActionBean</code>.
+For example, <code>&lt;c:out
+value="${wikiActionBean.context.wikiSession.loginName}" /&gt;</code> prints the
+user's login name.</blockquote>
 
-<h3>ActionBean Events</h3>
+<h3><a name="events">ActionBean Events</a></h3>
 <p>As discussed at the beginning of this page, Stripes ActionBeans
 includes getters and setters for parameters that are extracted from the
 request stream. ActionBeans also include methods that are annotated as
@@ -239,19 +292,20 @@
 
 <p>Scriptlets like these served only to clutter up JSPs with lots of
 spaghetti code. With Stripes, all event processing code is moved into
-ActionBean methods that have a special <code><a
-	href="http://stripes.sourceforge.net/docs/current/javadoc/net/sourceforge/stripes/action/HandlesEvent.html">@HandlesEvent</a></code>
-annotation. For example, the user preferences code that previously
-created user name assertions moved into an annotated <code>addAssertionCookie()</code>
-method that does the work by "handling" the event <code>createAssertedName</code>:</p>
+ActionBean methods that have a special {@link
+net.sourceforge.stripes.action.HandlesEvent} annotation. For example,
+the user preferences code that previously created user name assertions
+now lives in {@link com.ecyrd.jspwiki.action.UserPreferencesActionBean},
+in a "handler method" called <code>createAssertedName()</code> that
+handles the event <code>createAssertedName</code>:</p>
 
 <pre>@HandlesEvent("createAssertedName")
-public Resolution addAssertionCookie()
+public Resolution createAssertedName()
 {
     ...
     (event processing code goes here)
     ...
-    return new RedirectResolution("/");
+    return new RedirectResolution( ViewActionBean.class);
 }</pre>
 
 <p>The effect of moving POST processing code into ActionBeans
@@ -261,7 +315,7 @@
 counterparts, tells Stripes what beans and events it should invoke. For
 example:</p>
 
-<pre>&lt;stripes:form id="preferences" action="/UserPreferences.action" method="POST" acceptcharset="UTF-8"&gt;
+<pre>&lt;stripes:form id="preferences" beanclass="com.ecyrd.jspwiki.action.UserPreferencesBean" method="POST"&gt;
   ...
   (form markup)
   ...
@@ -270,56 +324,49 @@
 
 <p>Here, the <code><a
 	href="http://stripes.sourceforge.net/docs/current/taglib/stripes/form.html">stripes:form</a></code>
-tag includes an <code>action</code> parameter that specifies the URL to
-post to. Because the {@link
-com.ecyrd.jspwiki.action.UserPreferencesActionBean} contains a
-class-level <code>@UrlBinding("/UserPreferences.action")</code>
-annotation, Stripes knows that this corresponds to that bean class
-UserPreferencesActionBean. And because the <code><a
+tag includes a <code>beanclass</code> parameter that specifies what
+ActionBean the form should post to. The <code><a
 	href="http://stripes.sourceforge.net/docs/current/taglib/stripes/submit.html">stripes:submit</a></code>
-tag's <code>name</code> attribute contains the value <code>createAssertedName</code>,
-Stripes knows that it needs to locate and call the corresponding method
-that handles that event, in this case <code>addAssertionCookie</code>.
-<p>Note: an alternative syntax for &lt;stripes:form&gt; uses the <code>beanClass</code>
-attribute instead of <code>action</code>, which might be a little easier
-in some cases. This is functionally equivalent:</p>
-
-<pre>&lt;stripes:form id="preferences" action="/UserPreferences.action" method="POST" acceptcharset="UTF-8"&gt;</pre>
-
-<blockquote><strong>Guideline #5 for JSPWiki
-developers</strong>: processing code that handles form POST activities (that
-would have ordinarily gone into top-level JSPs) should <em>always</em>
-be moved into WikiActionBean event handler methods. These methods should
-contain a <code>@HandlesEvent</code> annotation that specifies which
-named event it handles. On JSPs, each event name should have an
-equivalent <code>&lt;stripes:submit&gt;</code> button; for example, <code>&lt;stripes:submit
-name="createAssertedName"/&gt;</code>. Its containing &lt;stripes:form&gt;
-element's <code>action</code> should contain the URL of the ActionBean
-the event pertains to, or alternative a <code>beanClass</code> attribute
-that names the ActionBean class explicitly. Example: <code>&lt;stripes:form
-beanClass="UserPreferencesAction.class"&gt;</code></blockquote>
-
-<blockquote><strong>Guideline #6 for JSPWiki
-developers</strong>: all <code>&lt;form&gt;</code> tags and related markup (such
-as <code>&lt;input&gt;</code>, <code>&lt;textarea&gt;</code>, <code>&lt;option&gt;</code>)
-should use the Stripes tags instead (<em>e.g.</em>, <code>&lt;stripes:form&gt;</code>,
-<code><a
+tag's <code>name</code> attribute supplies the event to execute, in this
+case <code>createAssertedName</code>, As you can see from the code
+snippet, the event name "createAssertedName" matches the value of the <code>@HandlerEvent</code>
+annotation.</code> .</p>
+
+<blockquote><a name="5" /><strong>Guideline #5
+for JSPWiki developers</strong>: processing code that handles form POST
+activities (that would have ordinarily gone into top-level JSPs) should
+<em>always</em> be moved into WikiActionBean event handler methods.
+These methods should contain a <code>@HandlesEvent</code> annotation
+that specifies which named event it handles. On JSPs, each event name
+should have an equivalent <code>&lt;stripes:submit&gt;</code> button;
+for example, <code>&lt;stripes:submit
+name="createAssertedName"/&gt;</code>. Its containing <code>&lt;stripes:form&gt;</code>
+element's <code>beanclass</code> attribute should contain the name of
+the ActionBean class the event pertains to. Example: <code>&lt;stripes:form
+beanclass="UserPreferencesAction.class"&gt;</code></blockquote>
+
+<blockquote><a name="6" /><strong>Guideline #6
+for JSPWiki developers</strong>: all <code>&lt;form&gt;</code> tags and related
+markup (such as <code>&lt;input&gt;</code>, <code>&lt;textarea&gt;</code>,
+<code>&lt;option&gt;</code>) should use the Stripes tags instead (<em>e.g.</em>,
+<code>&lt;stripes:form&gt;</code>, <code><a
 	href="http://stripes.sourceforge.net/docs/current/taglib/stripes/text.html">&lt;stripes:text&gt;</a></code>,
 <code><a
 	href="http://stripes.sourceforge.net/docs/current/taglib/stripes/textarea.html">&lt;stripes:textarea&gt;</a></code>,
 <code><a
 	href="http://stripes.sourceforge.net/docs/current/taglib/stripes/option.html">&lt;stripes:option&gt;</a></code>).
 With one or two exceptions, these tags are identical to their HTML
-equivalents, and contain a few extra attributes used by Stripes.</blockquote>
+equivalents, and contain a few extra attributes used by Stripes. The
+package {@link com.ecyrd.jspwiki.ui.migrator} contains a full set of
+utility classes that can automatically make these changes for you.</blockquote>
 
-<h3>Form validation</h3>
+<h3><a name="validation">Form validation</a></h3>
 <p>Because JSPWiki 3.0 uses Stripes, our WikiActionBeans take
 advantage of another terrific feature: automatic form field validation
 based on annotations. Recall previously that ActionBeans have getters
 and setters that Stripes uses to store and retrieve request parameters.
 For validation, either the getter or setter (by convention, the setter)
-can also contain a <code><a
-	href="http://stripes.sourceforge.net/docs/current/javadoc/net/sourceforge/stripes/validation/Validate.html">@validate</a></code>
+can also contain a {@link net.sourceforge.stripes.validation.Validate}
 annotation that indicates whether values are required, and what their
 acceptable lengths or constraints might be. For example, consider {@link
 com.ecyrd.jspwiki.action.UserProfileActionBean}, a WikiActionBean that
@@ -351,12 +398,12 @@
 type. In addition to Stripes' own converters, JSPWiki 3.0 contains
 converters for fields of type WikiPage, Group and Principal.</p>
 
-<blockquote><strong>Guideline #7 for JSPWiki
-developers</strong>: When creating WikiActionBeans, all fields that require
-validation should have <code>@validate</code> annotations on their
-setter methods.</blockquote>
+<blockquote><a name="7" /><strong>Guideline #7
+for JSPWiki developers</strong>: When creating WikiActionBeans, all fields that
+require validation should have <code>@validate</code> annotations on
+their setter methods.</blockquote>
 
-<h3>Pageflow and Redirection</h3>
+<h3><a name="flow">Pageflow and Redirection</a></h3>
 <p>Browser redirects and related pageflow issues are handled very
 differently in JSPWiki 3.0 than in previous versions. Previously,
 redirects were implemented directly in JSP scriptlet code. For example,
@@ -378,55 +425,71 @@
 <p>In JSPWiki 3.0, these activities fall into the category of
 "processing logic" -- the type of code that gets put into WikiActionBean
 events. In this particular case, the code moves into a Stripes event
-handler method in the EditActionBean class that has an <code>@HandlesEvent</code>
-annotation with the name <code>save</code>.</p>
+handler method in the {@link com.ecyrd.jspwiki.action.EditActionBean}
+class that has an <code>@HandlesEvent</code> annotation with the name <code>save</code>.</p>
 
 <p>When a Stripes event handler method needs to redirect a user to
-another page, it returns an object called a <a
-	href="http://stripes.sourceforge.net/docs/current/javadoc/net/sourceforge/stripes/action/Resolution.html">Resolution</a>
-that tells Stripes how to proceed. Stripes contains four easy-to-use
-Resolutions:</p>
+another page, it returns an object called a {@link
+net.sourceforge.stripes.action.Resolution} that tells Stripes how to
+proceed. Stripes contains four easy-to-use Resolutions:</p>
 
 <ul>
-	<li><strong><a
-		href="http://stripes.sourceforge.net/docs/current/javadoc/net/sourceforge/stripes/action/ForwardResolution.html">ForwardResolution</a></strong>:
-	forwards the user to another path within the same web application using
-	a server side forward. Constructors include:
+	<li><strong>{@link
+	net.sourceforge.stripes.action.ForwardResolution}</strong> - forwards the user
+	to another path within the same web application using a server side
+	forward. Important Constructors include:
 	<ul>
-		<li><code>ForwardResolution(Class<? extends ActionBean> beanType, String event)</code> - Constructs a ForwardResolution that will forward to the URL appropriate for the ActionBean supplied.</li>
-      <li><code>ForwardResolution(String path)</code> - Simple constructor that takes in the path to forward the user to.</li>
-    </ul>
-  </li>
-  <li><strong><a href="http://stripes.sourceforge.net/docs/current/javadoc/net/sourceforge/stripes/ajax/JavaScriptResolution.html">JavaScriptResolution</a></strong>: converts a Java object web to a web of JavaScript objects and arrays, and stream the JavaScript back to the client. The output of this resolution can be evaluated in JavaScript using the eval() function, and will return a reference to the top level JavaScript object. Constructors include:
-    <ul>
-      <li><code>JavaScriptResolution(Object rootObject, Class<?>...
-		userTypesExcluded)</code></li>
+		<li>{@link
+		net.sourceforge.stripes.action.ForwardResolution#ForwardResolution(Class,
+		String)} - Constructs a ForwardResolution that will forward to the URL
+		appropriate for the ActionBean supplied.</li>
+		<li>{@link
+		net.sourceforge.stripes.action.ForwardResolution#ForwardResolution(String)}
+		- Simple constructor that takes in the path to forward the user to.
+		ForwardResolutions are used in JSPWiki primarily to forward the user
+		to "display JSPs" that render pages.</li>
 	</ul>
 	</li>
-	<li><strong><a
-		href="http://stripes.sourceforge.net/docs/current/javadoc/net/sourceforge/stripes/action/RedirectResolution.html">RedirectResolution</a></strong>:
-	redirects the user to another path by issuing a client side redirect.
-	Constructors include:
+	<li><strong>{@link
+	net.sourceforge.stripes.ajax.JavaScriptResolution}</strong> - converts a Java
+	object web to a web of JavaScript objects and arrays, and stream the
+	JavaScript back to the client. The output of this resolution can be
+	evaluated in JavaScript using the eval() function, and will return a
+	reference to the top level JavaScript object.</li>
+	<li><strong>{@link
+	net.sourceforge.stripes.action.RedirectResolution}</strong> - redirects the user
+	to another path by issuing a client side redirect. Constructors
+	include:
 	<ul>
-		<li><code>RedirectResolution(Class&lt;? extends
-		ActionBean&gt; beanType)</code> - Constructs a RedirectResolution that will
-		redirect to the URL appropriate for the ActionBean supplied.</li>
-		<li><code>RedirectResolution(Class&lt;? extends
-		ActionBean&gt; beanType, String event)</code> - Constructs a
-		RedirectResolution that will redirect to the URL appropriate for the
-		ActionBean supplied.</li>
-		<li><code>RedirectResolution(String url)</code> - Simple
-		constructor that takes the URL to which to forward the user.</li>
+		<li>{@link
+		net.sourceforge.stripes.action.RedirectResolution#RedirectResolution(Class,
+		String)} - Constructs a RedirectResolution that will redirect to the
+		supplied ActionBean and event.</li>
+		<li>{@link
+		net.sourceforge.stripes.action.RedirectResolution#RedirectResolution(Class)}
+		- Simple constructor that accepts a URL to forward the user. <em>This
+		form of RedirectResolution is discouraged when the target corresponds
+		to an ActionBean event (for example: <code>Wiki.jsp</code>. Instead,
+		event methods should return a RedirectResolution that specifies the
+		ActionBean class directly, in this case <code>ViewActionBean.class</code>.</em>
+		</li>
 	</ul>
 	</li>
 
-	<li><strong><a
-		href="http://stripes.sourceforge.net/docs/current/javadoc/net/sourceforge/stripes/action/StreamingResolution.html">StreamingResolution</a></strong>:
+	<li>{@link net.sourceforge.stripes.action.StreamingResolution} -
 	streams data back to the client (in place of forwarding the user to
 	another page). Designed to be used for streaming non-page data such as
 	generated images/charts and XML islands.</li>
 </ul>
 
+<blockquote><a name="8" /><strong>Guideline #8
+for JSPWiki developers</strong>: event handler methods should return
+ForwardResolutions when the target is a "display JSP" that renders a
+page. Hander methods that need to redirect the user to destinations
+elsewhere in the application should return RedirectResolutions that
+specify the ActionBean plus event handler, <em>not</em> the URL.</blockquote>
+
+
 <p>Handler events that need to interrupt processing or return data
 to the use simply need to return one of these four resolutions. For
 example, the previous scriptlet code could be re-written as an
@@ -454,11 +517,11 @@
 automatically locate due to ConflictActionBean's <code>@UrlBinding</code>
 annotation (see Guideline #3). It will also append the parameter <code>page</code>
 with the value of the page. And finally it will add the current
-ActionBean to a temporary storage area called the <a
-	href="http://stripes.sourceforge.net/docs/current/javadoc/net/sourceforge/stripes/controller/FlashScope.html">flash
-scope</a> where it can be retrieved by the target of the redirect. When the
-target (in this case, ConflictActionBean) executes, it can retrieve the
-bean stored in flash scope easily:</p>
+ActionBean to a temporary storage area called the {@link
+net.sourceforge.stripes.controller.FlashScope} where it can be retrieved
+by the target of the redirect. When the target (in this case,
+ConflictActionBean) executes, it can retrieve the bean stored in flash
+scope easily:</p>
 
 <pre>FlashScope scope = FlashScope.getCurrent( request, true);
 WikiActionBean editContext = (WikiActionBean)scope.get( "/EditActionBean" );</pre>
@@ -466,22 +529,16 @@
 <p>After retrieving the previously-flashed EditActionBean, its
 contents can be retrieved and manipulated like any other bean.</p>
 
-<blockquote><strong>Guideline #8 for JSPWiki
-developers</strong>: processing logic that would, in previous versions of
-JSPWiki, reside in scriptlet code should be moved into WikiActionBean
-event handlers. When an event handler needs to modify the user page flow
-or redirect the browser, it should return a suitable Resolution, such as
-the RedirectResolution.</blockquote>
-
-<blockquote><strong>Guideline #9 for JSPWiki
-developers</strong>: client-side code that need to retrieve AJAX or JSON data
-from JSPWiki should POST to a WikiActionBean event handler, which should
-in turn return a <code>JavaScriptResolution</code> or a <code>StreamingResolution</code>.</blockquote>
-
-<blockquote><strong>Guideline #10 for JSPWiki
-developers</strong>: event handlers that need to ensure that the current
-WikiActionBean is accessible by the next one in the request cycle should
-add themselves to "flash scope" by calling the Resolution's <code><a
+<blockquote><a name="9" /><strong>Guideline #10
+for JSPWiki developers</strong>: client-side code that need to retrieve AJAX or
+JSON data from JSPWiki should POST to a WikiActionBean event handler,
+which should in turn return a <code>JavaScriptResolution</code> or a <code>StreamingResolution</code>.</blockquote>
+
+<blockquote><a name="10" /><strong>Guideline #11
+for JSPWiki developers</strong>: event handlers that need to ensure that the
+current WikiActionBean is accessible by the next one in the request
+cycle should add themselves to "flash scope" by calling the Resolution's
+<code><a
 	href="http://stripes.sourceforge.net/docs/current/javadoc/net/sourceforge/stripes/action/RedirectResolution.html#flash(net.sourceforge.stripes.action.ActionBean)">flash()</a></code>
 method. These can be retrieved by the next ActionBean by retrieving the
 current FlashScope for the request and calling its <code><a

Modified: incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/attachment/Attachment.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/attachment/Attachment.java?rev=728835&r1=728834&r2=728835&view=diff
==============================================================================
--- incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/attachment/Attachment.java (original)
+++ incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/attachment/Attachment.java Mon Dec 22 16:45:07 2008
@@ -20,8 +20,6 @@
  */
 package com.ecyrd.jspwiki.attachment;
 
-import org.apache.jspwiki.api.WikiPage;
-
 import com.ecyrd.jspwiki.JCRWikiPage;
 import com.ecyrd.jspwiki.WikiEngine;
 

Modified: incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/attachment/package.html
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/attachment/package.html?rev=728835&r1=728834&r2=728835&view=diff
==============================================================================
--- incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/attachment/package.html (original)
+++ incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/attachment/package.html Mon Dec 22 16:45:07 2008
@@ -15,7 +15,7 @@
 <ul>
 <li>AttachmentServlet - the servlet which allows both uploading and downloading servlets.</li>
 <li>AttachmentManager - The JSPWiki Manager component which manages the storage of attachments.</li>
-<li>Attachment - A special kind of a {@link com.ecyrd.jspwiki.WikiPage} which stores a handle
+<li>Attachment - A special kind of a {@link org.apache.jspwiki.api.WikiPage} which stores a handle
    to the attachment data.</li>
 </ul>
 

Modified: incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/auth/authorize/GroupManager.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/auth/authorize/GroupManager.java?rev=728835&r1=728834&r2=728835&view=diff
==============================================================================
--- incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/auth/authorize/GroupManager.java (original)
+++ incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/auth/authorize/GroupManager.java Mon Dec 22 16:45:07 2008
@@ -105,8 +105,60 @@
     }
 
     /**
+     * <p>
+     * Finds or creates a Group in the GroupDatabase. The Group will either be a
+     * copy of an existing Group (if one can be found), or a new, unregistered
+     * Group (if not). Optionally, this method can throw a WikiSecurityException
+     * if the Group does not yet exist in the GroupManager cache.
+     * </p>
+     * <p>
+     * This method does not commit the new Group to the GroupManager cache. To
+     * do that, use {@link #setGroup(WikiSession, Group)}.
+     * </p>
+     * 
+     * @param name the name of the group to look up (or create)
+     * @param create whether this method should create a new, empty Group if one
+     *            with the requested name is not found. If <code>false</code>,
+     *            groups that do not exist will cause a
+     *            <code>NoSuchPrincipalException</code> to be thrown
+     * @return a new or existing Group. If the Group has not previously been
+     *         saved to the GroupDatabase and <code>create</code> is
+     *         <code>true</code>, it will contain no members.
+     * @throws WikiSecurityException if the group name isn't allowed
+     * @throws NoSuchPrincipalException if the group isn't found, and
+     *             <code>create</code> is <code>false</code>
+     * @see com.ecyrd.jspwiki.auth.authorize.Group#RESTRICTED_GROUPNAMES
+     */
+    public Group getGroup( String name, boolean create ) throws NoSuchPrincipalException, WikiSecurityException
+    {
+        // Certain names are forbidden
+        if ( ArrayUtils.contains( Group.RESTRICTED_GROUPNAMES, name ) )
+        {
+            throw new WikiSecurityException( "Illegal group name: " + name );
+        }
+        
+        Group group = m_groups.get( new GroupPrincipal( name ) );
+        
+        // If not found...
+        if( group == null )
+        {
+            if( create )
+            {
+                group = new Group( name, m_engine.getApplicationName() );
+            }
+            else
+            {
+                throw new NoSuchPrincipalException( "Group " + name + " not found." );
+            }
+        }
+        
+        return group;
+    }
+    
+    /**
      * Returns the Group matching a given name. If the group cannot be found,
      * this method throws a <code>NoSuchPrincipalException</code>.
+     * 
      * @param name the name of the group to find
      * @return the group
      * @throws NoSuchPrincipalException if the group cannot be found

Modified: incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/preferences/Preferences.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/preferences/Preferences.java?rev=728835&r1=728834&r2=728835&view=diff
==============================================================================
--- incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/preferences/Preferences.java (original)
+++ incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/preferences/Preferences.java Mon Dec 22 16:45:07 2008
@@ -318,7 +318,7 @@
      * {@link com.ecyrd.jspwiki.ui.stripes.WikiInterceptor#intercept(net.sourceforge.stripes.controller.ExecutionContext)},
      * {@link com.ecyrd.jspwiki.action.WikiContextFactory#newContext(HttpServletRequest, javax.servlet.http.HttpServletResponse, String)}
      * and
-     * {@link com.ecyrd.jspwiki.action.WikiContextFactory#newViewContext(HttpServletRequest, javax.servlet.http.HttpServletResponse, com.ecyrd.jspwiki.WikiPage)}.
+     * {@link com.ecyrd.jspwiki.action.WikiContextFactory#newViewContext(HttpServletRequest, javax.servlet.http.HttpServletResponse, org.apache.jspwiki.api.WikiPage)}.
      * </p>
      * <p>
      * Every user preference is read from a unique cookie. This method parses

Modified: incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/providers/FileSystemProvider.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/providers/FileSystemProvider.java?rev=728835&r1=728834&r2=728835&view=diff
==============================================================================
--- incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/providers/FileSystemProvider.java (original)
+++ incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/providers/FileSystemProvider.java Mon Dec 22 16:45:07 2008
@@ -28,8 +28,6 @@
 import com.ecyrd.jspwiki.log.Logger;
 import com.ecyrd.jspwiki.log.LoggerFactory;
 
-import com.ecyrd.jspwiki.*;
-
 /**
  *  Provides a simple directory based repository for Wiki pages.
  *  <P>

Modified: incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/ui/stripes/GroupTypeConverter.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/ui/stripes/GroupTypeConverter.java?rev=728835&r1=728834&r2=728835&view=diff
==============================================================================
--- incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/ui/stripes/GroupTypeConverter.java (original)
+++ incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/ui/stripes/GroupTypeConverter.java Mon Dec 22 16:45:07 2008
@@ -24,6 +24,7 @@
 
 import com.ecyrd.jspwiki.WikiEngine;
 import com.ecyrd.jspwiki.auth.NoSuchPrincipalException;
+import com.ecyrd.jspwiki.auth.WikiSecurityException;
 import com.ecyrd.jspwiki.auth.authorize.Group;
 import com.ecyrd.jspwiki.auth.authorize.GroupManager;
 
@@ -66,11 +67,16 @@
         Group group = null;
         try
         {
-            group = mgr.getGroup( groupName );
+            group = mgr.getGroup( groupName, true );
         }
         catch( NoSuchPrincipalException e )
         {
-            errors.add( new LocalizableError( "group.doesnotexist", groupName ) );
+            // Should never happen
+        }
+        catch( WikiSecurityException e )
+        {
+            // Illegal group name
+            errors.add( new LocalizableError( "editgroup.illegalname" ) );
         }
         return group;
     }

Added: incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/ui/stripes/LineDelimitedTypeConverter.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/ui/stripes/LineDelimitedTypeConverter.java?rev=728835&view=auto
==============================================================================
--- incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/ui/stripes/LineDelimitedTypeConverter.java (added)
+++ incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/ui/stripes/LineDelimitedTypeConverter.java Mon Dec 22 16:45:07 2008
@@ -0,0 +1,18 @@
+package com.ecyrd.jspwiki.ui.stripes;
+
+import net.sourceforge.stripes.validation.OneToManyTypeConverter;
+
+/**
+ * Overrides {@link net.sourceforge.stripes.validation.OneToManyTypeConverter} so
+ * that multiple items parsed by the converter use carriage return delimiters, instead of spaces and commas.
+ */
+public class LineDelimitedTypeConverter extends OneToManyTypeConverter
+{
+
+    @Override
+    protected String getSplitRegex()
+    {
+        return "\n";
+    }
+    
+}

Modified: incubator/jspwiki/trunk/src/webdocs/Preview.jsp
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/webdocs/Preview.jsp?rev=728835&r1=728834&r2=728835&view=diff
==============================================================================
--- incubator/jspwiki/trunk/src/webdocs/Preview.jsp (original)
+++ incubator/jspwiki/trunk/src/webdocs/Preview.jsp Mon Dec 22 16:45:07 2008
@@ -1,37 +1,7 @@
-<%@ page import="com.ecyrd.jspwiki.log.Logger" %>
-<%@ page import="com.ecyrd.jspwiki.log.LoggerFactory" %>
-<%@ page import="com.ecyrd.jspwiki.*" %>
-<%@ page import="com.ecyrd.jspwiki.filters.*" %>
-<%@ page import="java.util.Date" %>
-<%@ page import="com.ecyrd.jspwiki.ui.EditorManager" %>
-<%@ page errorPage="/Error.jsp" %>
-<%@ taglib uri="http://jakarta.apache.org/jspwiki.tld" prefix="wiki" %>
 <%@ taglib uri="http://stripes.sourceforge.net/stripes.tld" prefix="stripes" %>
 <stripes:useActionBean beanclass="com.ecyrd.jspwiki.action.EditActionBean" event="preview" id="wikiActionBean" />
-<%! 
-    Logger log = LoggerFactory.getLogger("JSPWiki"); 
-%>
-
-<%
-    WikiEngine wiki = WikiEngine.getInstance( getServletConfig() );
-    // Create wiki context and check for authorization
-    WikiContext wikiContext = wiki.createContext( request, WikiContext.PREVIEW );
-    String pagereq = wikiContext.getPage().getName();
-
-    pageContext.setAttribute( EditorManager.ATTR_EDITEDTEXT,
-                              session.getAttribute( EditorManager.REQ_EDITEDTEXT ),
-                              PageContext.REQUEST_SCOPE );
-
-    String lastchange = SpamFilter.getSpamHash( wikiContext.getPage(), request );
-
-    pageContext.setAttribute( "lastchange",
-                              lastchange,
-                              PageContext.REQUEST_SCOPE );
-   
-    // Set the content type and include the response content
-    response.setContentType("text/html; charset="+wiki.getContentEncoding() );
-    String contentPage = wiki.getTemplateManager().findJSP( pageContext,
-                                                            wikiContext.getTemplate(),
-                                                            "ViewTemplate.jsp" );
-%><wiki:Include page="<%=contentPage%>" />
-
+<stripes:layout-render name="/templates/default/ViewLayout.jsp">
+  <stripes:layout-component name="content">
+    <jsp:include page="/templates/default/PreviewContent.jsp" />
+  </stripes:layout-component>
+</stripes:layout-render>

Modified: incubator/jspwiki/trunk/tests/com/ecyrd/jspwiki/action/GroupActionBeanTest.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/tests/com/ecyrd/jspwiki/action/GroupActionBeanTest.java?rev=728835&r1=728834&r2=728835&view=diff
==============================================================================
--- incubator/jspwiki/trunk/tests/com/ecyrd/jspwiki/action/GroupActionBeanTest.java (original)
+++ incubator/jspwiki/trunk/tests/com/ecyrd/jspwiki/action/GroupActionBeanTest.java Mon Dec 22 16:45:07 2008
@@ -1,6 +1,6 @@
 /*
-    JSPWiki - a JSP-based WikiWiki clone.
 
+    JSPWiki - a JSP-based WikiWiki clone.
     Licensed to the Apache Software Foundation (ASF) under one
     or more contributor license agreements.  See the NOTICE file
     distributed with this work for additional information
@@ -25,13 +25,28 @@
 import junit.framework.Test;
 import junit.framework.TestCase;
 import junit.framework.TestSuite;
+import net.sourceforge.stripes.mock.MockRoundtrip;
+import net.sourceforge.stripes.validation.ValidationErrors;
 
 import com.ecyrd.jspwiki.TestEngine;
+import com.ecyrd.jspwiki.WikiSession;
+import com.ecyrd.jspwiki.auth.NoSuchPrincipalException;
+import com.ecyrd.jspwiki.auth.SessionMonitor;
+import com.ecyrd.jspwiki.auth.Users;
+import com.ecyrd.jspwiki.auth.WikiPrincipal;
+import com.ecyrd.jspwiki.auth.authorize.Group;
+import com.ecyrd.jspwiki.auth.authorize.GroupDatabase;
+import com.ecyrd.jspwiki.auth.authorize.GroupManager;
 
 public class GroupActionBeanTest extends TestCase
 {
-    TestEngine m_engine;
+    public static Test suite()
+    {
+        return new TestSuite( GroupActionBeanTest.class );
+    }
 
+    TestEngine m_engine;
+    
     public void setUp()
     {
         // Start the WikiEngine, and stash reference
@@ -40,6 +55,7 @@
         {
             props.load( TestEngine.findTestProperties() );
             m_engine = new TestEngine( props );
+            deleteGroup( "TestGroup" );
         }
         catch( Exception e )
         {
@@ -47,8 +63,202 @@
         }
     }
 
-    public static Test suite()
+    public void tearDown()
     {
-        return new TestSuite( GroupActionBeanTest.class );
+        deleteGroup( "TestGroup" );
+    }
+
+    public void testView() throws Exception
+    {
+        // Start with an authenticated user
+        MockRoundtrip trip = m_engine.authenticatedTrip( Users.JANNE, Users.JANNE_PASS, GroupActionBean.class );
+
+        // View the group
+        trip.setParameter( "group", "TV" );
+        trip.execute( "view" );
+
+        // Verify we are directed to the view page
+        GroupActionBean bean = trip.getActionBean( GroupActionBean.class );
+        ValidationErrors errors = bean.getContext().getValidationErrors();
+        assertEquals( 0, errors.size() );
+        assertEquals( "/Group.jsp", trip.getDestination() );
+
+        // Verify we got the right group
+        Group group = bean.getGroup();
+        assertNotNull( group );
+        assertEquals( "TV", group.getName() );
+        assertEquals( 3, group.members().length );
+        assertTrue( group.isMember( new WikiPrincipal( "Archie Bunker" ) ) );
+        assertTrue( group.isMember( new WikiPrincipal( "BullwinkleMoose" ) ) );
+        assertTrue( group.isMember( new WikiPrincipal( "Fred Friendly" ) ) );
+    }
+      
+    public void testViewNonExistent() throws Exception
+    {
+        MockRoundtrip trip;
+        GroupActionBean bean;
+        ValidationErrors errors;
+
+        // Start with an authenticated user
+        trip = m_engine.authenticatedTrip( Users.JANNE, Users.JANNE_PASS, GroupActionBean.class );
+
+        // View a non-existent group
+        trip.setParameter( "group", "NonExistentGroup" );
+        trip.execute( "view" );
+
+        // Verify we are directed to the "create" event
+        bean = trip.getActionBean( GroupActionBean.class );
+        errors = bean.getContext().getValidationErrors();
+        assertEquals( 0, errors.size() );
+        assertEquals( "/Group.action?create=&group=NonExistentGroup", trip.getDestination() );
+    }
+        
+    public void testViewNullGroup() throws Exception
+    {
+        MockRoundtrip trip;
+        GroupActionBean bean;
+        ValidationErrors errors;
+
+        // Start with an authenticated user
+        trip = m_engine.authenticatedTrip( Users.JANNE, Users.JANNE_PASS, GroupActionBean.class );
+
+        // View a non-existent group
+        trip.execute( "view" );
+
+        // Verify we are directed to the "create" event with suggested group name
+        bean = trip.getActionBean( GroupActionBean.class );
+        errors = bean.getContext().getValidationErrors();
+        assertEquals( 0, errors.size() );
+        assertNotNull(  bean.getGroup() );
+        assertEquals( "Group1", bean.getGroup().getName() );
+        assertEquals( "/Group.action?create=&group=Group1", trip.getDestination() );
+    }
+   
+    public void testDeleteGroup() throws Exception
+    {
+        // Start with an authenticated user
+        MockRoundtrip trip = m_engine.authenticatedTrip( Users.ADMIN, Users.ADMIN_PASS, GroupActionBean.class );
+        WikiSession wikiSession = SessionMonitor.getInstance( m_engine ).find( trip.getRequest().getSession() );
+
+        // Create a new group
+        GroupManager mgr = m_engine.getGroupManager();
+        Group group = mgr.getGroup( "TestGroup", true );
+        group.add( new WikiPrincipal( "Janne Jalkanen" ) );
+        group.add( new WikiPrincipal( "Princess Buttercup" ) );
+        mgr.setGroup( wikiSession, group );
+        
+        // Make sure the group saved correctly
+        group = mgr.getGroup( "TestGroup" );
+        assertNotNull( group );
+        
+        // Now, try to delete the group
+        trip.setParameter( "group", "TestGroup" );
+        trip.execute( "delete" );
+        
+        // Make sure the group was deleted
+        try
+        {
+            group = mgr.getGroup( "TestGroup" );
+        }
+        catch ( NoSuchPrincipalException e )
+        {
+            // Excellent! This is what we expected
+        }
+        assertNull( null );
+        
+        // Verify we are directed to the wiki front page
+        assertEquals( "/Wiki.action", trip.getDestination() );
+
+    }
+    
+    public void testSaveExistingGroup() throws Exception
+    {
+        // Start with an authenticated user
+        MockRoundtrip trip = m_engine.authenticatedTrip( Users.JANNE, Users.JANNE_PASS, GroupActionBean.class );
+        WikiSession wikiSession = SessionMonitor.getInstance( m_engine ).find( trip.getRequest().getSession() );
+
+        // Create a new group
+        GroupManager mgr = m_engine.getGroupManager();
+        Group group = mgr.getGroup( "TestGroup", true );
+        group.add( new WikiPrincipal( "Janne Jalkanen" ) );
+        group.add( new WikiPrincipal( "Princess Buttercup" ) );
+        mgr.setGroup( wikiSession, group );
+        
+        // Make sure the group saved correctly
+        group = mgr.getGroup( "TestGroup" );
+        assertNotNull( group );
+
+        // Now, try to save the group with some new members
+        trip.setParameter( "group", "TestGroup" );
+        trip.setParameter( "members","Princess Buttercup\nInigo Montoya\nMiracle Max" );
+        trip.execute( "save" );
+        
+        // Verify we are directed to the group-view page
+        GroupActionBean bean = trip.getActionBean( GroupActionBean.class );
+        ValidationErrors errors = bean.getContext().getValidationErrors();
+        assertEquals( 0, errors.size() );
+        assertEquals( "/Group.action?view=&group=TestGroup", trip.getDestination() );
+
+        // Verify the Group saved correctly
+        group = bean.getGroup();
+        assertNotNull( group );
+        assertEquals( "TestGroup", group.getName() );
+        assertEquals( 3, group.members().length );
+        assertTrue( group.isMember( new WikiPrincipal( "Princess Buttercup" ) ) );
+        assertTrue( group.isMember( new WikiPrincipal( "Inigo Montoya" ) ) );
+        assertTrue( group.isMember( new WikiPrincipal( "Miracle Max" ) ) );
+    }
+    
+    public void testSaveNewGroup() throws Exception
+    {
+        // Start with an authenticated user
+        MockRoundtrip trip = m_engine.authenticatedTrip( Users.JANNE, Users.JANNE_PASS, GroupActionBean.class );
+
+        // Try to save the group
+        trip.setParameter( "group", "TestGroup" );
+        trip.setParameter( "members","Princess Buttercup\nInigo Montoya\nMiracle Max" );
+        trip.execute( "save" );
+
+        // Should NOT succeed because the save event requires Edit permissions, which we don't have
+        GroupActionBean bean = trip.getActionBean( GroupActionBean.class );
+        ValidationErrors errors = bean.getContext().getValidationErrors();
+        assertEquals( 0, errors.size() );
+        assertEquals( "/Login.action", trip.getDestination().substring( 0, 13 ) );
+        
+        // Try again with the 'saveNew' event
+        trip = m_engine.authenticatedTrip( Users.JANNE, Users.JANNE_PASS, GroupActionBean.class );
+        trip.setParameter( "group", "TestGroup" );
+        trip.setParameter( "members","Princess Buttercup\nInigo Montoya\nMiracle Max" );
+        trip.execute( "saveNew" );
+        
+        // Verify we are directed to the view page
+        bean = trip.getActionBean( GroupActionBean.class );
+        errors = bean.getContext().getValidationErrors();
+        assertEquals( 0, errors.size() );
+        assertEquals( "/Group.action?view=&group=TestGroup", trip.getDestination() );
+
+        // Verify the Group saved correctly
+        Group group = bean.getGroup();
+        assertNotNull( group );
+        assertEquals( "TestGroup", group.getName() );
+        assertEquals( 3, group.members().length );
+        assertTrue( group.isMember( new WikiPrincipal( "Princess Buttercup" ) ) );
+        assertTrue( group.isMember( new WikiPrincipal( "Inigo Montoya" ) ) );
+        assertTrue( group.isMember( new WikiPrincipal( "Miracle Max" ) ) );
+    }
+
+    private void deleteGroup( String groupName )
+    {
+        GroupManager mgr = m_engine.getGroupManager();
+        try
+        {
+            Group group = mgr.getGroup( groupName );
+            GroupDatabase db = m_engine.getGroupManager().getGroupDatabase();
+            db.delete( group );
+        }
+        catch ( Exception e )
+        {
+            // Ok; no group there, or GroupDatabase not initialized
+        }
     }
 }

Modified: incubator/jspwiki/trunk/tests/com/ecyrd/jspwiki/action/ViewActionBeanTest.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/tests/com/ecyrd/jspwiki/action/ViewActionBeanTest.java?rev=728835&r1=728834&r2=728835&view=diff
==============================================================================
--- incubator/jspwiki/trunk/tests/com/ecyrd/jspwiki/action/ViewActionBeanTest.java (original)
+++ incubator/jspwiki/trunk/tests/com/ecyrd/jspwiki/action/ViewActionBeanTest.java Mon Dec 22 16:45:07 2008
@@ -65,8 +65,8 @@
         ViewActionBean bean = trip.getActionBean(ViewActionBean.class);
         assertEquals( page, bean.getPage() );
         
-        // ...and the destination should be Wiki.jsp (aka /View.action)
-        assertEquals("/Wiki.action", trip.getDestination() );
+        // ...and the destination should be Wiki.jsp (aka display JSP)
+        assertEquals("/Wiki.jsp", trip.getDestination() );
     }
     
     public void testViewNoParameter() throws Exception {
@@ -84,8 +84,8 @@
         page = m_engine.getPage("Main");
         assertEquals( page, bean.getPage() );
         
-        // ...and the destination should be Wiki.jsp (aka /View.action)
-        assertEquals("/Wiki.action", trip.getDestination() );
+        // ...and the destination should be Wiki.jsp (aka display JSP)
+        assertEquals("/Wiki.jsp", trip.getDestination() );
     }
     
     public void testSpecialPage() throws Exception {