Merge V1.4 to HEAD

- Ignored Enterprise-specific changes
   svn merge svn://svn.alfresco.com:3691/alfresco/BRANCHES/V1.4@3701 svn://svn.alfresco.com:3691/alfresco/BRANCHES/V1.4@3703 .
   svn merge svn://svn.alfresco.com:3691/alfresco/BRANCHES/V1.4@3704 svn://svn.alfresco.com:3691/alfresco/BRANCHES/V1.4@3705 .
   svn merge svn://svn.alfresco.com:3691/alfresco/BRANCHES/V1.4@3707 svn://svn.alfresco.com:3691/alfresco/BRANCHES/V1.4@3876 .
   svn revert root\projects\web-client\source\web\jsp\admin\admin-console.jsp


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@3879 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Derek Hulley
2006-09-21 23:35:51 +00:00
parent 35594dadf8
commit 7df3c602a1
54 changed files with 1634 additions and 675 deletions

View File

@@ -0,0 +1,110 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* 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.alfresco.web.app.servlet;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.alfresco.web.bean.repository.User;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* This servlet filter is used to restrict direct URL access to administration
* resource in the web client, for example the admin and jBPM consoles.
*
* @author gavinc
*/
public class AdminAuthenticationFilter implements Filter
{
private static final Log logger = LogFactory.getLog(AdminAuthenticationFilter.class);
/**
* @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
*/
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException
{
HttpServletRequest httpRequest = (HttpServletRequest)req;
HttpServletResponse httpResponse = (HttpServletResponse)res;
// The fact that this filter is being called means a request for a protected
// resource has taken place, check that the current user is in fact an
// administrator.
if (logger.isDebugEnabled())
logger.debug("Authorising request for protected resource: " + httpRequest.getRequestURI());
// there should be a user at this point so retrieve it
User user = AuthenticationHelper.getUser(httpRequest, httpResponse);
// if the user is present check to see whether it is an admin user
boolean isAdmin = (user != null && user.isAdmin());
if (isAdmin)
{
if (logger.isDebugEnabled())
logger.debug("Current user has admin authority, allowing access.");
// continue filter chaining if current user is admin user
chain.doFilter(req, res);
}
else
{
// return the 401 Forbidden error as the current user is not an administrator
// if the response has already been committed there's nothing we can do but
// print out a warning
if (httpResponse.isCommitted() == false)
{
if (logger.isDebugEnabled())
logger.debug("Current user does not have admin authority, returning 401 Forbidden error...");
httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN);
}
else
{
if (logger.isWarnEnabled())
logger.warn("Access denied to '" + httpRequest.getRequestURI() +
"'. The response has already been committed so a 401 Forbidden error could not be sent!");
}
}
}
/**
* @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
*/
public void init(FilterConfig config) throws ServletException
{
// nothing to do
}
/**
* @see javax.servlet.Filter#destroy()
*/
public void destroy()
{
// nothing to do
}
}

View File

