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 2009/04/20 00:29:12 UTC

svn commit: r766538 - in /incubator/jspwiki/trunk: src/WebContent/templates/default/ src/WebContent/templates/default/editors/ src/java/org/apache/wiki/action/ tests/java/org/apache/wiki/action/

Author: ajaquith
Date: Sun Apr 19 22:29:12 2009
New Revision: 766538

URL: http://svn.apache.org/viewvc?rev=766538&view=rev
Log:
Massive refactoring to EditActionBean, and related spam-protection classes. Bug-fix to ContentManager's page-save task. Editing actually works again! More fixes coming later.

Added:
    incubator/jspwiki/trunk/tests/java/org/apache/wiki/action/EditActionBeanTest.java
Modified:
    incubator/jspwiki/trunk/src/WebContent/templates/default/EditContent.jsp
    incubator/jspwiki/trunk/src/WebContent/templates/default/PreviewContent.jsp
    incubator/jspwiki/trunk/src/WebContent/templates/default/editors/plain.jsp
    incubator/jspwiki/trunk/src/java/org/apache/wiki/action/EditActionBean.java
    incubator/jspwiki/trunk/tests/java/org/apache/wiki/action/AllTests.java

Modified: incubator/jspwiki/trunk/src/WebContent/templates/default/EditContent.jsp
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/WebContent/templates/default/EditContent.jsp?rev=766538&r1=766537&r2=766538&view=diff
==============================================================================
--- incubator/jspwiki/trunk/src/WebContent/templates/default/EditContent.jsp (original)
+++ incubator/jspwiki/trunk/src/WebContent/templates/default/EditContent.jsp Sun Apr 19 22:29:12 2009
@@ -15,8 +15,6 @@
 <wiki:TabbedSection defaultTab="editcontent">
 
   <wiki:Tab id="editcontent" titleKey="edit.tab.edit" accesskey="e">
-    <s:errors />
-    <s:messages />
     <wiki:Editor/>
   </wiki:Tab>
   

Modified: incubator/jspwiki/trunk/src/WebContent/templates/default/PreviewContent.jsp
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/WebContent/templates/default/PreviewContent.jsp?rev=766538&r1=766537&r2=766538&view=diff
==============================================================================
--- incubator/jspwiki/trunk/src/WebContent/templates/default/PreviewContent.jsp (original)
+++ incubator/jspwiki/trunk/src/WebContent/templates/default/PreviewContent.jsp Sun Apr 19 22:29:12 2009
@@ -25,8 +25,10 @@
         <s:hidden name="link" />
         <s:hidden name="page" value="${wikiActionBean.page.name}" />
         <s:hidden name="remember" />
+        <s:hidden name="startTime" />
         <s:hidden name="text" />
-        <s:hidden name="<%=SpamFilter.getHashFieldName(request)%>"><c:out value="${lastchange}" /></s:hidden>
+        <%-- Spam detection fields --%>
+        <wiki:SpamProtect />
       </p>
       <div id="submitbuttons">
         <c:set var="editTitle"><fmt:message key="editor.preview.edit.title"/></c:set>

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=766538&r1=766537&r2=766538&view=diff
==============================================================================
--- incubator/jspwiki/trunk/src/WebContent/templates/default/editors/plain.jsp (original)
+++ incubator/jspwiki/trunk/src/WebContent/templates/default/editors/plain.jsp Sun Apr 19 22:29:12 2009
@@ -22,9 +22,10 @@
       </p>
     </c:if>
   
-    <%-- Edit.jsp relies on these being found.  So be careful, if you make changes. --%>
+    <%-- EditActionBean relies on these being found.  So be careful, if you make changes. --%>
     <p id="submitbuttons">
       <s:hidden name="page"><wiki:Variable var='pagename' /></s:hidden>
+      <s:hidden name="startTime" />
       <c:set var="saveTitle" scope="page"><fmt:message key="editor.plain.save.title" /></c:set>
       <wiki:CheckRequestContext context='edit'>
         <s:submit name="save" accesskey="s" title="${saveTitle}" />

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=766538&r1=766537&r2=766538&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 Sun Apr 19 22:29:12 2009
@@ -22,8 +22,8 @@
 package org.apache.wiki.action;
 
 import java.io.IOException;
+import java.net.URI;
 import java.security.Principal;
-import java.util.Date;
 import java.util.List;
 
 import javax.servlet.http.HttpServletRequest;
@@ -34,7 +34,9 @@
 import net.sourceforge.stripes.validation.*;
 
 import org.apache.commons.lang.StringEscapeUtils;
