jspwiki-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ajaqu...@apache.org
Subject svn commit: r896520 - in /incubator/jspwiki/trunk: src/WebContent/templates/default/editors/plain.jsp src/java/org/apache/wiki/tags/SpamProtectTag.java tests/java/org/apache/wiki/TestEngine.java tests/java/org/apache/wiki/action/TestActionBean.java
Date Wed, 06 Jan 2010 16:46:10 GMT
Author: ajaquith
Date: Wed Jan  6 16:46:00 2010
New Revision: 896520

URL: http://svn.apache.org/viewvc?rev=896520&view=rev
Log:
Minor changes to SpamProtectTag. Now accepts a "challenge" attribute, which can have the value
"captcha" or "password," which will cause a challenge of that type to always be rendered,
even on first display. (We will use this for new user account registration, for example.)

Modified:
    incubator/jspwiki/trunk/src/WebContent/templates/default/editors/plain.jsp
    incubator/jspwiki/trunk/src/java/org/apache/wiki/tags/SpamProtectTag.java
    incubator/jspwiki/trunk/tests/java/org/apache/wiki/TestEngine.java
    incubator/jspwiki/trunk/tests/java/org/apache/wiki/action/TestActionBean.java

Modified: incubator/jspwiki/trunk/src/WebContent/templates/default/editors/plain.jsp
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/WebContent/templates/default/editors/plain.jsp?rev=896520&r1=896519&r2=896520&view=diff
==============================================================================
--- incubator/jspwiki/trunk/src/WebContent/templates/default/editors/plain.jsp (original)
+++ incubator/jspwiki/trunk/src/WebContent/templates/default/editors/plain.jsp Wed Jan  6
16:46:00 2010
@@ -28,10 +28,6 @@
 --%>
 <div style="width:100%"> <%-- Required for IE6 on Windows --%>
 
-  <%-- Print any messages or validation errors --%>
-  <s:messages />
-  <s:errors />
-
   <s:form beanclass="org.apache.wiki.action.EditActionBean"
               class="wikiform"
                  id="editform"
@@ -39,57 +35,6 @@
       acceptcharset="UTF-8"
             enctype="application/x-www-form-urlencoded">
 
-    <%-- If any conflicts, print the conflicting text here --%>
-    <c:if test="${not empty wikiActionBean.conflictText}">
-      <p>
-        <s:label for="conflictText" />
-        <s:textarea name="conflictText" readonly="true" />
-      </p>
-      <p>
-        <s:checkbox name="overrideConflict" />
-        <fmt:message key="overrideConflict" />
-      </p>
-    </c:if>
-
-    <%-- EditActionBean relies on these being found.  So be careful, if you make changes.
--%>
-    <p id="submitbuttons">
-      <s:hidden name="page" />
-      <s:hidden name="startTime" />
-      <s:hidden name="append" />
-      <c:set var="saveTitle" scope="page"><fmt:message key="editor.plain.save.title"
/></c:set>
-      <s:submit name="save" accesskey="s" title="${saveTitle}" />
-
-      <c:set var="previewTitle" scope="page"><fmt:message key="editor.plain.preview.title"
/></c:set>
-      <s:submit name="preview" accesskey="v" title="${previewTitle}" />
-
-      <c:set var="cancelTitle" scope="page"><fmt:message key="editor.plain.cancel.title"
/></c:set>
-      <s:submit name="cancel" accesskey="q" title="${cancelTitle}" />
-    </p>
-
-    <%-- Fields for changenote, renaming etc. --%>
-    <table>
-      <tr>
-        <td>
-          <s:label for="changenote" />
-        </td>
-        <td>
-          <s:text name="changenote" size="50" maxlength="80" />
-        </td>
-      </tr>
-      <wiki:UserCheck status="anonymous">
-        <tr>
-          <td><s:label for="author" accesskey="n" /></td>
-          <td><s:text name="author" /></td>
-        </tr>
-        <wiki:CheckRequestContext context="comment">
-          <tr>
-            <td><s:label for="email" accesskey="m" /></td>
-            <td><s:text name="email" size="24" /></td>
-          </tr>
-        </wiki:CheckRequestContext>
-      </wiki:UserCheck>
-    </table>
-
     <%-- Toolbar --%>
     <div id="toolbar" class="line">
 
