Author: ajaquith
Date: Tue Feb 23 22:02:47 2010
New Revision: 915545

URL: http://svn.apache.org/viewvc?rev=915545&view=rev
Log:
Useful tweaks to the AJAX-related Stripes JavaScript. The client-side 
JavaScript now injects validation errors and messages that were generated 
server-side by an EventResolution, using the same prefixes and headers defined 
in CoreResources for "normal" JSP validation errors and messages. Generation of 
an EventResolution for AJAX methods that generate errors is automatic when the 
event method is annotated with @AjaxEvent. Practically speaking, what it means 
is this: to call an Stripes method via Ajax, just annotate the method with 
@AjaxEvent. Then, client-side scripts need only call the JavaScript function 
Stripes.submitFormEvent(). The rest is MAGIC.

Added:
    incubator/jspwiki/trunk/src/java/org/apache/wiki/ui/stripes/AjaxEvent.java
Modified:
    
incubator/jspwiki/trunk/src/WebContent/WEB-INF/classes/CoreResources.properties
    
incubator/jspwiki/trunk/src/WebContent/WEB-INF/classes/CoreResources_es.properties
    
incubator/jspwiki/trunk/src/WebContent/WEB-INF/classes/CoreResources_nl.properties
    incubator/jspwiki/trunk/src/WebContent/scripts/jspwiki-common.js
    incubator/jspwiki/trunk/src/java/org/apache/wiki/action/EditActionBean.java
    
incubator/jspwiki/trunk/src/java/org/apache/wiki/action/EditDialogActionBean.java
    
incubator/jspwiki/trunk/src/java/org/apache/wiki/action/InstallActionBean.java
    
incubator/jspwiki/trunk/src/java/org/apache/wiki/action/SearchActionBean.java
    
incubator/jspwiki/trunk/src/java/org/apache/wiki/ui/stripes/EventResolution.java
    
incubator/jspwiki/trunk/src/java/org/apache/wiki/ui/stripes/WikiInterceptor.java

Modified: 
incubator/jspwiki/trunk/src/WebContent/WEB-INF/classes/CoreResources.properties
URL: 
http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/WebContent/WEB-INF/classes/CoreResources.properties?rev=915545&r1=915544&r2=915545&view=diff
==============================================================================
--- 
incubator/jspwiki/trunk/src/WebContent/WEB-INF/classes/CoreResources.properties 
(original)
+++ 
incubator/jspwiki/trunk/src/WebContent/WEB-INF/classes/CoreResources.properties 
Tue Feb 23 22:02:47 2010
@@ -303,10 +303,6 @@
 install.ldap.title=LDAP Configuration
 #Formerly named install.jsp.instr.submit.
 org.apache.wiki.action.InstallActionBean.save=Configure!
-org.apache.wiki.action.InstallActionBean.testLdapConnection=Test connection
-org.apache.wiki.action.InstallActionBean.testLdapAuthentication=Test 
authentication
-org.apache.wiki.action.InstallActionBean.testLdapUsers=Test user lookups
-org.apache.wiki.action.InstallActionBean.testLdapRoles=Test role lookups
 Default.ACTIVE_DIRECTORY=Active Directory
 Default.OPEN_LDAP=OpenLDAP
 test.connection=Test connection
@@ -396,3 +392,11 @@
 SearchScope.ATTACHMENTS=Attachments
 #Copied from src/WebContent/WEB-INF/classes/templates/default.properties.. 
Formerly named find.scope.pagename.. Formerly named SearchScope.PAGE_NAME.
 SearchScope.PAGE_NAMES=Page names
+#Formerly named org.apache.wiki.action.InstallActionBean.testLdapConnection.
+org.apache.wiki.action.InstallActionBean.ldapConnection=Test connection
+#Formerly named 
org.apache.wiki.action.InstallActionBean.testLdapAuthentication.
+org.apache.wiki.action.InstallActionBean.ldapAuthentication=Test authentication
+#Formerly named org.apache.wiki.action.InstallActionBean.testLdapUsers.
+org.apache.wiki.action.InstallActionBean.ldapUsers=Test user lookups
+#Formerly named org.apache.wiki.action.InstallActionBean.testLdapRoles.
+org.apache.wiki.action.InstallActionBean.ldapRoles=Test role lookups

Modified: 
incubator/jspwiki/trunk/src/WebContent/WEB-INF/classes/CoreResources_es.properties
URL: 
http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/WebContent/WEB-INF/classes/CoreResources_es.properties?rev=915545&r1=915544&r2=915545&view=diff
==============================================================================
--- 
incubator/jspwiki/trunk/src/WebContent/WEB-INF/classes/CoreResources_es.properties
 (original)
+++ 
incubator/jspwiki/trunk/src/WebContent/WEB-INF/classes/CoreResources_es.properties
 Tue Feb 23 22:02:47 2010
@@ -290,10 +290,6 @@
 attach.bad.filetype=No puede adjuntarse el fichero {2} debido a que contiene 
una extensión no permitida
 #Formerly named install.jsp.instr.submit.
 org.apache.wiki.action.InstallActionBean.save=¡Configura!
-org.apache.wiki.action.InstallActionBean.testLdapConnection=Probar conexión
-org.apache.wiki.action.InstallActionBean.testLdapAuthentication=Probar 
autenticación
-org.apache.wiki.action.InstallActionBean.testLdapUsers=Probar búsquedas de 
usuario
-org.apache.wiki.action.InstallActionBean.testLdapRoles=Probar búsquedas de rol
 Default.ACTIVE_DIRECTORY=Active Directory
 Default.OPEN_LDAP=OpenLDAP
 test.connection=Probar conexión