@@ -100,39 +100,15 @@ public final class AuthenticationHelper
{
HttpSession session = httpRequest.getSession();
// examine the appropriate session for our User object
User user = null;
// retrieve the User object
User user = getUser(httpRequest, httpResponse);
// get the login bean if we're not in the portal
LoginBean loginBean = null;
if (Application.inPortalServer() == false)
{
user = (User)session.getAttribute(AUTHENTICATION_USER);
loginBean = (LoginBean)session.getAttribute(LOGIN_BEAN);
}
else
{
// naff solution as we need to enumerate all session keys until we find the one that
// should match our User objects - this is weak but we don't know how the underlying
// Portal vendor has decided to encode the objects in the session
if (portalUserKeyName.get() == null)
{
String userKeyPostfix = "?" + AUTHENTICATION_USER;
Enumeration enumNames = session.getAttributeNames();
while (enumNames.hasMoreElements())
{
String name = (String)enumNames.nextElement();
if (name.endsWith(userKeyPostfix))
{
// cache the key value once found!
portalUserKeyName.set(name);
break;
}
}
}
if (portalUserKeyName.get() != null)
{
user = (User)session.getAttribute(portalUserKeyName.get());
}
}
// setup the authentication context
WebApplicationContext wc = WebApplicationContextUtils.getRequiredWebApplicationContext(context);
@@ -388,6 +364,52 @@ public final class AuthenticationHelper
return AuthenticationStatus.Failure;
}
/**
* Attempts to retrieve the User object stored in the current session.
*
* @param httpRequest The HTTP request
* @param httpResponse The HTTP response
* @return The User object representing the current user or null if it could not be found
*/
public static User getUser(HttpServletRequest httpRequest, HttpServletResponse httpResponse)
{
HttpSession session = httpRequest.getSession();
User user = null;
// examine the appropriate session to try and find the User object
if (Application.inPortalServer() == false)
{
user = (User)session.getAttribute(AUTHENTICATION_USER);
}
else
{
// naff solution as we need to enumerate all session keys until we find the one that
// should match our User objects - this is weak but we don't know how the underlying
// Portal vendor has decided to encode the objects in the session
if (portalUserKeyName.get() == null)
{
String userKeyPostfix = "?" + AUTHENTICATION_USER;
Enumeration enumNames = session.getAttributeNames();
while (enumNames.hasMoreElements())
{
String name = (String)enumNames.nextElement();
if (name.endsWith(userKeyPostfix))
{
// cache the key value once found!
portalUserKeyName.set(name);
break;
}
}
}
if (portalUserKeyName.get() != null)
{
user = (User)session.getAttribute(portalUserKeyName.get());
}
}
return user;
}
/**
* Setup the Alfresco auth cookie value.
*

View File

@@ -0,0 +1,301 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* 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.alfresco.web.app.servlet;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.SocketException;
import java.net.URLEncoder;
import java.text.MessageFormat;
import java.util.Date;
import java.util.StringTokenizer;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.filestore.FileContentReader;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.MimetypeService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.security.AccessStatus;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.namespace.QName;
import org.alfresco.web.app.Application;
import org.alfresco.web.ui.common.Utils;
import org.apache.commons.logging.Log;
/**
* Base class for the download content servlets. Provides common
* processing for the request.
*
* @see org.alfresco.web.app.servlet.DownloadContentServlet
* @see org.alfresco.web.app.servlet.GuestDownloadContentServlet
*
* @author Kevin Roast
* @author gavinc
*/
public abstract class BaseDownloadContentServlet extends BaseServlet
{
private static final long serialVersionUID = -4558907921887235966L;
protected static final String MIMETYPE_OCTET_STREAM = "application/octet-stream";
protected static final String MSG_ERROR_CONTENT_MISSING = "error_content_missing";
protected static final String ARG_PROPERTY = "property";
protected static final String ARG_ATTACH = "attach";
protected static final String ARG_PATH = "path";
/**
* Gets the logger to use for this request.
* <p>
* This will show all debug entries from this class as though they
* came from the subclass.
*
* @return The logger
*/
protected abstract Log getLogger();
/**
* Processes the download request using the current context i.e. no
* authentication checks are made, it is presumed they have already
* been done.
*
* @param req The HTTP request
* @param res The HTTP response
* @param redirectToLogin Flag to determine whether to redirect to the login
* page if the user does not have the correct permissions
*/
protected void processDownloadRequest(HttpServletRequest req, HttpServletResponse res,
boolean redirectToLogin)
throws ServletException, IOException
{
Log logger = getLogger();
String uri = req.getRequestURI();
if (logger.isDebugEnabled())
{
String queryString = req.getQueryString();
logger.debug("Processing URL: " + uri +
((queryString != null && queryString.length() > 0) ? ("?" + queryString) : ""));
}
// TODO: add compression here?
// see http://servlets.com/jservlet2/examples/ch06/ViewResourceCompress.java for example
// only really needed if we don't use the built in compression of the servlet container
uri = uri.substring(req.getContextPath().length());
StringTokenizer t = new StringTokenizer(uri, "/");
int tokenCount = t.countTokens();
t.nextToken(); // skip servlet name
// attachment mode (either 'attach' or 'direct')
String attachToken = t.nextToken();
boolean attachment = attachToken.equals(ARG_ATTACH);
// get or calculate the noderef and filename to download as
NodeRef nodeRef;
String filename;
// do we have a path parameter instead of a NodeRef?
String path = req.getParameter(ARG_PATH);
if (path != null && path.length() != 0)
{
// process the name based path to resolve the NodeRef and the Filename element
PathRefInfo pathInfo = resolveNamePath(getServletContext(), path);
nodeRef = pathInfo.NodeRef;
filename = pathInfo.Filename;
}
else
{
// a NodeRef must have been specified if no path has been found
if (tokenCount < 6)
{
throw new IllegalArgumentException("Download URL did not contain all required args: " + uri);
}
// assume 'workspace' or other NodeRef based protocol for remaining URL elements
StoreRef storeRef = new StoreRef(t.nextToken(), t.nextToken());
String id = t.nextToken();
// build noderef from the appropriate URL elements
nodeRef = new NodeRef(storeRef, id);
// filename is last remaining token
filename = t.nextToken();
}
// get qualified of the property to get content from - default to ContentModel.PROP_CONTENT
QName propertyQName = ContentModel.PROP_CONTENT;
String property = req.getParameter(ARG_PROPERTY);
if (property != null && property.length() != 0)
{
propertyQName = QName.createQName(property);
}
if (logger.isDebugEnabled())
{
logger.debug("Found NodeRef: " + nodeRef.toString());
logger.debug("Will use filename: " + filename);
logger.debug("For property: " + propertyQName);
logger.debug("With attachment mode: " + attachment);
}
// get the services we need to retrieve the content
ServiceRegistry serviceRegistry = getServiceRegistry(getServletContext());
NodeService nodeService = serviceRegistry.getNodeService();
ContentService contentService = serviceRegistry.getContentService();
PermissionService permissionService = serviceRegistry.getPermissionService();
try
{
// check that the user has at least READ_CONTENT access - else redirect to the login page
if (permissionService.hasPermission(nodeRef, PermissionService.READ_CONTENT) == AccessStatus.DENIED)
{
if (logger.isDebugEnabled())
logger.debug("User does not have permissions to read content for NodeRef: " + nodeRef.toString());
if (redirectToLogin)
{
if (logger.isDebugEnabled())
logger.debug("Redirecting to login page...");
redirectToLoginPage(req, res, getServletContext());
}
else
{
if (logger.isDebugEnabled())
logger.debug("Returning 403 Forbidden error...");
res.sendError(HttpServletResponse.SC_FORBIDDEN);
}
return;
}
// check If-Modified-Since header and set Last-Modified header as appropriate
Date modified = (Date)nodeService.getProperty(nodeRef, ContentModel.PROP_MODIFIED);
long modifiedSince = req.getDateHeader("If-Modified-Since");
if (modifiedSince > 0L)
{
// round the date to the ignore millisecond value which is not supplied by header
long modDate = (modified.getTime() / 1000L) * 1000L;
if (modDate <= modifiedSince)
{
res.setStatus(304);
return;
}
}
res.setDateHeader("Last-Modified", modified.getTime());
if (attachment == true)
{
// set header based on filename - will force a Save As from the browse if it doesn't recognise it
// this is better than the default response of the browser trying to display the contents
res.setHeader("Content-Disposition", "attachment");
}
// get the content reader
ContentReader reader = contentService.getReader(nodeRef, propertyQName);
// ensure that it is safe to use
reader = FileContentReader.getSafeContentReader(
reader,
Application.getMessage(req.getSession(), MSG_ERROR_CONTENT_MISSING),
nodeRef, reader);
String mimetype = reader.getMimetype();
// fall back if unable to resolve mimetype property
if (mimetype == null || mimetype.length() == 0)
{
MimetypeService mimetypeMap = serviceRegistry.getMimetypeService();
mimetype = MIMETYPE_OCTET_STREAM;
int extIndex = filename.lastIndexOf('.');
if (extIndex != -1)
{
String ext = filename.substring(extIndex + 1);
String mt = mimetypeMap.getMimetypesByExtension().get(ext);
if (mt != null)
{
mimetype = mt;
}
}
}
// set mimetype for the content and the character encoding for the stream
res.setContentType(mimetype);
res.setCharacterEncoding(reader.getEncoding());
// get the content and stream directly to the response output stream
// assuming the repo is capable of streaming in chunks, this should allow large files
// to be streamed directly to the browser response stream.
try
{
reader.getContent( res.getOutputStream() );
}
catch (SocketException e)
{
if (e.getMessage().contains("ClientAbortException"))
{
// the client cut the connection - our mission was accomplished apart from a little error message
logger.error("Client aborted stream read:\n node: " + nodeRef + "\n content: " + reader);
}
else
{
throw e;
}
}
}
catch (Throwable err)
{
throw new AlfrescoRuntimeException("Error during download content servlet processing: " + err.getMessage(), err);
}
}
/**
* Helper to generate a URL to a content node for downloading content from the server.
*
* @param pattern The pattern to use for the URL
* @param ref NodeRef of the content node to generate URL for (cannot be null)
* @param name File name to return in the URL (cannot be null)
*
* @return URL to download the content from the specified node
*/
protected final static String generateUrl(String pattern, NodeRef ref, String name)
{
String url = null;
try
{
url = MessageFormat.format(pattern, new Object[] {
ref.getStoreRef().getProtocol(),
ref.getStoreRef().getIdentifier(),
ref.getId(),
Utils.replace(URLEncoder.encode(name, "UTF-8"), "+", "%20") } );
}
catch (UnsupportedEncodingException uee)
{
throw new AlfrescoRuntimeException("Failed to encode content URL for node: " + ref, uee);
}
return url;
}
}