@@ -170,6 +115,22 @@
 
     </div><%-- end of the toolbar --%>
 
+    <%-- Print any messages or validation errors --%>
+    <s:messages />
+    <s:errors />
+
+    <%-- If any conflicts, print the conflicting text here --%>
+    <c:if test="${not empty wikiActionBean.conflictText}">
+      <p>
+        <s:label for="conflictText" />
+        <s:textarea name="conflictText" readonly="true" />
+      </p>
+      <p>
+        <s:checkbox name="overrideConflict" />
+        <fmt:message key="overrideConflict" />
+      </p>
+    </c:if>
+
     <%-- You knew this would be here somewhere. Yes, it's the textarea where the user
          actually edits stuff. --%>
     <div id="editor-content" class="line" style="clear:both;">
@@ -189,8 +150,46 @@
 
     </div>
 
-    <%-- Spam detection fields --%>
+    <%-- Fields for changenote, renaming etc. --%>
+    <table>
+      <tr>
+        <td>
+          <s:label for="changenote" />
+        </td>
+        <td>
+          <s:text name="changenote" size="50" maxlength="80" />
+        </td>
+      </tr>
+      <wiki:UserCheck status="anonymous">
+        <tr>
+          <td><s:label for="author" accesskey="n" /></td>
+          <td><s:text name="author" /></td>
+        </tr>
+        <wiki:CheckRequestContext context="comment">
+          <tr>
+            <td><s:label for="email" accesskey="m" /></td>
+            <td><s:text name="email" size="24" /></td>
+          </tr>
+        </wiki:CheckRequestContext>
+      </wiki:UserCheck>
+    </table>
+
+    <%-- Spam detection fields and optional CAPTCHA --%>
     <wiki:SpamProtect />
+
+    <%-- EditActionBean relies on these being found.  So be careful, if you make changes.
--%>
+    <p id="submitbuttons">
+      <s:hidden name="page" />
+      <s:hidden name="startTime" />
+      <s:hidden name="append" />
+      <c:set var="saveTitle" scope="page"><fmt:message key="editor.plain.save.title"
/></c:set>
+      <s:submit name="save" accesskey="s" title="${saveTitle}" />
+      <c:set var="previewTitle" scope="page"><fmt:message key="editor.plain.preview.title"
/></c:set>
+      <s:submit name="preview" accesskey="v" title="${previewTitle}" />
+      <c:set var="cancelTitle" scope="page"><fmt:message key="editor.plain.cancel.title"
/></c:set>
+      <s:submit name="cancel" accesskey="q" title="${cancelTitle}" />
+    </p>
+
   </s:form>
 
 </div>
\ No newline at end of file

Modified: incubator/jspwiki/trunk/src/java/org/apache/wiki/tags/SpamProtectTag.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/java/org/apache/wiki/tags/SpamProtectTag.java?rev=896520&r1=896519&r2=896520&view=diff
==============================================================================
--- incubator/jspwiki/trunk/src/java/org/apache/wiki/tags/SpamProtectTag.java (original)
+++ incubator/jspwiki/trunk/src/java/org/apache/wiki/tags/SpamProtectTag.java Wed Jan  6 16:46:00
2010
@@ -21,9 +21,7 @@
 package org.apache.wiki.tags;
 
 import java.io.IOException;
-import java.lang.reflect.Method;
 import java.security.SecureRandom;
-import java.util.Map;
 import java.util.Random;
 
 import javax.servlet.http.HttpServletRequest;
@@ -31,17 +29,19 @@
 import javax.servlet.jsp.JspWriter;
 import javax.servlet.jsp.tagext.TagSupport;
 
-import net.sourceforge.stripes.controller.ActionResolver;
-import net.sourceforge.stripes.controller.StripesFilter;
-import net.sourceforge.stripes.tag.FormTag;
 import net.sourceforge.stripes.util.CryptoUtil;
+import net.sourceforge.stripes.validation.LocalizableError;
+import net.sourceforge.stripes.validation.ValidationError;
+import net.sourceforge.stripes.validation.ValidationErrors;
 
 import org.apache.wiki.WikiEngine;