@@ -365,3 +361,11 @@
 SearchScope.ATTACHMENTS=Adjuntos
 #Copied from src/WebContent/WEB-INF/classes/templates/default.properties.. 
Formerly named find.scope.pagename.. Formerly named SearchScope.PAGE_NAME.
 SearchScope.PAGE_NAMES=Nombre de las Páginas
+#Formerly named org.apache.wiki.action.InstallActionBean.testLdapConnection.
+org.apache.wiki.action.InstallActionBean.ldapConnection=Probar conexión
+#Formerly named 
org.apache.wiki.action.InstallActionBean.testLdapAuthentication.
+org.apache.wiki.action.InstallActionBean.ldapAuthentication=Probar 
autenticación
+#Formerly named org.apache.wiki.action.InstallActionBean.testLdapUsers.
+org.apache.wiki.action.InstallActionBean.ldapUsers=Probar búsquedas de usuario
+#Formerly named org.apache.wiki.action.InstallActionBean.testLdapRoles.
+org.apache.wiki.action.InstallActionBean.ldapRoles=Probar búsquedas de rol

Modified: 
incubator/jspwiki/trunk/src/WebContent/WEB-INF/classes/CoreResources_nl.properties
URL: 
http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/WebContent/WEB-INF/classes/CoreResources_nl.properties?rev=915545&r1=915544&r2=915545&view=diff
==============================================================================
--- 
incubator/jspwiki/trunk/src/WebContent/WEB-INF/classes/CoreResources_nl.properties
 (original)
+++ 
incubator/jspwiki/trunk/src/WebContent/WEB-INF/classes/CoreResources_nl.properties
 Tue Feb 23 22:02:47 2010
@@ -244,10 +244,6 @@
 install.ldap.title=LDAP Configuratie
 #Formerly named install.jsp.instr.submit.
 org.apache.wiki.action.InstallActionBean.save=Configureer!
-org.apache.wiki.action.InstallActionBean.testLdapConnection=Test verbinding
-org.apache.wiki.action.InstallActionBean.testLdapAuthentication=Test 
authenticatie
-org.apache.wiki.action.InstallActionBean.testLdapUsers=Test gebruikers lookups
-org.apache.wiki.action.InstallActionBean.testLdapRoles=Test rol lookups
 Default.ACTIVE_DIRECTORY=Actieve Directory
 Default.OPEN_LDAP=OpenLDAP
 test.connection=Test verbinding
@@ -325,3 +321,11 @@
 SearchScope.ATTACHMENTS=Bijlagen
 #Copied from src/WebContent/WEB-INF/classes/templates/default.properties.. 
Formerly named find.scope.pagename.. Formerly named SearchScope.PAGE_NAME.
 SearchScope.PAGE_NAMES=Pagina naam
+#Formerly named org.apache.wiki.action.InstallActionBean.testLdapConnection.
+org.apache.wiki.action.InstallActionBean.ldapConnection=Test verbinding
+#Formerly named 
org.apache.wiki.action.InstallActionBean.testLdapAuthentication.
+org.apache.wiki.action.InstallActionBean.ldapAuthentication=Test authenticatie
+#Formerly named org.apache.wiki.action.InstallActionBean.testLdapUsers.
+org.apache.wiki.action.InstallActionBean.ldapUsers=Test gebruikers lookups
+#Formerly named org.apache.wiki.action.InstallActionBean.testLdapRoles.
+org.apache.wiki.action.InstallActionBean.ldapRoles=Test rol lookups