-import org.apache.wiki.*;
+import org.apache.wiki.WikiContext;
+import org.apache.wiki.WikiEngine;
+import org.apache.wiki.WikiSession;
 import org.apache.wiki.api.WikiException;
 import org.apache.wiki.api.WikiPage;
 import org.apache.wiki.auth.permissions.PagePermission;
@@ -42,12 +44,12 @@
 import org.apache.wiki.content.PageNotFoundException;
 import org.apache.wiki.content.lock.PageLock;
 import org.apache.wiki.filters.RedirectException;
-import org.apache.wiki.filters.SpamProtect;
 import org.apache.wiki.htmltowiki.HtmlStringToWikiTranslator;
 import org.apache.wiki.log.Logger;
 import org.apache.wiki.log.LoggerFactory;
 import org.apache.wiki.providers.ProviderException;
 import org.apache.wiki.ui.stripes.HandlerPermission;
+import org.apache.wiki.ui.stripes.SpamProtect;
 import org.apache.wiki.ui.stripes.WikiActionBeanContext;
 import org.apache.wiki.ui.stripes.WikiRequestContext;
 import org.apache.wiki.util.TextUtil;
@@ -84,7 +86,7 @@
 
     private String m_link = null;
 
-    private Date m_startTime = null;
+    private long m_startTime = -1;
 
     /**
      * Event handler method that cancels any locks the user possesses for the
@@ -95,11 +97,12 @@
      */
     @DontValidate
     @HandlesEvent( "cancel" )
-    @HandlerPermission( permissionClass = PagePermission.class, target = "${page.qualifiedName}", actions = PagePermission.EDIT_ACTION )
+    @HandlerPermission( permissionClass = PagePermission.class, target = "${page.path}", actions = PagePermission.EDIT_ACTION )
     @WikiRequestContext( "cancel" )
     public Resolution cancel()
     {
-        String pagereq = m_page.getName();
+        WikiPage page = getPage();
+        String pagereq = page.getName();
         log.debug( "Cancelled editing " + pagereq );
 
         // Cancel page lock
@@ -115,12 +118,12 @@
     }
 
     @HandlesEvent( "comment" )
-    @HandlerPermission( permissionClass = PagePermission.class, target = "${page.qualifiedName}", actions = PagePermission.COMMENT_ACTION )
+    @HandlerPermission( permissionClass = PagePermission.class, target = "${page.path}", actions = PagePermission.COMMENT_ACTION )
     @WikiRequestContext( "comment" )
     public Resolution comment()
     {
-        // Set the editing start time
-        m_startTime = new Date();
+        // Set the editing start time (will be written to the JSPs as encrypted parameter)
+        setStartTime( System.currentTimeMillis() );
 
         return null;
     }
@@ -132,7 +135,7 @@
      * @return a forward resolution back to the preview page.
      */
     @HandlesEvent( "diff" )