View File

@@ -73,7 +73,7 @@ public class CommandServlet extends BaseServlet
/**
* @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
protected void doGet(HttpServletRequest req, HttpServletResponse res)
protected void service(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException
{
String uri = req.getRequestURI();

View File

@@ -17,32 +17,12 @@
package org.alfresco.web.app.servlet;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.SocketException;
import java.net.URLEncoder;
import java.text.MessageFormat;
import java.util.Date;
import java.util.StringTokenizer;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.filestore.FileContentReader;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.MimetypeService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.security.AccessStatus;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.namespace.QName;
import org.alfresco.web.app.Application;
import org.alfresco.web.ui.common.Utils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -71,26 +51,30 @@ import org.apache.commons.logging.LogFactory;
* Like most Alfresco servlets, the URL may be followed by a valid 'ticket' argument for authentication:
* ?ticket=1234567890
* <p>
* And/or also followed by the "?guest=true" argument to force guest access login for the URL.
* And/or also followed by the "?guest=true" argument to force guest access login for the URL. If the
* guest=true parameter is used the current session will be logged out and the guest user logged in.
* Therefore upon completion of this request the current user will be "guest".
* <p>
* If the user attempting the request is not authorised to access the requested node the login page
* will be redirected to.
*
* @author Kevin Roast
* @author gavinc
*/
public class DownloadContentServlet extends BaseServlet
public class DownloadContentServlet extends BaseDownloadContentServlet
{
private static final long serialVersionUID = -4558907921887235966L;
private static final long serialVersionUID = -576405943603122206L;
private static Log logger = LogFactory.getLog(DownloadContentServlet.class);
private static final String DOWNLOAD_URL = "/download/attach/{0}/{1}/{2}/{3}";
private static final String BROWSER_URL = "/download/direct/{0}/{1}/{2}/{3}";
private static final String MIMETYPE_OCTET_STREAM = "application/octet-stream";
private static final String MSG_ERROR_CONTENT_MISSING = "error_content_missing";
private static final String ARG_PROPERTY = "property";
private static final String ARG_ATTACH = "attach";
private static final String ARG_PATH = "path";
@Override
protected Log getLogger()
{
return logger;
}
/**
* @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
@@ -98,10 +82,12 @@ public class DownloadContentServlet extends BaseServlet
protected void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException
{
String uri = req.getRequestURI();
if (logger.isDebugEnabled())
logger.debug("Processing URL: " + uri + (req.getQueryString() != null ? ("?" + req.getQueryString()) : ""));
{
String queryString = req.getQueryString();
logger.debug("Authenticating request to URL: " + req.getRequestURI() +
((queryString != null && queryString.length() > 0) ? ("?" + queryString) : ""));
}
AuthenticationStatus status = servletAuthenticate(req, res);
if (status == AuthenticationStatus.Failure)
@@ -109,159 +95,7 @@ public class DownloadContentServlet extends BaseServlet
return;
}
// TODO: add compression here?
// see http://servlets.com/jservlet2/examples/ch06/ViewResourceCompress.java for example
// only really needed if we don't use the built in compression of the servlet container
uri = uri.substring(req.getContextPath().length());
StringTokenizer t = new StringTokenizer(uri, "/");
int tokenCount = t.countTokens();
t.nextToken(); // skip servlet name
// attachment mode (either 'attach' or 'direct')
String attachToken = t.nextToken();
boolean attachment = attachToken.equals(ARG_ATTACH);
// get or calculate the noderef and filename to download as
NodeRef nodeRef;
String filename;
// do we have a path parameter instead of a NodeRef?
String path = req.getParameter(ARG_PATH);
if (path != null && path.length() != 0)
{
// process the name based path to resolve the NodeRef and the Filename element
PathRefInfo pathInfo = resolveNamePath(getServletContext(), path);
nodeRef = pathInfo.NodeRef;
filename = pathInfo.Filename;
}
else
{
// a NodeRef must have been specified if no path has been found
if (tokenCount < 6)
{
throw new IllegalArgumentException("Download URL did not contain all required args: " + uri);
}
// assume 'workspace' or other NodeRef based protocol for remaining URL elements
StoreRef storeRef = new StoreRef(t.nextToken(), t.nextToken());
String id = t.nextToken();
// build noderef from the appropriate URL elements
nodeRef = new NodeRef(storeRef, id);
// filename is last remaining token
filename = t.nextToken();
}
// get qualified of the property to get content from - default to ContentModel.PROP_CONTENT
QName propertyQName = ContentModel.PROP_CONTENT;
String property = req.getParameter(ARG_PROPERTY);
if (property != null && property.length() != 0)
{
propertyQName = QName.createQName(property);
}
if (logger.isDebugEnabled())
{
logger.debug("Found NodeRef: " + nodeRef.toString());
logger.debug("Will use filename: " + filename);
logger.debug("For property: " + propertyQName);
logger.debug("With attachment mode: " + attachment);
}
// get the services we need to retrieve the content
ServiceRegistry serviceRegistry = getServiceRegistry(getServletContext());
NodeService nodeService = serviceRegistry.getNodeService();
ContentService contentService = serviceRegistry.getContentService();
PermissionService permissionService = serviceRegistry.getPermissionService();
try
{
// check that the user has at least READ_CONTENT access - else redirect to the login page
if (permissionService.hasPermission(nodeRef, PermissionService.READ_CONTENT) == AccessStatus.DENIED)
{
if (logger.isDebugEnabled())
logger.debug("User does not have permissions to read content for NodeRef: " + nodeRef.toString());
redirectToLoginPage(req, res, getServletContext());
return;
}
// check If-Modified-Since header and set Last-Modified header as appropriate
Date modified = (Date)nodeService.getProperty(nodeRef, ContentModel.PROP_MODIFIED);
long modifiedSince = req.getDateHeader("If-Modified-Since");
if (modifiedSince > 0L)
{
// round the date to the ignore millisecond value which is not supplied by header
long modDate = (modified.getTime() / 1000L) * 1000L;
if (modDate <= modifiedSince)
{
res.setStatus(304);
return;
}
}
res.setDateHeader("Last-Modified", modified.getTime());
if (attachment == true)
{
// set header based on filename - will force a Save As from the browse if it doesn't recognise it
// this is better than the default response of the browser trying to display the contents
res.setHeader("Content-Disposition", "attachment");
}
// get the content reader
ContentReader reader = contentService.getReader(nodeRef, propertyQName);
// ensure that it is safe to use
reader = FileContentReader.getSafeContentReader(
reader,
Application.getMessage(req.getSession(), MSG_ERROR_CONTENT_MISSING),
nodeRef, reader);
String mimetype = reader.getMimetype();
// fall back if unable to resolve mimetype property
if (mimetype == null || mimetype.length() == 0)
{
MimetypeService mimetypeMap = serviceRegistry.getMimetypeService();
mimetype = MIMETYPE_OCTET_STREAM;
int extIndex = filename.lastIndexOf('.');
if (extIndex != -1)
{
String ext = filename.substring(extIndex + 1);
String mt = mimetypeMap.getMimetypesByExtension().get(ext);
if (mt != null)
{
mimetype = mt;
}
}
}
// set mimetype for the content and the character encoding for the stream
res.setContentType(mimetype);
res.setCharacterEncoding(reader.getEncoding());
// get the content and stream directly to the response output stream
// assuming the repo is capable of streaming in chunks, this should allow large files
// to be streamed directly to the browser response stream.
try
{
reader.getContent( res.getOutputStream() );
}
catch (SocketException e)
{
if (e.getMessage().contains("ClientAbortException"))
{
// the client cut the connection - our mission was accomplished apart from a little error message
logger.error("Client aborted stream read:\n node: " + nodeRef + "\n content: " + reader);
}
else
{
throw e;
}
}
}
catch (Throwable err)
{
throw new AlfrescoRuntimeException("Error during download content servlet processing: " + err.getMessage(), err);
}
processDownloadRequest(req, res, true);
}
/**
@@ -276,22 +110,7 @@ public class DownloadContentServlet extends BaseServlet
*/
public final static String generateDownloadURL(NodeRef ref, String name)
{
String url = null;
try
{
url = MessageFormat.format(DOWNLOAD_URL, new Object[] {
ref.getStoreRef().getProtocol(),
ref.getStoreRef().getIdentifier(),
ref.getId(),
Utils.replace(URLEncoder.encode(name, "UTF-8"), "+", "%20") } );
}
catch (UnsupportedEncodingException uee)
{
throw new AlfrescoRuntimeException("Failed to encode content URL for node: " + ref, uee);
}
return url;
return generateUrl(DOWNLOAD_URL, ref, name);
}
/**
@@ -306,21 +125,6 @@ public class DownloadContentServlet extends BaseServlet
*/
public final static String generateBrowserURL(NodeRef ref, String name)
{
String url = null;
try
{
url = MessageFormat.format(BROWSER_URL, new Object[] {
ref.getStoreRef().getProtocol(),
ref.getStoreRef().getIdentifier(),
ref.getId(),
Utils.replace(URLEncoder.encode(name, "UTF-8"), "+", "%20") } );
}
catch (UnsupportedEncodingException uee)
{
throw new AlfrescoRuntimeException("Failed to encode content URL for node: " + ref, uee);
}
return url;
return generateUrl(BROWSER_URL, ref, name);
}
}