Modified: incubator/jspwiki/trunk/src/WebContent/scripts/jspwiki-common.js
URL: 
http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/WebContent/scripts/jspwiki-common.js?rev=915545&r1=915544&r2=915545&view=diff
==============================================================================
--- incubator/jspwiki/trunk/src/WebContent/scripts/jspwiki-common.js (original)
+++ incubator/jspwiki/trunk/src/WebContent/scripts/jspwiki-common.js Tue Feb 23 
22:02:47 2010
@@ -1915,8 +1915,8 @@
        render: function(page, name){
                page = $(page); if(!page) return;
 
-               var cookie = Wiki.Context.test(/view|edit|comment/) ? 
"JSPWiki"+name : "";
-               //var cookie = "";  //activate this line if you want to 
deactivatie cookie handling
+               //var cookie = Wiki.Context.test(/view|edit|comment/) ? 
"JSPWiki"+name : "";
+               var cookie = "";  //activate this line if you want to 
deactivatie cookie handling
 
                if(!this.bullet) {
                        this.bullet = new 
Element('div',{'class':'collapseBullet'}).set('html','•');
@@ -2929,15 +2929,18 @@
                 event method must return an EventResolution, the response
                 for which will be eval'ed and be assigned to the variable
                 'eventResponse.' See 
org.apache.wiki.ui.stripes.EventResolution.
-    divTarget - if the 'eventResponse' variable returned by the AJAX call
-                has an 'html' property whose boolean value is true,
-                the results will be injected into this target div.
+    divTarget - if the 'callback' function is not supplied, the results 
returned
+                by the AJAX call will be injected into this target div as a
+                single string that includes the HTML representation of any 
Stripes
+                messages or validation errors prepended, plus the result 
object(s).
+                The entire string will be wrapped in a <div> whose class is
+                "eventResponse".
     callback -  a callback function to invoke. The 'eventResponse' variable
-                will be passed to this function. It contains the response
-                object, which can be any primitive type, an array, map
+                will be passed to this function as a parameter. It contains the
+                response object, which can be any primitive type, an array, map
                 or anything supported by 
net.sourceforge.stripes.ajax.JavaScriptBuilder.
-                It also contains a list of validation errors messages and
-                the 'html' boolean property.
+                It also contains two properties that contain HTML 
representations of
+                any errors or Stripes messages set server-side.
   */
   submitFormEvent: function( formName, event, divTarget, callback ){
     var form = $(formName);
@@ -2949,12 +2952,17 @@
       method: 'post',
       evalResponse: true,
       onComplete: function(response) {
-        // If HTML response, put results into the div
-        if (eventResponse.html) {
+        // If no custom callback function supplied, put results into the div
+        if (!callback) {
+          var newContent = '<div class="eventResponse">';
+          if (eventResponse.errors) { newContent += eventResponse.errors; }
+          if (eventResponse.messages) { newContent += eventResponse.messages; }
+          if (eventResponse.results) { newContent += eventResponse.results; }
+          newContent += "</div>";
           $(divTarget).empty();
-          $(divTarget).set('html',eventResponse.results);
+          $(divTarget).set('html',newContent);
         }
-        // Call the callback function
+        // Otherwise, call the callback function
         if (callback) {
           callback(eventResponse);
         }

Modified: 
incubator/jspwiki/trunk/src/java/org/apache/wiki/action/EditActionBean.java
URL: 
http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/java/org/apache/wiki/action/EditActionBean.java?rev=915545&r1=915544&r2=915545&view=diff
==============================================================================
--- incubator/jspwiki/trunk/src/java/org/apache/wiki/action/EditActionBean.java 
(original)
+++ incubator/jspwiki/trunk/src/java/org/apache/wiki/action/EditActionBean.java 
Tue Feb 23 22:02:47 2010
@@ -107,6 +107,7 @@
      * @return always returns a {...@link EventResolution} containing the
      * results
      */
+    @AjaxEvent
     @HandlesEvent( "preview" )
     @HandlerPermission( permissionClass = PagePermission.class, target = 
"${page.path}", actions = PagePermission.VIEW_ACTION )
     @WikiRequestContext( "preview" )
@@ -123,7 +124,7 @@
         String result = renderer.getHTML( context, doc );
 
         // Return an EventResolution
-        Resolution r = new EventResolution( context, result, true );
+        Resolution r = new EventResolution( context, result );
         return r;
     }
 

Modified: 
incubator/jspwiki/trunk/src/java/org/apache/wiki/action/EditDialogActionBean.java
URL: 
http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/java/org/apache/wiki/action/EditDialogActionBean.java?rev=915545&r1=915544&r2=915545&view=diff
==============================================================================
--- 
incubator/jspwiki/trunk/src/java/org/apache/wiki/action/EditDialogActionBean.java
 (original)
+++ 
incubator/jspwiki/trunk/src/java/org/apache/wiki/action/EditDialogActionBean.java
 Tue Feb 23 22:02:47 2010
@@ -80,6 +80,6 @@
             log.debug("Suggestion request for "+wikiName+" done in "+sw );
         }
         
-        return new EventResolution( getContext(), list, false );
+        return new EventResolution( getContext(), list );
     }
 }

Modified: 
incubator/jspwiki/trunk/src/java/org/apache/wiki/action/InstallActionBean.java
URL: 
http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/java/org/apache/wiki/action/InstallActionBean.java?rev=915545&r1=915544&r2=915545&view=diff
==============================================================================
--- 
incubator/jspwiki/trunk/src/java/org/apache/wiki/action/InstallActionBean.java 
(original)
+++ 
incubator/jspwiki/trunk/src/java/org/apache/wiki/action/InstallActionBean.java 
Tue Feb 23 22:02:47 2010
@@ -38,9 +38,11 @@
 import javax.servlet.ServletContext;
 
 import net.sourceforge.stripes.action.*;
-import net.sourceforge.stripes.ajax.JavaScriptResolution;
 import net.sourceforge.stripes.controller.LifecycleStage;
-import net.sourceforge.stripes.validation.*;
+import net.sourceforge.stripes.validation.SimpleError;
+import net.sourceforge.stripes.validation.Validate;
+import net.sourceforge.stripes.validation.ValidateNestedProperties;
+import net.sourceforge.stripes.validation.ValidationErrors;
 
 import org.apache.wiki.WikiEngine;
 import org.apache.wiki.auth.AuthenticationManager;
@@ -53,7 +55,7 @@
 import org.apache.wiki.auth.user.UserDatabase;
 import org.apache.wiki.auth.user.UserProfile;
 import org.apache.wiki.auth.user.XMLUserDatabase;
-import org.apache.wiki.ui.stripes.WikiRequestContext;
+import org.apache.wiki.ui.stripes.*;
 import org.apache.wiki.util.CommentedProperties;
 import org.apache.wiki.util.CryptoUtil;
 import org.apache.wiki.util.PropertyReader;
@@ -61,7 +63,7 @@
 import org.freshcookies.security.Keychain;
 
 @HttpCache( allow = false )
-public class InstallActionBean extends AbstractActionBean implements 
ValidationErrorHandler
+public class InstallActionBean extends AbstractActionBean
 {
     /**
      * Wrapper class that encapsulates a properties file as a Stripes-friendly
@@ -403,29 +405,6 @@
     }
 
     /**
-     * Intercepts any validation errors generated by the various AJAX "text"
-     * methods and returns them to the client as an array of JSON-encoded
-     * strings.
-     */
-    public Resolution handleValidationErrors( ValidationErrors errors ) throws 
Exception
-    {
-        String event = getContext().getEventName();
-        if( event.startsWith( "test" ) && errors.size() > 0 )
-        {
-            Set<String> errorStrings = new HashSet<String>();
-            for( Map.Entry<String, List<ValidationError>> errorList : 
errors.entrySet() )
-            {
-                for( ValidationError error : errorList.getValue() )
-                {
-                    errorStrings.add( error.getMessage( 
getContext().getLocale() ) );
-                }
-            }
-            return new JavaScriptResolution( errorStrings.toArray( new 
String[errorStrings.size()] ) );
-        }
-        return null;
-    }
-
-    /**
      * Pre-action that loads the JSPWiki properties file before user-supplied
      * parameters are bound to the ActionBean.
      * 
@@ -482,9 +461,9 @@
         initKeychain( path, jspwiki );
 
         // Set some sensible defaults
-        if( !jspwiki.containsKey( CONFIG_BASE_URL ) )
+        if( !jspwiki.containsKey( CONFIG_BASE_URL ) || jspwiki.get( 
CONFIG_BASE_URL ).trim().length() ==  0 )
         {
-            jspwiki.put( CONFIG_BASE_URL, "http://localhost:8080/JSPWiki"; );
+            jspwiki.put( CONFIG_BASE_URL, "http://localhost:8080/JSPWiki/"; );
         }
         if( !jspwiki.containsKey( CONFIG_USERDATABASE ) )
         {
@@ -513,9 +492,10 @@
 
     /**
      * Default Stripes event handler method that loads up the current
-     * configuration settings and forwards the user to the display JSP.
+     * configuration settings and forwards the user to the template JSP.
      * 
-     * @return always returns a ForwardResolution to {...@code 
/admin/Install.jsp}
+     * @return always returns a TemplateResolution to template JSP
+     * {...@code admin/Install.jsp}
      */
     @DefaultHandler
     @DontValidate
@@ -523,14 +503,14 @@
     @WikiRequestContext( "install" )
     public Resolution install()
     {
-        return new ForwardResolution( "/admin/Install.jsp" );
+        return new TemplateResolution( "admin/Install.jsp" );
     }
 
     /**
      * Saves the properties files with updated settings, and restarts the wiki.
      * 
      * @return if successful, always returns a {...@link ForwardResolution} to
-     *         {...@code /admin/InstallSuccess.jsp}.
+     *         template JSP {...@code admin/InstallSuccess.jsp}.
      * @throws Exception
      */
     @HandlesEvent( "save" )
@@ -580,7 +560,7 @@
         WikiEngine engine = getContext().getEngine();
         engine.restart();
 
-        return new RedirectResolution( "/admin/InstallSuccess.jsp" );
+        return new TemplateResolution( "admin/InstallSuccess.jsp" );
     }
 
     /**
@@ -645,11 +625,13 @@
 
     /**
      * AJAX event method that tests LDAP authentication based on the bind-user
-     * settings, returning any results as an array of JavaScript strings.
+     * settings, returning an {...@link EventResolution} whose response object 
is
+     * an array of strings.
      * 
      * @return the results
      * @throws WikiSecurityException
      */
+    @AjaxEvent
     @HandlesEvent( "testLdapAuthentication" )
     public Resolution testLdapAuthentication() throws WikiSecurityException
     {
@@ -661,118 +643,126 @@
 
     /**
      * AJAX event method that tests the connection to the LDAP server, 
returning
-     * any results as an array of JavaScript strings.
+     * an {...@link EventResolution} whose response object is
+     * an array of strings.
      * 
      * @return the results
      * @throws WikiSecurityException
      */
+    @AjaxEvent
     @HandlesEvent( "testLdapConnection" )
     public Resolution testLdapConnection() throws WikiSecurityException
     {
-        List<String> messages = new ArrayList<String>();
+        WikiActionBeanContext context = getContext();
         try
         {
-            initLdapConnection( messages );
-            messages.add( "Success!" );
+            initLdapConnection();
+            List<Message> messages = context.getMessages();
+            messages.add( new SimpleMessage( "Success!" ) );
         }
         catch( Exception e )
         {
-            messages.add( "Error: " + e.getMessage() );
+            ValidationErrors errors = context.getValidationErrors();
+            errors.addGlobalError( new SimpleError( e.getMessage() ) );
             if( e.getCause() != null )
             {
-                messages.add( " Cause: " + e.getCause().getMessage() );
+                errors.addGlobalError( new SimpleError( " Cause: " + 
e.getCause().getMessage() ) );
             }
         }
-        Resolution r = new JavaScriptResolution( messages.toArray( new 
String[messages.size()] ) );
-        return r;
+        return new EventResolution( getContext() );
     }
 
     /**
      * AJAX event method that tests the LDAP role lookups based on the
-     * configured user base.
+     * configured user base, returning an {...@link EventResolution}
+     * whose response object is an array of strings.
      * 
      * @return the results
      * @throws WikiSecurityException
      */
+    @AjaxEvent
     @HandlesEvent( "testLdapRoles" )
     public Resolution testLdapRoleLookup() throws WikiSecurityException
     {
-        WikiEngine engine = getContext().getEngine();
+        WikiActionBeanContext context = getContext();
         PropertiesMap<String, String> jspwiki = m_properties.get( "jspwiki" );
-        List<String> messages = new ArrayList<String>();
         try
         {
             // Initialize a new user authorizer
             Authorizer authorizer = new LdapAuthorizer();
-            authorizer.initialize( engine, jspwiki.m_props );
+            authorizer.initialize( context.getEngine(), jspwiki.m_props );
 
             // Count roles
+            List<Message> messages = context.getMessages();
             Principal[] principals = authorizer.getRoles();
-            messages.add( "Found " + principals.length + " roles." );
+            messages.add( new SimpleMessage( "Found " + principals.length + " 
roles." ) );
 
             // Get first role details
             if( principals.length > 0 )
             {
-                messages.add( "Details for first role..." );
-                messages.add( "Name: " + principals[0].getName() );
+                messages.add( new SimpleMessage( "Details for first role..." ) 
);
+                messages.add( new SimpleMessage( "Name: " + 
principals[0].getName() ) );
             }
         }
         catch( Exception e )
         {
-            messages.add( "Error: " + e.getMessage() );
+            ValidationErrors errors = context.getValidationErrors();
+            errors.addGlobalError( new SimpleError( "Error: " + e.getMessage() 
) );
             if( e.getCause() != null )
             {
-                messages.add( " Cause: " + e.getCause().getMessage() );
+                errors.addGlobalError( new SimpleError( " Cause: " + 
e.getCause().getMessage() ) );
             }
         }
-        return new JavaScriptResolution( messages.toArray( new 
String[messages.size()] ) );
+        return new EventResolution( getContext() );
     }
 
     /**
      * AJAX event method that tests the LDAP user lookups based on the
-     * configured user base. server, returning any results as an array of
-     * JavaScript strings.
+     * configured user base, returning an {...@link EventResolution} whose
+     * response object is an array of strings.
      * 
      * @return the results
      * @throws WikiSecurityException
      */
+    @AjaxEvent
     @HandlesEvent( "testLdapUsers" )
     public Resolution testLdapUserLookup()
     {
-        WikiEngine engine = getContext().getEngine();
+        WikiActionBeanContext context = getContext();
         PropertiesMap<String, String> jspwiki = m_properties.get( "jspwiki" );
-        List<String> messages = new ArrayList<String>();
         try
         {
             // Initialize a new user database
             UserDatabase db = new LdapUserDatabase();
-            db.initialize( engine, jspwiki.m_props );
+            db.initialize( context.getEngine(), jspwiki.m_props );
 
             // Count users
+            List<Message> messages = context.getMessages();
             Principal[] principals = db.getWikiNames();
-            messages.add( "Found " + principals.length + " users." );
+            messages.add( new SimpleMessage( "Found " + principals.length + " 
users." ) );
 
             // Get first user details
             if( principals.length > 0 )
             {
                 UserProfile user = db.findByWikiName( principals[0].getName() 
);
-                messages.add( "Details for first user..." );
-                messages.add( "Uid: " + user.getUid() );
-                messages.add( "Login name: " + user.getLoginName() );
-                messages.add( "Full name: " + user.getFullname() );
-                messages.add( "Wiki name: " + user.getWikiName() );
-                messages.add( "E-mail: " + user.getEmail() );
+                messages.add( new SimpleMessage( "Details for first user..." ) 
);
+                messages.add( new SimpleMessage( "Uid: " + user.getUid() ) );
+                messages.add( new SimpleMessage( "Login name: " + 
user.getLoginName() ) );
+                messages.add( new SimpleMessage( "Full name: " + 
user.getFullname() ) );
+                messages.add( new SimpleMessage( "Wiki name: " + 
user.getWikiName() ) );
+                messages.add( new SimpleMessage( "E-mail: " + user.getEmail() 
) );
             }
         }
         catch( Exception e )
         {
-            messages.add( "Error: " + e.getMessage() );
+            ValidationErrors errors = context.getValidationErrors();
+            errors.addGlobalError( new SimpleError( e.getMessage() ) );
             if( e.getCause() != null )
             {
-                messages.add( " Cause: " + e.getCause().getMessage() );
+                errors.addGlobalError( new SimpleError( " Cause: " + 
e.getCause().getMessage() ) );
             }
         }
-        return new JavaScriptResolution( messages.toArray( new 
String[messages.size()] ) );
+        return new EventResolution( getContext() );
     }
 
     /**
@@ -828,22 +818,23 @@
      * @throws NamingException if a connection cannot be made to the LDAP 
server
      *             for any reason
      */
-    private LdapContext initLdapConnection( List<String> messages ) throws 
NamingException
+    private LdapContext initLdapConnection() throws NamingException
     {
         PropertiesMap<String, String> jspwiki = m_properties.get( "jspwiki" );
         LdapConfig config = LdapConfig.getInstance( m_keychain, 
jspwiki.m_props, new String[0] );
         Hashtable<String, String> env;
+        List<Message> messages = getContext().getMessages();
         if( !jspwiki.containsKey( "ldap_bindUser" ) )
         {
             env = config.newJndiEnvironment();
-            messages.add( "Binding as anonymous user." );
+            messages.add( new SimpleMessage( "Binding as anonymous user." ) );
         }
         else
         {
             String username = jspwiki.get( "ldap_bindUser" );
             env = config.newJndiEnvironment( username, m_bindPassword );
             Object principal = env.get( Context.SECURITY_PRINCIPAL );
-            messages.add( "Binding with principal: " + principal.toString() );
+            messages.add( new SimpleMessage( "Binding with principal: " + 
principal.toString() ) );
         }
         return new InitialLdapContext( env, null );
     }

Modified: 
incubator/jspwiki/trunk/src/java/org/apache/wiki/action/SearchActionBean.java
URL: 
http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/java/org/apache/wiki/action/SearchActionBean.java?rev=915545&r1=915544&r2=915545&view=diff
==============================================================================
--- 
incubator/jspwiki/trunk/src/java/org/apache/wiki/action/SearchActionBean.java 
(original)
+++ 
incubator/jspwiki/trunk/src/java/org/apache/wiki/action/SearchActionBean.java 
Tue Feb 23 22:02:47 2010
@@ -36,6 +36,7 @@
 import org.apache.wiki.log.Logger;
 import org.apache.wiki.log.LoggerFactory;
 import org.apache.wiki.search.SearchResult;
+import org.apache.wiki.ui.stripes.AjaxEvent;
 import org.apache.wiki.ui.stripes.EventResolution;
 import org.apache.wiki.ui.stripes.TemplateResolution;
 import org.apache.wiki.ui.stripes.WikiRequestContext;
@@ -214,9 +215,10 @@
      * for this ActionBean. Results are streamed back to the client as an array
      * of JSON-encoded SearchResult objects.
      * 
-     * @return always returns a {...@link JavaScriptResolution} containing the
+     * @return always returns an {...@link EventResolution} containing the
      *         results; this may be a zero-length array
      */
+    @AjaxEvent
     @HandlesEvent( "ajaxSearch" )
     public Resolution ajaxSearch()
     {
@@ -230,6 +232,7 @@
      * @return a {...@link EventResolution} containing HTML to be inserted 
into an
      *         element.
      */
+    @AjaxEvent
     @HandlesEvent( "quickSearch" )
     public Resolution quickSearch()
     {
@@ -250,6 +253,6 @@
             b.append( "</ul>" );
             html = b.toString();
         }
-        return new EventResolution( getContext(), html, true );
+        return new EventResolution( getContext(), html );
     }
 }

Added: 
incubator/jspwiki/trunk/src/java/org/apache/wiki/ui/stripes/AjaxEvent.java
URL: 
http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/java/org/apache/wiki/ui/stripes/AjaxEvent.java?rev=915545&view=auto
==============================================================================
--- incubator/jspwiki/trunk/src/java/org/apache/wiki/ui/stripes/AjaxEvent.java 
(added)
+++ incubator/jspwiki/trunk/src/java/org/apache/wiki/ui/stripes/AjaxEvent.java 
Tue Feb 23 22:02:47 2010
@@ -0,0 +1,17 @@
+package org.apache.wiki.ui.stripes;
+
+import java.lang.annotation.*;
+
+/**
+ * Method-level annotation indicating that an event is an AJAX event. Any
+ * validation errors will be intercepted after the
+ * {...@link 
net.sourceforge.stripes.controller.LifecycleStage#CustomValidation}
+ * stage and returned as an {...@link 
org.apache.wiki.ui.stripes.EventResolution}.
+ */
+...@documented
+...@inherited
+...@retention( value = RetentionPolicy.RUNTIME )
+...@target( { ElementType.METHOD } )
+public @interface AjaxEvent
+{
+}

Modified: 
incubator/jspwiki/trunk/src/java/org/apache/wiki/ui/stripes/EventResolution.java
URL: 
http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/java/org/apache/wiki/ui/stripes/EventResolution.java?rev=915545&r1=915544&r2=915545&view=diff
==============================================================================
--- 
incubator/jspwiki/trunk/src/java/org/apache/wiki/ui/stripes/EventResolution.java
 (original)
+++ 
incubator/jspwiki/trunk/src/java/org/apache/wiki/ui/stripes/EventResolution.java
 Tue Feb 23 22:02:47 2010
@@ -1,8 +1,9 @@
 package org.apache.wiki.ui.stripes;
 
-import java.util.ArrayList;
 import java.util.List;
-import java.util.Set;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
@@ -11,6 +12,9 @@
 import net.sourceforge.stripes.action.Message;
 import net.sourceforge.stripes.action.Resolution;
 import net.sourceforge.stripes.ajax.JavaScriptBuilder;
+import net.sourceforge.stripes.controller.StripesFilter;
+import net.sourceforge.stripes.tag.ErrorsTag;
+import net.sourceforge.stripes.tag.MessagesTag;
 import net.sourceforge.stripes.validation.ValidationError;
 import net.sourceforge.stripes.validation.ValidationErrors;
 
@@ -22,67 +26,82 @@
  * ActionBeanContext messages and validation errors. It has four properties:
  * </p>
  * <ul>
- * <li>{...@code results} - the Object that contains the results of the AJAX 
call.
- * This can be just about anything that
- * {...@link net.sourceforge.stripes.ajax.JavaScriptBuilder} can encode.</li>
- * <li>{...@code fieldErrors} - any field-level errors</li>
- * <li>{...@code globalErrors} - any global errors</li>
- * <li>{...@code messages} - any messages</li>
- * <li>{...@code isHtml} - whether the results should be treated as HTML (for
- * example, to inject directly into a {...@code div})</li>
+ * <li>{...@code results} - the Object (or an array of Objects) that represents
+ * the results of the AJAX call. The object or objects can be just about 
anything that
+ * {...@link net.sourceforge.stripes.ajax.JavaScriptBuilder} can encode.
+ * It is most commonly a single HTML string.</li>
+ * <li>{...@code errors} - any global or field-level errors, as an HTML 
string. If
+ * no errors, this property will be {...@code null}</li>
+ * <li>{...@code messages} - any messages, as an HTML string. If no messages, 
this
+ * property will be {...@code null}</li>
  * </ul>
+ * <p>
+ * Error and message strings are generated in the same way that the Stripes
+ * {...@link net.sourceforge.stripes.tag.ErrorsTag} and
+ * {...@link net.sourceforge.stripes.tag.MessagesTag} work. For errors, the
+ * contents of i18n message {...@code stripes.errors.header} is prepended. 
Then,
+ * for each error, the message {...@code stripes.errors.beforeError} is added, 
then
+ * the error text, then {...@code stripes.errors.afterError}. Finally, the 
message
+ * {...@code stripes.errors.footer} is appended. For messages, the string is
+ * generated the same way except that the messages {...@code
+ * stripes.messages.header}, {...@code stripes.messages.beforeMessage}, 
{...@code
+ * stripes.messages.afterMessage} and {...@code stripes.messages.footer} are 
used.
+ * </p>
  */
 public class EventResolution implements Resolution
 {
+    /**
+     * Lightweight class that represents the result of an AJAX call, including
+     * any Stripes messages or validation errors.
+     */
     public static class Result
     {
-        private final List<ValidationError> m_globalErrors = new 
ArrayList<ValidationError>();
-
-        private final List<ValidationError> m_fieldErrors = new 
ArrayList<ValidationError>();
+        private final String m_errors;
 
-        private final List<Message> m_messages;
+        private final String m_messages;
 
         private final Object m_rootObject;
 
-        private final boolean m_isHtml;
-
         /**
          * Constructs a new EventResolution.
          * 
          * @param context the ActionBeanContext that supplies the messages and
          *            validation errors.
-         * @param rootObject the Object to be encoded
-         * @param isHtml whether the results should be interpreted as HTML
+         * @param objects zero or more Objects to be returned
          */
-        public Result( ActionBeanContext context, Object rootObject, boolean 
isHtml )
+        public Result( ActionBeanContext context, Object... objects )
         {
             super();
 
             // Set the messages
-            m_messages = context.getMessages();
+            m_messages = context.getMessages().size() > 0 ? generateMessages( 
context ) : null;
 
             // Set the validation errors
-            ValidationErrors errors = context.getValidationErrors();
-            Set<String> fields = errors.keySet();
-            for( String field : fields )
+            m_errors = context.getValidationErrors().size() > 0 ? 
generateErrors( context ) : null;
+
+            // Set the root object
+            if ( objects.length == 1 )
             {
-                List<ValidationError> fieldErrors = errors.get( field );
-                if( fieldErrors != null )
-                {
-                    if( ValidationErrors.GLOBAL_ERROR.equals( field ) )
-                    {
-                        m_globalErrors.addAll( fieldErrors );
-                    }
-                    else
-                    {
-                        m_fieldErrors.addAll( fieldErrors );
-                    }
-                }
+                m_rootObject = objects[0];
+            }
+            else if ( objects.length > 0 )
+            {
+                m_rootObject = objects;
             }
+            else
+            {
+                m_rootObject = null;
+            }
+        }
 
-            // Set the root object & HTML properties
-            m_rootObject = rootObject;
-            m_isHtml = isHtml;
+        /**
+         * Return the errors set by the event.
+         * 
+         * @return the errors
+         */
+        public String getErrors()
+        {
+            return m_errors;
         }
 
         /**
@@ -90,58 +109,129 @@
          * 
          * @return the messages
          */
-        public List<Message> getMessages()
+        public String getMessages()
         {
             return m_messages;
         }
 
         /**
-         * Return the global ValidationErrors set by the event.
+         * Returns the result of the AJAX call. If no objects were passed
+         * to the constructor, this method returns {...@code null}. If one
+         * was passed, that object will be returned. Otherwise, an array of
+         * objects will be returned.
          * 
-         * @return the errors
+         * @return the root object; {...@code null}, one object, or an array 
of objects
          */
-        public List<ValidationError> getGlobalErrors()
+        public Object getResults()
         {
-            return m_globalErrors;
+            return m_rootObject;
         }
 
         /**
-         * Return the field-level ValidationErrors set by the event.
-         * 
-         * @return the errors
-         */
-        public List<ValidationError> getFieldErrors()
-        {
-            return m_fieldErrors;
+         * Generates an HTML-formatted String representing the errors 
contained by
+         * an ActionBeanContext. Global errors will be first, followed by
+         * field-level errors. The contents will be localized according to the
+         * user's locale.
+         * 
+         * @param context the context
+         * @return the form
+         */
+        private String generateErrors( ActionBeanContext context )
+        {
+            Locale locale = context.getLocale();
+            ResourceBundle bundle = 
StripesFilter.getConfiguration().getLocalizationBundleFactory().getErrorMessageBundle(
 locale );
+
+            // Fetch the error headers and footers
+            String header = getMessage( bundle, "stripes.errors.header", 
ErrorsTag.DEFAULT_HEADER );
+            String footer = getMessage( bundle, "stripes.errors.footer", 
ErrorsTag.DEFAULT_FOOTER );
+            String beforeError = getMessage( bundle, 
"stripes.errors.beforeError", "<li>" );
+            String afterError = getMessage( bundle, 
"stripes.errors.afterError", "</li>" );
+
+            // Write out the error messages
+            StringBuilder s = new StringBuilder();
+            s.append( header );
+            ValidationErrors errors = context.getValidationErrors();
+            for( List<ValidationError> errorList : errors.values() )
+            {
+                for ( ValidationError error : errorList )
+                {
+                    s.append( beforeError );
+                    s.append( error.getMessage( locale ) );
+                    s.append( afterError );
+                }
+            }
+            s.append( footer );
+            return s.toString();
         }
 
         /**
-         * Returns the root object.
+         * Generates an HTML-formatted String representing the messages 
contained by
+         * an ActionBeanContext. The contents will be localized according to 
the
+         * user's locale.
          * 
-         * @return the root object
+         * @param context the context
+         * @return the form
          */
-        public Object getResults()
+        private String generateMessages( ActionBeanContext context )
         {
-            return m_rootObject;
+            Locale locale = context.getLocale();
+            ResourceBundle bundle = 
StripesFilter.getConfiguration().getLocalizationBundleFactory().getErrorMessageBundle(
 locale );
+
+            // Fetch the message headers and footers
+            String header = getMessage( bundle, "stripes.messages.header", 
MessagesTag.DEFAULT_HEADER );
+            String footer = getMessage( bundle, "stripes.messages.footer", 
MessagesTag.DEFAULT_FOOTER );
+            String beforeMessage = getMessage( bundle, 
"stripes.messages.beforeMessage", "<li>" );
+            String afterMessage = getMessage( bundle, 
"stripes.messages.afterMessage", "</li>" );
+
+            // Write out the error messages
+            StringBuilder s = new StringBuilder();
+            s.append( header );
+            for( Message message : context.getMessages() )
+            {
+                s.append( beforeMessage );
+                s.append( message.getMessage( locale ) );
+                s.append( afterMessage );
+            }
+            s.append( footer );
+            return s.toString();
         }
 
         /**
-         * Returns {...@code true} if the result should be interpreted as HTML,
-         * rather than as a JavaScript object that should be {...@code 
eval}-ed.
-         * 
-         * @return the result
+         * Looks up a message in a MessageBundle with a given key. If the key 
is not
+         * found in the ResourceBundle, a default value is returned. 
+         * @param bundle the message bundle
+         * @param key the message key to loop up
+         * @param defaultValue the value to return if the message is not found
+         * @return
          */
-        public boolean isHtml()
+        private String getMessage( ResourceBundle bundle, String key, String 
defaultValue )
         {
-            return m_isHtml;
+            String value = null;
+            try
+            {
+                value = bundle.getString( key );
+            }
+            catch( MissingResourceException mre )
+            {
+                value = defaultValue;
+            }
+            return value;
         }
     }
 
     private final JavaScriptBuilder m_builder;
-
-    public EventResolution( ActionBeanContext context, Object rootObject, 
boolean isHtml )
+    
+    /**
+     * Constructs a new EventResolution for a supplied ActionBeanContext and
+     * result objects.
+     * @param context the ActionBeanContext
+     * @param objects zero, one or more objects that represent the result
+     * of an {...@link org.apache.wiki.ui.stripes.AjaxEvent}-annotated
+     * event method
+     */
+    public EventResolution( ActionBeanContext context, Object... objects )
     {
-        m_builder = new JavaScriptBuilder( new Result( context, rootObject, 
isHtml ) );
+        m_builder = new JavaScriptBuilder( new Result( context, objects ) );
     }
 
     /**

Modified: 
incubator/jspwiki/trunk/src/java/org/apache/wiki/ui/stripes/WikiInterceptor.java
URL: 
http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/java/org/apache/wiki/ui/stripes/WikiInterceptor.java?rev=915545&r1=915544&r2=915545&view=diff
==============================================================================
--- 
incubator/jspwiki/trunk/src/java/org/apache/wiki/ui/stripes/WikiInterceptor.java
 (original)
+++ 
incubator/jspwiki/trunk/src/java/org/apache/wiki/ui/stripes/WikiInterceptor.java
 Tue Feb 23 22:02:47 2010
@@ -32,9 +32,11 @@
 import javax.servlet.jsp.PageContext;
 
 import net.sourceforge.stripes.action.ActionBean;
+import net.sourceforge.stripes.action.ActionBeanContext;
 import net.sourceforge.stripes.action.RedirectResolution;
 import net.sourceforge.stripes.action.Resolution;
 import net.sourceforge.stripes.controller.*;
+import net.sourceforge.stripes.validation.ValidationErrors;
 
 import org.apache.wiki.WikiEngine;
 import org.apache.wiki.WikiSession;
@@ -333,11 +335,14 @@
      * {...@link 
net.sourceforge.stripes.controller.LifecycleStage#CustomValidation}
      * lifecycle stage, to ensure that it runs after all
      * {...@link 
net.sourceforge.stripes.controller.LifecycleStage#BindingAndValidation}
-     * interceptors, and checks for proper access to the current ActionBean and
-     * target event. The access-checking logic runs after after the rest of the
-     * BindingAndValidation processing logic does, after which point Stripes 
has
-     * already discovered the correct ActionBean, and bound and validated its
-     * request parameters.
+     * interceptors, checks for validation errors generated by AJAX event 
methods,
+     * and then checks for proper access to the current ActionBean and
+     * target event.</p>
+     * <p>
+     * To ensure that AJAX-related validation errors are handled correctly, if 
the
+     * current event method is annotated with {...@link 
org.apache.wiki.ui.stripes.AjaxEvent},
+     * an {...@link org.apache.wiki.ui.stripes.EventResolution} will be 
returned
+     * if one or more validation errors were generated earlier in the 
lifecycle.
      * </p>
      * <p>
      * To determine if the user is allowed to access the target event method,
@@ -349,6 +354,11 @@
      * returns <code>false</code> -- this method returns a RedirectResolution 
to
      * the login page, with all current parameters appended.
      * </p>
+     * <p>Both the AJAX-error and access-checking logic run after after the
+     * rest of the BindingAndValidation processing logic does, after which
+     * point Stripes will have already discovered the correct ActionBean,
+     * and bound and validated its request parameters.
+     * </p>
      * 
      * @param context the execution context
      * @return a Resolution if the
@@ -367,9 +377,17 @@
             return r;
         }
 
+        // If handler is AJAX method, check for validation errors (and bail if 
there were any)
+        ActionBeanContext actionBeanContext = context.getActionBeanContext();
+        ValidationErrors errors = actionBeanContext.getValidationErrors();
+        Method handler = context.getHandler();
+        if ( handler.getAnnotation( AjaxEvent.class ) != null && errors.size() 
> 0 )
+        {
+            return new EventResolution( actionBeanContext );
+        }
+
         // Get the event handler method
         WikiActionBean actionBean = (WikiActionBean) context.getActionBean();
-        Method handler = context.getHandler();
         Map<Method, HandlerInfo> eventinfos = 
HandlerInfo.getHandlerInfoCollection( actionBean.getClass() );
         HandlerInfo eventInfo = eventinfos.get( handler );
 


Reply via email to