Author: ajaquith
Date: Sat May 23 13:37:22 2009
New Revision: 777923
URL: http://svn.apache.org/viewvc?rev=777923&view=rev
Log:
Major refactoring of attachment handling. AttachmentServlet has been removed in
favor of AttachmentActionBean. The Stripes DynamicMappingFilter was added to
ensure that /attach URLs are mapped correctly. Upload URLs are slightly
different, but download URLs remain the same (and in fact are now path-aware,
so they support spaces and subpages). AttachmentServlet will be removed from
the code entirely once AttachmentActionBean is better road-tested.
Added:
incubator/jspwiki/trunk/src/java/org/apache/wiki/action/AttachmentActionBean.java
- copied, changed from r775662,
incubator/jspwiki/trunk/src/java/org/apache/wiki/action/UploadActionBean.java
Removed:
incubator/jspwiki/trunk/src/java/org/apache/wiki/action/AttachActionBean.java
incubator/jspwiki/trunk/src/java/org/apache/wiki/action/UploadActionBean.java
Modified:
incubator/jspwiki/trunk/src/WebContent/WEB-INF/web.xml
incubator/jspwiki/trunk/src/WebContent/templates/default/AttachmentTab.jsp
incubator/jspwiki/trunk/src/java/org/apache/wiki/WikiContext.java
incubator/jspwiki/trunk/src/java/org/apache/wiki/action/ViewActionBean.java
incubator/jspwiki/trunk/src/java/org/apache/wiki/url/StripesURLConstructor.java
Modified: incubator/jspwiki/trunk/src/WebContent/WEB-INF/web.xml
URL:
http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/WebContent/WEB-INF/web.xml?rev=777923&r1=777922&r2=777923&view=diff
==============================================================================
--- incubator/jspwiki/trunk/src/WebContent/WEB-INF/web.xml (original)
+++ incubator/jspwiki/trunk/src/WebContent/WEB-INF/web.xml Sat May 23 13:37:22
2009
@@ -91,6 +91,13 @@
</init-param>
</filter>
+ <filter>
+ <description>Dynamically maps URLs to ActionBeans.</description>
+ <display-name>Stripes Dynamic Mapping Filter</display-name>
+ <filter-name>DynamicMappingFilter</filter-name>
+
<filter-class>net.sourceforge.stripes.controller.DynamicMappingFilter</filter-class>
+ </filter>
+
<!--
This is new in 2.4. This defines a servlet filter which filters all
requests.
-->
@@ -133,10 +140,17 @@
<servlet-name>StripesDispatcher</servlet-name>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
+ <filter-mapping>
+ <filter-name>DynamicMappingFilter</filter-name>
+ <url-pattern>/*</url-pattern>
+ <dispatcher>REQUEST</dispatcher>
+ <dispatcher>FORWARD</dispatcher>
+ <dispatcher>INCLUDE</dispatcher>
+ </filter-mapping>
<!--
HttpSessionListener used for managing WikiSessions.
- -->
+ -->
<listener>
<listener-class>org.apache.wiki.auth.SessionMonitor</listener-class>
</listener>
@@ -209,15 +223,6 @@
-->
</servlet>
- <!--
- Attachment exchange handler.
- -->
-
- <servlet>
- <servlet-name>AttachmentServlet</servlet-name>
-
<servlet-class>org.apache.wiki.attachment.AttachmentServlet</servlet-class>
- </servlet>
-
<servlet-mapping>
<servlet-name>StripesDispatcher</servlet-name>
<url-pattern>/dispatcher</url-pattern>
@@ -263,11 +268,6 @@
AND REMOVE ME TOO -->
<servlet-mapping>
- <servlet-name>AttachmentServlet</servlet-name>
- <url-pattern>/attach/*</url-pattern>
- </servlet-mapping>
-
- <servlet-mapping>
<servlet-name>WikiServlet</servlet-name>
<url-pattern>/wiki/*</url-pattern>
</servlet-mapping>
Modified:
incubator/jspwiki/trunk/src/WebContent/templates/default/AttachmentTab.jsp
URL:
http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/WebContent/templates/default/AttachmentTab.jsp?rev=777923&r1=777922&r2=777923&view=diff
==============================================================================
--- incubator/jspwiki/trunk/src/WebContent/templates/default/AttachmentTab.jsp
(original)
+++ incubator/jspwiki/trunk/src/WebContent/templates/default/AttachmentTab.jsp
Sat May 23 13:37:22 2009
@@ -2,10 +2,6 @@
<%@ taglib uri="http://jakarta.apache.org/jspwiki.tld" prefix="wiki" %>
<%@ taglib uri="http://stripes.sourceforge.net/stripes.tld" prefix="s" %>
<%@ page import="org.apache.wiki.*" %>
-<%@ page import="org.apache.wiki.auth.*" %>
-<%@ page import="org.apache.wiki.ui.progress.*" %>
-<%@ page import="org.apache.wiki.auth.permissions.*" %>
-<%@ page import="java.security.Permission" %>
<%@ page import="javax.servlet.jsp.jstl.fmt.*" %>
<%@ page import="org.apache.wiki.action.WikiContextFactory" %>
<%@ page import="org.apache.wiki.util.TextUtil" %>
@@ -27,7 +23,7 @@
<div id="addattachment">
<h3><fmt:message key="attach.add" /></h3>
<wiki:Permission permission="upload">
- <s:form beanclass="org.apache.wiki.action.UploadActionBean"
class="wikiform" id="uploadform" acceptcharset="UTF-8">
+ <s:form beanclass="org.apache.wiki.action.AttachmentActionBean"
class="wikiform" id="uploadform" acceptcharset="UTF-8">
<s:param name="progressid" value="<%=progressId%>" />
<s:param name="page" value="${wikiActionBean.page.name}" />
<table>
@@ -35,7 +31,7 @@
<td colspan="2"><div class="formhelp"><fmt:message
key="attach.add.info" /></div></td>
</tr>
<tr>
- <td span="2"><s:errors field="newAttachments" /></td>
+ <td colspan="2"><s:errors field="newAttachments" /></td>
</tr>
<tr>
<td><s:label for="attachfile0" name="attach.add.selectfile" /></td>
@@ -78,8 +74,8 @@
<div class="slimbox-img sortable">
<table class="wikitable">
<tr>
- <th><fmt:message key="info.attachment.type" /></th>
<th><fmt:message key="info.attachment.name" /></th>
+ <th><fmt:message key="info.attachment.type" /></th>
<th><fmt:message key="info.size" /></th>
<th><fmt:message key="info.version" /></th>
<th><fmt:message key="info.date" /></th>
@@ -91,15 +87,12 @@
<wiki:AttachmentsIterator id="att">
<%
String name = att.getFileName();
- int dot = name.lastIndexOf(".");
- String attachtype = ( dot != -1 ) ? name.substring(dot+1) : " ";
-
String sname = name;
if( sname.length() > MAXATTACHNAMELENGTH ) sname =
sname.substring(0,MAXATTACHNAMELENGTH) + "...";
%>
<tr>
- <td><div class="attachtype"><%= attachtype %></div></td>
<td><wiki:LinkTo title="<%= name %>"><%= sname
%></wiki:LinkTo></td>
+ <td><div class="attachtype"><%= att.getContentType()
%></div></td>
<td style="white-space:nowrap;text-align:right;">
<fmt:formatNumber
value='<%=Double.toString(att.getSize()/1000.0)%>' maxFractionDigits='1'
minFractionDigits='1' /> <fmt:message key="info.kilobytes" />
</td>
Modified: incubator/jspwiki/trunk/src/java/org/apache/wiki/WikiContext.java
URL:
http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/java/org/apache/wiki/WikiContext.java?rev=777923&r1=777922&r2=777923&view=diff
==============================================================================
--- incubator/jspwiki/trunk/src/java/org/apache/wiki/WikiContext.java (original)
+++ incubator/jspwiki/trunk/src/java/org/apache/wiki/WikiContext.java Sat May
23 13:37:22 2009
@@ -97,7 +97,7 @@
public static final String ERROR = HandlerInfo.getHandlerInfo(
ErrorActionBean.class, "error" ).getRequestContext();
/** User is uploading something. */
- public static final String UPLOAD = HandlerInfo.getHandlerInfo(
UploadActionBean.class, "upload" ).getRequestContext();
+ public static final String UPLOAD = HandlerInfo.getHandlerInfo(
AttachmentActionBean.class, "upload" ).getRequestContext();
/** User is commenting something. */
public static final String COMMENT = HandlerInfo.getHandlerInfo(
EditActionBean.class, "comment" ).getRequestContext();
@@ -127,7 +127,7 @@
public static final String DELETE = HandlerInfo.getHandlerInfo(
DeleteActionBean.class, "delete" ).getRequestContext();
/** User is downloading an attachment. */
- public static final String ATTACH = HandlerInfo.getHandlerInfo(
AttachActionBean.class, "upload" ).getRequestContext();
+ public static final String ATTACH = HandlerInfo.getHandlerInfo(
AttachmentActionBean.class, "download" ).getRequestContext();
/** RSS feed is being generated. */
public static final String RSS = HandlerInfo.getHandlerInfo(
RSSActionBean.class, "rss" ).getRequestContext();
Copied:
incubator/jspwiki/trunk/src/java/org/apache/wiki/action/AttachmentActionBean.java
(from r775662,
incubator/jspwiki/trunk/src/java/org/apache/wiki/action/UploadActionBean.java)
URL:
http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/java/org/apache/wiki/action/AttachmentActionBean.java?p2=incubator/jspwiki/trunk/src/java/org/apache/wiki/action/AttachmentActionBean.java&p1=incubator/jspwiki/trunk/src/java/org/apache/wiki/action/UploadActionBean.java&r1=775662&r2=777923&rev=777923&view=diff
==============================================================================
---
incubator/jspwiki/trunk/src/java/org/apache/wiki/action/UploadActionBean.java
(original)
+++
incubator/jspwiki/trunk/src/java/org/apache/wiki/action/AttachmentActionBean.java
Sat May 23 13:37:22 2009
@@ -24,12 +24,13 @@
import java.io.IOException;
import java.io.InputStream;
import java.security.Principal;
+import java.util.Iterator;
import java.util.List;
-import net.sourceforge.stripes.action.FileBean;
-import net.sourceforge.stripes.action.HandlesEvent;
-import net.sourceforge.stripes.action.RedirectResolution;
-import net.sourceforge.stripes.action.Resolution;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import net.sourceforge.stripes.action.*;
import net.sourceforge.stripes.validation.LocalizableError;
import net.sourceforge.stripes.validation.Validate;
import net.sourceforge.stripes.validation.ValidationErrors;
@@ -37,11 +38,14 @@
import org.apache.wiki.WikiContext;
import org.apache.wiki.WikiEngine;
+import org.apache.wiki.WikiProvider;
import org.apache.wiki.api.WikiException;
import org.apache.wiki.api.WikiPage;
import org.apache.wiki.attachment.Attachment;
import org.apache.wiki.attachment.AttachmentManager;
import org.apache.wiki.auth.permissions.PagePermission;
+import org.apache.wiki.content.ContentManager;
+import org.apache.wiki.content.PageNotFoundException;
import org.apache.wiki.content.WikiPath;
import org.apache.wiki.i18n.InternationalizationManager;
import org.apache.wiki.log.Logger;
@@ -50,25 +54,51 @@
import org.apache.wiki.ui.stripes.HandlerPermission;
import org.apache.wiki.ui.stripes.WikiRequestContext;
-public class UploadActionBean extends AbstractPageActionBean
+...@urlbinding( "/attach/{page}" )
+public class AttachmentActionBean extends AbstractPageActionBean
{
- private static final Logger log = LoggerFactory.getLogger(
UploadActionBean.class );
+ private static final Logger log = LoggerFactory.getLogger(
AttachmentActionBean.class );
private List<FileBean> m_newAttachments;
private String m_changeNote = null;
+ private int m_version = WikiProvider.LATEST_VERSION;
+
+ /**
+ * Returns the version of the attachment to download. If not set by
+ * {...@link #setVersion(int)}, returns {...@link
WikiProvider#LATEST_VERSION}.
+ *
+ * @return the version
+ */
+ public int getVersion()
+ {
+ return m_version;
+ }
+
+ /**
+ * Sets the version of the attachment to download.
+ *
+ * @param version the version to download
+ */
+ @Validate( required = false )
+ public void setVersion( int version )
+ {
+ m_version = version;
+ }
+
/**
* Returns the new attachments uploaded by the user.
+ *
* @return the new files to attach
*/
public List<FileBean> getNewAttachments()
{
return m_newAttachments;
}
-
+
/**
- * Sets the set of new attachments that should be saved when the
+ * Sets the new attachments that should be saved when the
* {...@link #upload()} event is executed.
*
* @param newAttachments the new files to attach
@@ -79,7 +109,8 @@
}
/**
- * Sets the changenote for this upload; usually a short comment.
+ * Sets the changenote for when the {...@link #upload()} event is executed.
+ * Usually, this is a short comment.
*
* @param changenote the change note
*/
@@ -98,7 +129,7 @@
}
/**
- * Handler method that uploads a new attachment to the ViewActionBean.
+ * Handler method that uploads a new attachment.
*
* @return Resolution
*/
@@ -109,7 +140,7 @@
{
for( FileBean attachment : m_newAttachments )
{
- if ( attachment != null )
+ if( attachment != null )
{
executeUpload( attachment );
log.debug( "Executed upload; " + m_newAttachments.size() + "
attachments found." );
@@ -118,14 +149,102 @@
return new RedirectResolution( ViewActionBean.class, "attachments"
).addParameter( "page", getPage().getName() );
}
-
- @ValidationMethod
- public void validateFileType( ValidationErrors errors )
+
+ /**
+ * Handler method that downloads an attachment.
+ *
+ * @return a streaming resolution
+ * @throws Exception
+ */
+ @DefaultHandler
+ @HandlesEvent( "download" )
+ @HandlerPermission( permissionClass = PagePermission.class, target =
"${page.path}", actions = PagePermission.VIEW_ACTION )
+ @WikiRequestContext( "download" )
+ public Resolution download() throws Exception
+ {
+ Attachment att = (Attachment)getPage();
+ StreamingResolution r = new StreamingResolution( att.getContentType(),
att.getContentAsStream() );
+ r.setFilename( att.getPath().getName() );
+
+ // Add caching properties to response
+ HttpServletResponse response = getContext().getResponse();
+ if ( att.getLastModified() != null )
+ {
+ response.addDateHeader("Last-Modified",
att.getLastModified().getTime());
+ }
+ if( !att.isCacheable() )
+ {
+ response.addHeader( "Pragma", "no-cache" );
+ response.addHeader( "Cache-control", "no-cache" );
+ }
+
+ // If provider reports file size, add to response
+ if( att.getSize() >= 0 )
+ {
+ response.setContentLength( (int)att.getSize() );
+ }
+
+ // Send it!
+ if(log.isDebugEnabled())
+ {
+ HttpServletRequest req = getContext().getRequest();
+ String msg = "Attachment "+att.getFileName()+" sent to
"+req.getRemoteUser()+" on "+req.getRemoteAddr();
+ log.debug( msg );
+ }
+ return r;
+ }
+
+ /**
+ * Validates that a requested download exists at the path supplied. This
+ * method fires when the {...@link #download()} handler executes.
+ *
+ * @param errors the current validation errors collection
+ */
+ @ValidationMethod( on = "download" )
+ public void validateDownloadedFile( ValidationErrors errors )
+ {
+ // Check if the page + version exists
+ WikiPath path = getPage().getPath();
+ ContentManager manager = getContext().getEngine().getContentManager();
+ try
+ {
+ WikiPage page = manager.getPage( path, m_version );
+ // If the page isn't actually an attachment, add a validation error
+ if ( !page.isAttachment() )
+ {
+ // FIXME: bogus labels
+ errors.add( "download", new LocalizableError(
"notAnAttachment", path.toString(), m_version ) );
+ return;
+ }
+ }
+ catch( PageNotFoundException e )
+ {
+ // FIXME: bogus labels
+ errors.add( "cantFindAttachment", new LocalizableError(
e.getMessage(), path.toString(), m_version ) );
+ return;
+ }
+ catch( ProviderException e )
+ {
+ // FIXME: bogus labels
+ errors.add( "error", new LocalizableError( e.getMessage(),
path.toString(), m_version ) );
+ return;
+ }
+ }
+
+ /**
+ * Validates that an uploaded attachment has a clean file name, and isn't
+ * using a prohibited file extension. This method files when the
+ * {...@link #upload()} handler executes.
+ *
+ * @param errors the current validation errors collection
+ */
+ @ValidationMethod( on = "upload" )
+ public void validateUploadedFileType( ValidationErrors errors )
{
AttachmentManager mgr =
getContext().getEngine().getAttachmentManager();
- for ( FileBean attachment : m_newAttachments )
+ for( FileBean attachment : m_newAttachments )
{
- if ( attachment != null )
+ if( attachment != null )
{
// Clean the file name before validating
String filename = attachment.getFileName();
@@ -138,8 +257,8 @@
// Error message returns the i18n key name
errors.add( "newAttachments", new LocalizableError(
e.getMessage(), filename ) );
}
-
- if ( !mgr.isFileTypeAllowed( filename ) )
+
+ if( !mgr.isFileTypeAllowed( filename ) )
{
errors.add( "newAttachments", new LocalizableError(
"attach.bad.filetype", filename ) );
}
@@ -164,11 +283,11 @@
WikiContext context = getContext();
WikiEngine engine = context.getEngine();
AttachmentManager mgr = engine.getAttachmentManager();
-
+
// Get the file name, size etc from the FileBean
InputStream data = filebean.getInputStream();
String filename = filebean.getFileName();
-
+
// Cleanse the file name
filename = AttachmentManager.cleanFileName( filename );
log.debug( "file=" + filename );
@@ -176,40 +295,39 @@
// Get the name of the user uploading the file
Principal user = context.getCurrentUser();
- //
- // Check whether we already have this kind of a page.
- // If the "page" parameter already defines an attachment
- // name for an update, then we just use that file.
- // Otherwise we create a new attachment, and use the
- // filename given. Incidentally, this will also mean
- // that if the user uploads a file with the exact
- // same name than some other previous attachment,
- // then that attachment gains a new version.
- //
-
- Attachment att = mgr.getAttachmentInfo( context.getPage().getName() );
-
- if( att == null )
- {
- String contentType = "application/octet-stream"; // FIXME: This is
not a good guess
- WikiPath path = context.getPage().getPath().resolve(filename);
- att = engine.getContentManager().addPage( path, contentType );
+ // Look up the attachment for this page
+ WikiPage page = context.getPage();
+ Attachment attachment = null;
+ Iterator<WikiPage> attachments = mgr.listAttachments( page
).iterator();
+ while ( attachments.hasNext() )
+ {
+ WikiPage currentAttachment = attachments.next();
+ if( filename.equals( currentAttachment.getPath().getName() ) )
+ {
+ attachment = (Attachment) currentAttachment;
+ }
+ }
+
+ if( attachment == null )
+ {
+ WikiPath path = WikiPath.valueOf( page.getPath().toString() + "/"
+ filename );
+ attachment = engine.getContentManager().addPage( path,
filebean.getContentType() );
created = true;
}
if( user != null )
{
- att.setAuthor( user.getName() );
+ attachment.setAuthor( user.getName() );
}
if( m_changeNote != null && m_changeNote.length() > 0 )
{
- att.setAttribute( WikiPage.CHANGENOTE, m_changeNote );
+ attachment.setAttribute( WikiPage.CHANGENOTE, m_changeNote );
}
try
{
- engine.getAttachmentManager().storeAttachment( att, data );
+ engine.getAttachmentManager().storeAttachment( attachment, data );
}
catch( ProviderException pe )
{
@@ -217,15 +335,16 @@
// the i18n key
// here we have the context available, so we can
// internationalize it properly :
- throw new ProviderException( context.getBundle(
InternationalizationManager.CORE_BUNDLE )
- .getString( pe.getMessage() ), pe );
+ throw new ProviderException( context.getBundle(
InternationalizationManager.CORE_BUNDLE ).getString( pe.getMessage() ),
+ pe );
}
-
+
// Close the stream and delete the filebean, since we're done with it
data.close();
filebean.delete();
- log.info( "User " + user + " uploaded attachment to " +
getPage().getName() + " called " + filename + ", size " + att.getSize() );
+ log.info( "User " + user + " uploaded attachment to " +
getPage().getName() + " called " + filename + ", size "
+ + filebean.getSize() );
return created;
}
Modified:
incubator/jspwiki/trunk/src/java/org/apache/wiki/action/ViewActionBean.java
URL:
http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/java/org/apache/wiki/action/ViewActionBean.java?rev=777923&r1=777922&r2=777923&view=diff
==============================================================================
--- incubator/jspwiki/trunk/src/java/org/apache/wiki/action/ViewActionBean.java
(original)
+++ incubator/jspwiki/trunk/src/java/org/apache/wiki/action/ViewActionBean.java
Sat May 23 13:37:22 2009
@@ -151,7 +151,7 @@
throw new WikiException( "Page not supplied, and WikiEngine does
not define a front page! This is highly unusual." );
}
- // Is there an ALIAS attribute in the wiki pge?
+ // Is there an ALIAS attribute in the wiki page?
String specialUrl = (String) getPage().getAttribute( WikiPage.ALIAS );
if( specialUrl != null )
{
@@ -165,8 +165,14 @@
return new RedirectResolution( getContext().getViewURL( specialUrl
) );
}
- // If we got this far, it means the user supplied a page parameter, AND
- // it exists
+ // Ok, the page exists. If attachment, make sure it's directed to the
"info" handler
+ WikiPage page = getPage();
+ String handler = getContext().getEventName();
+ if ( getPage().isAttachment() && !"info".equals( handler ) )
+ {
+ return new RedirectResolution( ViewActionBean.class, "info"
).addParameter( "page", page.getPath().toString() );
+ }
+
return null;
}
Modified:
incubator/jspwiki/trunk/src/java/org/apache/wiki/url/StripesURLConstructor.java
URL:
http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/java/org/apache/wiki/url/StripesURLConstructor.java?rev=777923&r1=777922&r2=777923&view=diff
==============================================================================
---
incubator/jspwiki/trunk/src/java/org/apache/wiki/url/StripesURLConstructor.java
(original)
+++
incubator/jspwiki/trunk/src/java/org/apache/wiki/url/StripesURLConstructor.java
Sat May 23 13:37:22 2009
@@ -22,7 +22,9 @@
import java.net.MalformedURLException;
import java.net.URL;
-import java.util.*;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
import java.util.Map.Entry;
import net.sourceforge.stripes.action.ActionBean;
@@ -34,8 +36,8 @@
import org.apache.commons.lang.StringUtils;
import org.apache.wiki.WikiContext;
import org.apache.wiki.WikiEngine;
-import org.apache.wiki.action.AttachActionBean;
import org.apache.wiki.action.GroupActionBean;
+import org.apache.wiki.action.AttachmentActionBean;
import org.apache.wiki.action.WikiActionBean;
import org.apache.wiki.log.Logger;
import org.apache.wiki.log.LoggerFactory;
@@ -134,7 +136,7 @@
// Append the "name" parameter for page/group action beans (argh;
we have to do stupid if/else tricks)
if ( name != null )
{
- if( AttachActionBean.class.isAssignableFrom( beanClass ) )
+ if( AttachmentActionBean.class.isAssignableFrom( beanClass ) )
{
int slashAt = name.indexOf( '/' );
if ( slashAt == -1 )