-import org.apache.wiki.action.WikiActionBean;
-import org.apache.wiki.content.inspect.*;
+import org.apache.wiki.api.WikiException;
+import org.apache.wiki.content.inspect.BotTrapInspector;
+import org.apache.wiki.content.inspect.Challenge;
+import org.apache.wiki.content.inspect.SpamInspectionFactory;
 import org.apache.wiki.filters.SpamFilter;
-import org.apache.wiki.ui.stripes.HandlerInfo;
 import org.apache.wiki.ui.stripes.SpamInterceptor;
+import org.apache.wiki.ui.stripes.WikiActionBeanContext;
 
 /**
  * <p>
@@ -49,13 +49,25 @@
  * current form, which will be parsed and verified by {@link SpamInterceptor}
  * whenever the input is processed by an ActionBean event handler method
  * annotated with the {@link org.apache.wiki.ui.stripes.SpamProtect} annotation.
- * If a CAPTCHA test is required, the required content will be written to the
+ * If a Challenge test is required, the required content will be written to the
  * page also, based on the results of
- * {@link Captcha#formContent(org.apache.wiki.content.inspect.Inspection)}.
+ * {@link Challenge#formContent(org.apache.wiki.content.inspect.Inspection)}.
+ * </p>
+ * <p>
+ * This tag has one optional; attribute, {@code challenge}. If supplied, a
+ * {@link Challenge} will be rendered in the format specified. The value {@code
+ * captcha} indicates that a CAPTCHA will be rendered using the CAPTCHA object
+ * configured for the WikiEngine. The value {@code password} indicates that the
+ * user must supply their password. The password option is only available if the
+ * user is already logged in, and JSPWiki is using built-in authentication. If
+ * container authentication is used or if the user is not logged in, the {@code
+ * password} will be ignored. If {@code challenge} is not supplied, a CAPTCHA
+ * will be generated on-demand if {@link SpamInterceptor} determined that the
+ * ActionBean contains spam.
  * </p>
  * <p>
  * This tag must be added as a child of an existing &lt;form&gt; or
- * &lt;stripes:form&gt; element. If The SpamProtect tag will cause the following
+ * &lt;stripes:form&gt; element. The SpamProtect tag will cause the following
  * parameters into the form:
  * </p>
  * <ol>
@@ -66,67 +78,60 @@
  * prevents the "hey, my edit destroyed all UTF-8 characters" problem.</li>
  * <li><b>A token field </b>, which has a random name and fixed value</b>
This
  * means that a bot will need to actually GET the form first and parse it out
- * before it can send syntactically correct POSTs. This is a LOT more effort
+ * before it can send syntactically correct POSTs. This requires more effort
  * than just simply looking at the fields once and crafting your auto-poster to
  * conform.</li>
- * <li><b>An empty, input field hidden with CSS</b>. This parameter is
meant to
+ * <li><b>An empty input field hidden with CSS</b>. This parameter is meant
to
  * catch bots which do a GET and then randomly fill all fields with garbage.
  * This field <em>must</em>be empty when SpamFilter examines the contents of
the
- * POST. Since it's hidden with the use of CSS, the bot would need to understand
- * CSS to bypass this check. Because the parameter is also randomized, it
- * prevents bot authors from hard-coding the fact that it needs to be empty.</li>
- * </li>
+ * POST. Since it is hidden with the use of CSS, the bot would need to
+ * understand CSS to bypass this check. Because the parameter is also
+ * randomized, it prevents bot authors from hard-coding the fact that it needs
+ * to be empty.</li> </li>
  * <li><b>An an encrypted parameter</b> called
  * {@link BotTrapInspector#REQ_SPAM_PARAM}, whose contents are the names of
  * parameters 2 and 3, separated by a carriage return character. These contents
- * are then encrypted.</li>
- * <li><b>Any parameters needed by the configured {@link Captcha}</b>,
if a
- * CAPTCHA is needed. The current CAPTCHA inspector's method
- * {@link Captcha#formContent(org.apache.wiki.WikiContext)} will be called to
+ * are encrypted with a key stored server-side so that they cannot be tampered
+ * with.</li>
+ * <li><b>Any parameters needed by the configured {@link Challenge}</b>,
if a
+ * Challenge is needed. The appropriate Challenge inspector's method
+ * {@link Challenge#formContent(org.apache.wiki.WikiContext)} will be called to
  * generate the relevant form parameters or any other markup needed.</li>
  * </ol>
- * <p>
- * The idea between 2 & 3 is that SpamInterceptor expects two fields with random
- * names, one of which needs to be empty, and one of which needs to be filled
- * with pre-determined data. This is quite hard for most bots to catch, unless
- * they are specifically crafted for JSPWiki and contain some amount of logic to
- * figure out the scheme.
- * </p>
  */
 public class SpamProtectTag extends WikiTagBase
 {
-    private static String getUniqueID()
-    {
-        StringBuilder sb = new StringBuilder();
-        Random rand = new SecureRandom();
+    private Challenge.Request m_challenge = Challenge.Request.CAPTCHA_ON_DEMAND;
 
-        for( int i = 0; i < 6; i++ )
-        {
-            char x = (char) ('A' + rand.nextInt( 26 ));
+    private static final String CHALLENGE_PASSWORD = "password";
 
-            sb.append( x );
-        }
+    private static final String CHALLENGE_CAPTCHA = "captcha";
 
-        return sb.toString();
-    }
+    private static final Random RANDOM = new SecureRandom();
 
     /**
-     * Writes the CAPTCHA form content and spam parameters to the curret
-     * PageContext's JSPWriter. If a CAPTCHA is not needed, it will not
-     * be written.
+     * Writes the Challenge form content and spam parameters to the current
+     * PageContext's JSPWriter. If a Challenge is not needed, it will not be
+     * written.
+     * 
+     * @throws IOException if content cannot be written to the JSPWriter
      */
     @Override
     public int doEndTag() throws JspException
     {
         try
         {
-            writeCaptchaFormContent();
+            writeChallengeFormContent();
             writeSpamParams();
         }
         catch( IOException e )
         {
             throw new JspException( e );
         }
+        catch( WikiException e )
+        {
+            throw new JspException( e );
+        }
         return super.doEndTag();
     }
 
@@ -137,30 +142,119 @@
     }
 
     /**
-     * Writes CAPTCHA-related form content to the current PageContext's
-     * JSPWriter, if a CAPTCHA is needed.
+     * Sets the {@code challenge} attribute for this tag. Valid values are
+     * {@code password} for {@link Challenge.Request#PASSWORD} or {@code
+     * captcha} for {@link Challenge.Request#CAPTCHA}. If not supplied, the
+     * challenge will default to {@link Challenge.Request#CAPTCHA_ON_DEMAND}.
      * 
-     * @throws IOException
+     * @param challenge the type of challenge the user should see
+     */
+    public void setChallenge( String challenge )
+    {
+        if( CHALLENGE_CAPTCHA.equals( challenge.toLowerCase() ) )
+        {
+            m_challenge = Challenge.Request.CAPTCHA;
+        }
+        else if( CHALLENGE_PASSWORD.equals( challenge.toLowerCase() ) )
+        {
+            m_challenge = Challenge.Request.PASSWORD;
+        }
+        else
+        {
+            m_challenge = Challenge.Request.CAPTCHA_ON_DEMAND;
+        }
+    }
+
+    /**
+     * Generates a new 6-character unique identifier.
+     * 
+     * @return the unique ID
+     */
+    private String getUniqueID()
+    {
+        StringBuilder sb = new StringBuilder();
+        for( int i = 0; i < 6; i++ )
+        {
+            char x = (char) ('A' + RANDOM.nextInt( 26 ));
+            sb.append( x );
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Determines whether spam has been identified earlier in the request by
+     * {@link SpamInterceptor}, based on the presence or absence of a global
+     * {@link ValidationError} with key name
+     * {@link SpamInterceptor#SPAM_VALIDATION_ERROR}.
+     * @param actionBeanContext the ActionBeanContext
+     * @return {@code true} if the ValidationError indicates that the
+     *         ActionBean's contents are spam, or {@code false} is the contents
+     *         are ok.
      */
-    private void writeCaptchaFormContent() throws IOException
+    static public boolean isSpamDetected( WikiActionBeanContext actionBeanContext )
     {
-        boolean needsCaptcha = false;
-        Captcha captcha = findCaptcha();
-        String captchaContent = null;
-        if( captcha != null )
+        ValidationErrors errors = actionBeanContext.getValidationErrors();
+        if ( errors.containsKey( ValidationErrors.GLOBAL_ERROR ) )
         {
-            captchaContent = captcha.formContent( m_wikiContext );
-            if( captchaContent != null )
+            for( ValidationError error : errors.get( ValidationErrors.GLOBAL_ERROR ) )
             {
-                needsCaptcha = true;
+                if( error instanceof LocalizableError )
+                {
+                    LocalizableError localError = (LocalizableError) error;
+                    if( SpamInterceptor.SPAM_VALIDATION_ERROR.equals( localError.getMessageKey()
) )
+                    {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Writes Challenge-related form content to the current PageContext's
+     * JSPWriter, if a challenge is needed. The value of the {@code challenge}
+     * attribute will always be written out as an encrypted parameter so that it
+     * can be extracted by {@link SpamInterceptor} when the form is POSTed.
+     * 
+     * @throws IOException if content cannot be written to the JSPWriter
+     */
+    private void writeChallengeFormContent() throws IOException, WikiException
+    {
+        WikiEngine engine = m_wikiContext.getEngine();
+        String challengeContent = null;
+
+        switch( m_challenge )
+        {
+            case PASSWORD: {
+                // Not implemented yet
+                break;
+            }
+            case CAPTCHA: {
+                Challenge captcha = SpamInspectionFactory.getCaptcha( engine );
+                challengeContent = captcha.formContent( m_wikiActionBean.getContext() );
+                break;
+            }
+            case CAPTCHA_ON_DEMAND: {
+                if( isSpamDetected( (WikiActionBeanContext)m_wikiContext ) )
+                {
+                    m_challenge = Challenge.Request.CAPTCHA;
+                    Challenge captcha = SpamInspectionFactory.getCaptcha( engine );
+                    challengeContent = captcha.formContent( m_wikiActionBean.getContext()
);
+                }
+                break;
             }
         }
+
+        // Always output the Challenge request parameter
         JspWriter out = getPageContext().getOut();
-        out.write( "<input name=\"" + CaptchaInspector.CAPTCHA_NEEDED_PARAM + "\" type=\"hidden\"
value=\""
-                   + CryptoUtil.encrypt( String.valueOf( Boolean.valueOf( needsCaptcha )
) ) + "\" />\n" );
-        if( needsCaptcha )
+        out.write( "<input name=\"" + SpamInterceptor.CHALLENGE_REQUEST_PARAM + "\" type=\"hidden\"
value=\""
+                   + CryptoUtil.encrypt( String.valueOf( m_challenge.name() ) ) + "\" />\n"
);
+
+        // Output any generated Challenge content
+        if( challengeContent != null )
         {
-            out.write( captchaContent );
+            out.write( challengeContent );
         }
     }
 
@@ -168,7 +262,7 @@
      * Writes hidden spam-protection parameters to the current PageContext's
      * JSPWriter.
      * 
-     * @throws IOException
+     * @throws IOException if content cannot be written to the JSPWriter
      */
     private void writeSpamParams() throws IOException
     {
@@ -194,53 +288,4 @@
         String encryptedParam = CryptoUtil.encrypt( tokenParam );
         out.write( "<input name=\"" + BotTrapInspector.REQ_SPAM_PARAM + "\" type=\"hidden\"
value=\"" + encryptedParam + "\" />\n" );
     }
-
-    /**
-     * Determines whether to use a CAPTCHA, based on WikiActionBean being used.
-     * The WikiActionBean is determined by looking for a parent Stripes
-     * {@link net.sourceforge.stripes.tag.FormTag}. If one is found, that
-     * ActionBean is used (which makes sense, because that is where the form
-     * will be posted to). If not, we use the current ActionBean returned by
-     * {@code m_wikiActionBean} (usually a pretty good guess). If any event
-     * handler methods for the ActionBean requires a CAPTCHA, the initialized
-     * {@link Captcha} implementation will be returned.
-     * 
-     * @return the Captcha if one is required, or {@code null} is it is not
-     *         needed for this ActionBean.
-     */
-    @SuppressWarnings( "unchecked" )
-    protected Captcha findCaptcha()
-    {
-        Class<? extends WikiActionBean> actionBeanClass = null;
-
-        // Try figuring out the ActionBean the enclosing FormTag will submit to
-        FormTag tag = this.getParentTag( FormTag.class );
-        if( tag != null )
-        {
-            // Figure out the ActionBean class
-            String action = tag.getAction();
-            ActionResolver resolver = StripesFilter.getConfiguration().getActionResolver();
-            actionBeanClass = (Class<? extends WikiActionBean>) resolver.getActionBeanType(
action );
-        }
-
-        // If that doesn't work, use the current ActionBean and handler event
-        if( actionBeanClass == null )
-        {
-            actionBeanClass = m_wikiActionBean.getClass();
-        }
-
-        // Now that we know what ActionBean we're looking at, do we need a
-        // Captcha for it?
-        Map<Method, HandlerInfo> events = HandlerInfo.getHandlerInfoCollection( actionBeanClass
);
-        for( Map.Entry<Method, HandlerInfo> entry : events.entrySet() )
-        {
-            if( entry.getValue().getCaptchaPolicy() == Captcha.Policy.ALWAYS )
-            {
-                WikiEngine engine = m_wikiActionBean.getContext().getEngine();
-                InspectionPlan plan = SpamInspectionFactory.getInspectionPlan( engine, engine.getWikiProperties()
);
-                return plan.getCaptcha();
-            }
-        }
-        return null;
-    }
 }

Modified: incubator/jspwiki/trunk/tests/java/org/apache/wiki/TestEngine.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/tests/java/org/apache/wiki/TestEngine.java?rev=896520&r1=896519&r2=896520&view=diff
==============================================================================
--- incubator/jspwiki/trunk/tests/java/org/apache/wiki/TestEngine.java (original)
+++ incubator/jspwiki/trunk/tests/java/org/apache/wiki/TestEngine.java Wed Jan  6 16:46:00
2010
@@ -51,12 +51,14 @@
 import org.apache.wiki.content.PageNotFoundException;
 import org.apache.wiki.content.WikiPath;
 import org.apache.wiki.content.inspect.BotTrapInspector;
+import org.apache.wiki.content.inspect.Challenge;
 import org.apache.wiki.log.Logger;
 import org.apache.wiki.log.LoggerFactory;
 import org.apache.wiki.providers.AbstractFileProvider;
 import org.apache.wiki.providers.ProviderException;
 import org.apache.wiki.tags.SpamProtectTag;
 import org.apache.wiki.ui.WikiServletFilter;
+import org.apache.wiki.ui.stripes.SpamInterceptor;
 import org.apache.wiki.util.FileUtil;
 import org.apache.wiki.util.TextUtil;
 
@@ -184,6 +186,8 @@
         trip.addParameter( BotTrapInspector.REQ_TRAP_PARAM, new String[0] );
         trip.addParameter( "TOKENA", trip.getRequest().getSession().getId() );
         trip.addParameter( BotTrapInspector.REQ_SPAM_PARAM, paramValue );
+        paramValue = CryptoUtil.encrypt( Challenge.Request.CAPTCHA_ON_DEMAND.name() );
+        trip.addParameter( SpamInterceptor.CHALLENGE_REQUEST_PARAM, paramValue );
 
         // Add the UTF-8 token
         trip.addParameter( BotTrapInspector.REQ_ENCODING_CHECK, "\u3041" );

Modified: incubator/jspwiki/trunk/tests/java/org/apache/wiki/action/TestActionBean.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/tests/java/org/apache/wiki/action/TestActionBean.java?rev=896520&r1=896519&r2=896520&view=diff
==============================================================================
--- incubator/jspwiki/trunk/tests/java/org/apache/wiki/action/TestActionBean.java (original)
+++ incubator/jspwiki/trunk/tests/java/org/apache/wiki/action/TestActionBean.java Wed Jan
 6 16:46:00 2010
@@ -21,10 +21,10 @@
 
 package org.apache.wiki.action;
 
+import net.sourceforge.stripes.action.HandlesEvent;
 import net.sourceforge.stripes.action.Resolution;
 import net.sourceforge.stripes.validation.Validate;
 
-import org.apache.wiki.action.AbstractPageActionBean;
 import org.apache.wiki.api.WikiPage;
 import org.apache.wiki.ui.stripes.SpamProtect;
 
@@ -34,6 +34,7 @@
 public class TestActionBean extends AbstractPageActionBean
 {
     @SpamProtect( content = "text" )
+    @HandlesEvent( "test" )
     public Resolution test()
     {
         return null;



Mime
View raw message