View File

@@ -145,12 +145,31 @@ public final class FacesHelper
else
{
// make sure we do not have illegal characters in the id
id = makeLegalId(id);
}
component.setId(id);
}
/**
* Makes the given id a legal JSF component id by replacing illegal
* characters with underscores.
*
* @param id The id to make legal
* @return The legalised id
*/
public static String makeLegalId(String id)
{
if (id != null)
{
// replace illegal ID characters with an underscore
id = id.replace(':', '_');
id = id.replace(' ', '_');
// TODO: check all other illegal characters - only allowed dash and underscore
}
component.setId(id);
return id;
}
/**

View File

@@ -0,0 +1,147 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* 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.alfresco.web.app.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.security.PermissionService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Servlet responsible for streaming node content from the repo directly to the response stream.
* The appropriate mimetype is calculated based on filename extension.
* <p>
* The URL to the servlet should be generated thus:
* <pre>/alfresco/guestDownload/attach/workspace/SpacesStore/0000-0000-0000-0000/myfile.pdf</pre>
* or
* <pre>/alfresco/guestDownload/direct/workspace/SpacesStore/0000-0000-0000-0000/myfile.pdf</pre>
* or
* <pre>/alfresco/guestDownload/[direct|attach]?path=/Company%20Home/MyFolder/myfile.pdf</pre>
* The protocol, followed by either the store and Id (NodeRef) or instead specify a name based
* encoded Path to the content, note that the filename element is used for mimetype lookup and
* as the returning filename for the response stream.
* <p>
* The 'attach' or 'direct' element is used to indicate whether to display the stream directly
* in the browser or download it as a file attachment.
* <p>
* By default, the download assumes that the content is on the
* {@link org.alfresco.model.ContentModel#PROP_CONTENT content property}.<br>
* To retrieve the content of a specific model property, use a 'property' arg, providing the workspace,
* node ID AND the qualified name of the property.
* <p>
* This servlet only accesses content available to the guest user. If the guest user does not
* have access to the requested a 401 Forbidden response is returned to the caller.
* <p>
* This servlet does not effect the current session, therefore if guest access is required to a
* resource this servlet can be used without logging out the current user.
*
* @author gavinc
*/
public class GuestDownloadContentServlet extends BaseDownloadContentServlet
{
private static final long serialVersionUID = -5258137503339817457L;
private static Log logger = LogFactory.getLog(GuestDownloadContentServlet.class);
private static final String DOWNLOAD_URL = "/guestDownload/attach/{0}/{1}/{2}/{3}";
private static final String BROWSER_URL = "/guestDownload/direct/{0}/{1}/{2}/{3}";
@Override
protected Log getLogger()
{
return logger;
}
/**
* @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
protected void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException
{
if (logger.isDebugEnabled())
{
String queryString = req.getQueryString();
logger.debug("Setting up guest access to URL: " + req.getRequestURI() +
((queryString != null && queryString.length() > 0) ? ("?" + queryString) : ""));
}
DownloadContentWork dcw = new DownloadContentWork(req, res);
AuthenticationUtil.runAs(dcw, PermissionService.GUEST_AUTHORITY);
}
/**
* Helper to generate a URL to a content node for downloading content from the server.
* The content is supplied as an HTTP1.1 attachment to the response. This generally means
* a browser should prompt the user to save the content to specified location.
*
* @param ref NodeRef of the content node to generate URL for (cannot be null)
* @param name File name to return in the URL (cannot be null)
*
* @return URL to download the content from the specified node
*/
public final static String generateDownloadURL(NodeRef ref, String name)
{
return generateUrl(DOWNLOAD_URL, ref, name);
}
/**
* Helper to generate a URL to a content node for downloading content from the server.
* The content is supplied directly in the reponse. This generally means a browser will
* attempt to open the content directly if possible, else it will prompt to save the file.
*
* @param ref NodeRef of the content node to generate URL for (cannot be null)
* @param name File name to return in the URL (cannot be null)
*
* @return URL to download the content from the specified node
*/
public final static String generateBrowserURL(NodeRef ref, String name)
{
return generateUrl(BROWSER_URL, ref, name);
}
/**
* Class to wrap the call to processDownloadRequest.
*
* @author gavinc
*/
public class DownloadContentWork implements RunAsWork<Object>
{
private HttpServletRequest req = null;
private HttpServletResponse res = null;
public DownloadContentWork(HttpServletRequest req, HttpServletResponse res)
{
this.req = req;
this.res = res;
}
public Object doWork() throws Exception
{
processDownloadRequest(this.req, this.res, false);
return null;
}
}
}