-    @HandlerPermission( permissionClass = PagePermission.class, target = "${page.qualifiedName}", actions = PagePermission.VIEW_ACTION )
+    @HandlerPermission( permissionClass = PagePermission.class, target = "${page.path}", actions = PagePermission.VIEW_ACTION )
     @WikiRequestContext( "diff" )
     public Resolution diff()
     {
@@ -142,7 +145,7 @@
     @DefaultHandler
     @DontValidate
     @HandlesEvent( "edit" )
-    @HandlerPermission( permissionClass = PagePermission.class, target = "${page.qualifiedName}", actions = PagePermission.EDIT_ACTION )
+    @HandlerPermission( permissionClass = PagePermission.class, target = "${page.path}", actions = PagePermission.EDIT_ACTION )
     @WikiRequestContext( "edit" )
     public Resolution edit() throws ProviderException
     {
@@ -150,15 +153,19 @@
         HttpServletRequest request = wikiContext.getRequest();
         HttpSession session = request.getSession();
         Principal user = wikiContext.getCurrentUser();
-        String pageName = m_page.getName();
+        WikiPage page = getPage();
+        String pageName = page.getName();
 
         log.info( "Editing page " + pageName + ". User=" + user.getName() + ", host=" + request.getRemoteAddr() );
 
+        // Set the editing start time (will be written to the JSPs as encrypted parameter)
+        setStartTime( System.currentTimeMillis() );
+        
         // If page is locked, make sure we tell the user
         List<Message> messages = wikiContext.getMessages();
         WikiEngine engine = wikiContext.getEngine();
         ContentManager mgr = engine.getContentManager();
-        PageLock lock = mgr.getCurrentLock( m_page );
+        PageLock lock = mgr.getCurrentLock( page );
         if( lock != null )
         {
             messages.add( new LocalizableMessage( "edit.locked", lock.getLocker(), lock.getTimeLeft() ) );
@@ -169,29 +176,26 @@
         WikiPage latest;
         try
         {
-            latest = engine.getPage( m_page.getName() );
+            latest = engine.getPage( page.getName() );
         }
         catch( PageNotFoundException e )
         {
-            latest = m_page;
+            latest = page;
         }
-        if( latest.getVersion() != m_page.getVersion() )
+        if( latest.getVersion() != page.getVersion() )
         {
-            errors.addGlobalError( new LocalizableError( "edit.restoring", m_page.getVersion() ) );
+            errors.addGlobalError( new LocalizableError( "edit.restoring", page.getVersion() ) );
         }
 
         // Attempt to lock the page.
-        lock = mgr.lockPage( m_page, user.getName() );
+        lock = mgr.lockPage( page, user.getName() );
         if( lock != null )
         {
             session.setAttribute( LOCK_PREFIX + pageName, lock );
         }
 
         // Load the page text
-        m_text = engine.getPureText( m_page );
-
-        // Set the editing start time
-        m_startTime = new Date();
+        m_text = engine.getPureText( page );
 
         return new ForwardResolution( "/Edit.jsp" );
     }
@@ -292,7 +296,7 @@
      * 
      * @return the start time
      */
-    public Date getStartTime()
+    public long getStartTime()
     {
         return m_startTime;
     }
@@ -322,9 +326,9 @@
     {
         // Set author: prefer authenticated/asserted principals first
         WikiSession wikiSession = getContext().getWikiSession();
-        if( m_author == null || !wikiSession.isAnonymous() )
+        if( getAuthor() == null || !wikiSession.isAnonymous() )
         {
-            m_author = wikiSession.getUserPrincipal().getName();
+            setAuthor( wikiSession.getUserPrincipal().getName() );
         }
     }
 
@@ -334,11 +338,11 @@
      * @return a forward resolution back to the preview page.
      */
     @HandlesEvent( "preview" )
-    @HandlerPermission( permissionClass = PagePermission.class, target = "${page.qualifiedName}", actions = PagePermission.VIEW_ACTION )
+    @HandlerPermission( permissionClass = PagePermission.class, target = "${page.path}", actions = PagePermission.VIEW_ACTION )
     @WikiRequestContext( "preview" )
     public Resolution preview()
     {
-        log.debug( "Previewing " + m_page.getName() );
+        log.debug( "Previewing " + getPage().getName() );
         return new ForwardResolution( "/Preview.jsp" );
     }
 
@@ -355,34 +359,45 @@
     @ValidationMethod( on = "save", when = ValidationState.NO_ERRORS )
     public void validateNoConflicts() throws ProviderException
     {
-        if( m_startTime.before( m_page.getLastModified() ) )
+        ValidationErrors errors = getContext().getValidationErrors();
+        WikiPage page = getPage();
+        boolean exists = getContext().getEngine().pageExists( page );
+        long lastModified = exists ? page.getLastModified().getTime() : -1;
+        if( exists && m_startTime < lastModified )
         {
             // Retrieve and escape the conflicting text
-            String conflictText = m_page.getContentAsString();
+            String conflictText = page.getContentAsString();
             conflictText = StringEscapeUtils.escapeXml( conflictText );
             conflictText = TextUtil.replaceString( conflictText, "\n", "<br />" );
             m_conflictText = conflictText;
 
             // Create a validation error
-            ValidationErrors errors = getContext().getValidationErrors();
             errors.add( "text", new LocalizableError( "edit.conflict" ) );
         }
+        
+        // Is the user trying to edit a special page? Tsk, tsk.
+        URI uri = getContext().getEngine().getSpecialPageReference( page.getName() );
+        if( uri != null )
+        {
+            errors.add( "page", new LocalizableError( "edit.specialpage" ) );
+        }
     }
 
     @HandlesEvent( "save" )
-    @HandlerPermission( permissionClass = PagePermission.class, target = "${page.qualifiedName}", actions = PagePermission.EDIT_ACTION )
+    @HandlerPermission( permissionClass = PagePermission.class, target = "${page.path}", actions = PagePermission.EDIT_ACTION )
     @WikiRequestContext( "save" )
-    @SpamProtect
+    @SpamProtect( content = "text" )
     public Resolution save() throws WikiException
     {
         WikiSession wikiSession = getContext().getWikiSession();
         HttpServletRequest request = getContext().getHttpRequest();
         HttpSession session = request.getSession();
         WikiContext wikiContext = getContext();
+        WikiPage page = getPage();
         WikiEngine engine = getContext().getEngine();
-        String pagereq = m_page.getName();
+        String pagereq = page.getName();
 
-        log.info( "Saving page " + m_page.getName() + ". UserPrincipal=" + wikiSession.getUserPrincipal().getName() + ", Author="
+        log.info( "Saving page " + page.getName() + ". UserPrincipal=" + wikiSession.getUserPrincipal().getName() + ", Author="
                   + m_author + ", Host=" + getContext().getRequest().getRemoteAddr() );
 
         // Set author information and other metadata
@@ -411,11 +426,11 @@
             {
                 engine.saveText( wikiContext, m_text );
             }
-            session.removeAttribute( LOCK_PREFIX +m_page.getName() );
+            session.removeAttribute( LOCK_PREFIX +page.getName() );
         }
         catch( DecisionRequiredException ex )
         {
-            session.removeAttribute( LOCK_PREFIX +m_page.getName() );
+            session.removeAttribute( LOCK_PREFIX +page.getName() );
             return new RedirectResolution( ViewActionBean.class, "view" ).addParameter( "page", "ApprovalRequiredForPageChanges" );
         }
         catch( RedirectException ex )
@@ -546,14 +561,14 @@
      * tampered with by the user. When the <code>save</code> event is
      * executed, it will be decrypted and used to detect edit conflicts. This
      * value is initialized to the current time when the
-     * {@link #initDefaultValues()} method fires.
+     * {@link #edit()} or {@link #comment()} methods fire.
      * 
      * @param date the start time
      */
     @Validate( required = true, encrypted = true )
-    public void setStartTime( Date date )
+    public void setStartTime( long time )
     {
-        m_startTime = date;
+        m_startTime = time;
     }
 
     /**

Modified: incubator/jspwiki/trunk/tests/java/org/apache/wiki/action/AllTests.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/tests/java/org/apache/wiki/action/AllTests.java?rev=766538&r1=766537&r2=766538&view=diff
==============================================================================
--- incubator/jspwiki/trunk/tests/java/org/apache/wiki/action/AllTests.java (original)
+++ incubator/jspwiki/trunk/tests/java/org/apache/wiki/action/AllTests.java Sun Apr 19 22:29:12 2009
@@ -37,6 +37,7 @@
         TestSuite suite = new TestSuite("ActionBean tests");
 
         suite.addTest( DeleteActionBeanTest.suite() );
+        suite.addTest( EditActionBeanTest.suite() );
         suite.addTest( GroupActionBeanTest.suite() );
         suite.addTest( LoginActionBeanTest.suite() );
         suite.addTest( RenameActionBeanTest.suite() );

Added: incubator/jspwiki/trunk/tests/java/org/apache/wiki/action/EditActionBeanTest.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/tests/java/org/apache/wiki/action/EditActionBeanTest.java?rev=766538&view=auto
==============================================================================
--- incubator/jspwiki/trunk/tests/java/org/apache/wiki/action/EditActionBeanTest.java (added)
+++ incubator/jspwiki/trunk/tests/java/org/apache/wiki/action/EditActionBeanTest.java Sun Apr 19 22:29:12 2009
@@ -0,0 +1,177 @@
+/*
+    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
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.    
+ */
+package org.apache.wiki.action;
+
+import java.util.Properties;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import net.sourceforge.stripes.mock.MockRoundtrip;
+import net.sourceforge.stripes.util.CryptoUtil;
+import net.sourceforge.stripes.validation.ValidationErrors;
+
+import org.apache.wiki.TestEngine;
+import org.apache.wiki.api.WikiPage;
+
+public class EditActionBeanTest extends TestCase
+{
+    TestEngine m_engine;
+
+    public void setUp()
+    {
+        // Start the WikiEngine, and stash reference
+        Properties props = new Properties();
+        try
+        {
+            props.load( TestEngine.findTestProperties() );
+            m_engine = new TestEngine( props );
+        }
+        catch( Exception e )
+        {
+            throw new RuntimeException( "Could not set up TestEngine: " + e.getMessage() );
+        }
+    }
+
+    public void tearDown()
+    {
+        m_engine.shutdown();
+    }
+
+    public void testEditNoParameter() throws Exception
+    {
+        // Try editing without specifying a page
+        MockRoundtrip trip = m_engine.guestTrip( "/Edit.action" );
+        String startTime = String.valueOf( System.currentTimeMillis() );
+        trip.addParameter( "startTime", CryptoUtil.encrypt( startTime ) );
+        trip.addParameter( "text", "This is the edited text" );
+        TestEngine.addSpamProtectParams( trip );
+        trip.execute( "save" );
+
+        // ...we should NOT see any page bound to the ActionBean
+        EditActionBean bean = trip.getActionBean( EditActionBean.class );
+        assertNull( bean.getPage() );
+
+        // ...and the "page" param should be flagged as invalid
+        ValidationErrors errors = trip.getValidationErrors();
+        assertEquals( 1, errors.size() );
+        assertTrue( errors.hasFieldErrors() );
+        assertTrue( errors.containsKey( "page" ) );
+
+        // ...and the destination should be the original display JSP (for
+        // displaying errors)
+        assertEquals( MockRoundtrip.DEFAULT_SOURCE_PAGE, trip.getDestination() );
+    }
+
+    public void testEditSpecialPage() throws Exception
+    {
+        // Make sure no special page FindPage actually exists
+        m_engine.deletePage( "FindPage" );
+
+        // Try editing the 'special page'
+        MockRoundtrip trip = m_engine.guestTrip( "/Edit.action" );
+        String startTime = String.valueOf( System.currentTimeMillis() );
+        trip.addParameter( "page", "FindPage" );
+        trip.addParameter( "startTime", CryptoUtil.encrypt( startTime ) );
+        trip.addParameter( "text", "This is the edited text" );
+        TestEngine.addSpamProtectParams( trip );
+        trip.execute( "save" );
+
+        // ...we should NOT see any page bound to the ActionBean
+        EditActionBean bean = trip.getActionBean( EditActionBean.class );
+        assertNull( bean.getPage() );
+
+        // ...and the "page" param should be flagged as invalid
+        ValidationErrors errors = trip.getValidationErrors();
+        assertEquals( 1, errors.size() );
+        assertTrue( errors.hasFieldErrors() );
+        assertTrue( errors.containsKey( "page" ) );
+
+        // ...and the destination should be the original display JSP (for
+        // displaying errors)
+        assertEquals( MockRoundtrip.DEFAULT_SOURCE_PAGE, trip.getDestination() );
+    }
+
+    public void testEditExistingPage() throws Exception
+    {
+
+        String pageName = "EditActionBeanTest" + System.currentTimeMillis();
+
+        m_engine.saveText( pageName, "This is a test." );
+        WikiPage page = m_engine.getPage( pageName );
+        assertNotNull( "Did not save page " + pageName + "!", page );
+
+        // Set up the marked-up page
+        MockRoundtrip trip = m_engine.guestTrip( "/Edit.action" );
+        trip.setParameter( "page", pageName );
+        String startTime = String.valueOf( System.currentTimeMillis() );
+        trip.addParameter( "startTime", CryptoUtil.encrypt( startTime ) );
+        trip.addParameter( "text", "This is the edited text" );
+        TestEngine.addSpamProtectParams( trip );
+        trip.execute( "save" );
+
+        // ...we should automatically see the test page bound to the ActionBean
+        EditActionBean bean = trip.getActionBean( EditActionBean.class );
+        ValidationErrors errors = trip.getValidationErrors();
+        assertEquals( 0, errors.size() );
+        assertEquals( page, bean.getPage() );
+
+        // ...and the destination should be Wiki.action (aka display JSP)
+        assertEquals( "/Wiki.action?view=&page=" + pageName, trip.getDestination() );
+
+        // Delete the test page
+        m_engine.deletePage( pageName );
+    }
+
+    public void testEditNewPage() throws Exception
+    {
+
+        String pageName = "EditActionBeanTest" + System.currentTimeMillis();
+        assertFalse( m_engine.pageExists( pageName ) );
+
+        // Set up the marked-up page
+        MockRoundtrip trip = m_engine.guestTrip( "/Edit.action" );
+        trip.setParameter( "page", pageName );
+        String startTime = String.valueOf( System.currentTimeMillis() );
+        trip.addParameter( "startTime", CryptoUtil.encrypt( startTime ) );
+        trip.addParameter( "text", "This is the edited text" );
+        TestEngine.addSpamProtectParams( trip );
+        trip.execute( "save" );
+
+        // ...we should automatically see the test page bound to the ActionBean
+        EditActionBean bean = trip.getActionBean( EditActionBean.class );
+        ValidationErrors errors = trip.getValidationErrors();
+        assertEquals( 0, errors.size() );
+        assertNotNull( bean.getPage() );
+        assertEquals( pageName, bean.getPage().getName() );
+
+        // ...and the destination should be Wiki.action (aka display JSP)
+        assertEquals( "/Wiki.action?view=&page=" + pageName, trip.getDestination() );
+
+        // Delete the test page
+        m_engine.deletePage( pageName );
+    }
+
+    public static Test suite()
+    {
+        return new TestSuite( EditActionBeanTest.class );
+    }
+}