View File

@@ -86,8 +86,6 @@ public class TemplateContentServlet extends BaseServlet
private static final String DEFAULT_URL = "/template/{0}/{1}/{2}";
private static final String TEMPLATE_URL = "/template/{0}/{1}/{2}/{3}/{4}/{5}";
private static final String MSG_ERROR_CONTENT_MISSING = "error_content_missing";
private static final String ARG_MIMETYPE = "mimetype";
private static final String ARG_TEMPLATE_PATH = "templatePath";
private static final String ARG_CONTEXT_PATH = "contextPath";
@@ -95,13 +93,17 @@ public class TemplateContentServlet extends BaseServlet
/**
* @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
protected void doGet(HttpServletRequest req, HttpServletResponse res)
protected void service(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException
{
String uri = req.getRequestURI();
if (logger.isDebugEnabled())
logger.debug("Processing URL: " + uri + (req.getQueryString() != null ? ("?" + req.getQueryString()) : ""));
{
String queryString = req.getQueryString();
logger.debug("Processing URL: " + uri +
((queryString != null && queryString.length() > 0) ? ("?" + queryString) : ""));
}
AuthenticationStatus status = servletAuthenticate(req, res);
if (status == AuthenticationStatus.Failure)
@@ -258,6 +260,7 @@ public class TemplateContentServlet extends BaseServlet
*
* @return an object model ready for executing template against
*/
@SuppressWarnings("unchecked")
private Object getModel(ServiceRegistry services, HttpServletRequest req, NodeRef templateRef, NodeRef nodeRef)
{
// build FreeMarker default model and merge

View File

@@ -22,7 +22,7 @@ public interface AjaxCommand
* expression. Parameters required to call the method can be retrieved
* from the request.
*
* Currently the content type of the response will always be text/html, in the
* Currently the content type of the response will always be text/xml, in the
* future sublcasses may provide a mechanism to allow the content type to be set
* dynamically.
*

View File

@@ -42,7 +42,7 @@ public class InvokeCommand extends BaseAjaxCommand
// NOTE: it doesn't seem to matter what the content type of the response is (at least with Dojo),
// it determines it's behaviour from the mimetype specified in the AJAX call on the client,
// therefore, for now we will always return a content type of text/html.
// therefore, for now we will always return a content type of text/xml.
// In the future we may use annotations on the method to be called to specify what content
// type should be used for the response.
@@ -53,7 +53,7 @@ public class InvokeCommand extends BaseAjaxCommand
RenderKit renderKit = renderFactory.getRenderKit(facesContext,
viewRoot.getRenderKitId());
ResponseWriter writer = renderKit.createResponseWriter(
new OutputStreamWriter(os), MimetypeMap.MIMETYPE_HTML, "UTF-8");
new OutputStreamWriter(os), MimetypeMap.MIMETYPE_XML, "UTF-8");
facesContext.setResponseWriter(writer);
response.setContentType(writer.getContentType());