diff --git a/source/java/org/alfresco/web/app/servlet/BaseServlet.java b/source/java/org/alfresco/web/app/servlet/BaseServlet.java index 5f4a945015..7a252331bc 100644 --- a/source/java/org/alfresco/web/app/servlet/BaseServlet.java +++ b/source/java/org/alfresco/web/app/servlet/BaseServlet.java @@ -23,6 +23,7 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.StringTokenizer; import javax.faces.context.FacesContext; import javax.servlet.ServletContext; @@ -140,9 +141,10 @@ public abstract class BaseServlet extends HttpServlet throws IOException { // authentication failed - so end servlet execution and redirect to login page - // also save the requested URL so the login page knows where to redirect too later + // also save the full requested URL so the login page knows where to redirect too later res.sendRedirect(req.getContextPath() + FACES_SERVLET + Application.getLoginPage(sc)); String uri = req.getRequestURI(); + String url = uri + (req.getQueryString() != null ? ("?" + req.getQueryString()) : ""); if (uri.indexOf(req.getContextPath() + FACES_SERVLET) != -1) { // if we find a JSF servlet reference in the URI then we need to check if the rest of the @@ -150,12 +152,12 @@ public abstract class BaseServlet extends HttpServlet int jspIndex = uri.indexOf(BaseServlet.FACES_SERVLET) + BaseServlet.FACES_SERVLET.length(); if (uri.length() > jspIndex && BaseServlet.validRedirectJSP(uri.substring(jspIndex))) { - req.getSession().setAttribute(LoginBean.LOGIN_REDIRECT_KEY, uri); + req.getSession().setAttribute(LoginBean.LOGIN_REDIRECT_KEY, url); } } else { - req.getSession().setAttribute(LoginBean.LOGIN_REDIRECT_KEY, uri); + req.getSession().setAttribute(LoginBean.LOGIN_REDIRECT_KEY, url); } } @@ -179,9 +181,47 @@ public abstract class BaseServlet extends HttpServlet * Resolves the given path elements to a NodeRef in the current repository * * @param context Faces context - * @param args The elements of the path to lookup + * @param args The elements of the path to lookup */ public static NodeRef resolveWebDAVPath(FacesContext context, String[] args) + { + WebApplicationContext wc = FacesContextUtils.getRequiredWebApplicationContext(context); + return resolveWebDAVPath(wc, args, true); + } + + /** + * Resolves the given path elements to a NodeRef in the current repository + * + * @param context ServletContext context + * @param args The elements of the path to lookup + */ + public static NodeRef resolveWebDAVPath(ServletContext context, String[] args) + { + WebApplicationContext wc = WebApplicationContextUtils.getRequiredWebApplicationContext(context); + return resolveWebDAVPath(wc, args, true); + } + + /** + * Resolves the given path elements to a NodeRef in the current repository + * + * @param context ServletContext context + * @param args The elements of the path to lookup + * @param decode True to decode the arg from UTF-8 format, false for no decoding + */ + public static NodeRef resolveWebDAVPath(ServletContext context, String[] args, boolean decode) + { + WebApplicationContext wc = WebApplicationContextUtils.getRequiredWebApplicationContext(context); + return resolveWebDAVPath(wc, args, decode); + } + + /** + * Resolves the given path elements to a NodeRef in the current repository + * + * @param WebApplicationContext Context + * @param args The elements of the path to lookup + * @param decode True to decode the arg from UTF-8 format, false for no decoding + */ + private static NodeRef resolveWebDAVPath(WebApplicationContext wc, String[] args, boolean decode) { NodeRef nodeRef = null; @@ -193,7 +233,7 @@ public abstract class BaseServlet extends HttpServlet // create a list of path elements (decode the URL as we go) for (int x = 1; x < args.length; x++) { - paths.add(URLDecoder.decode(args[x], "UTF-8")); + paths.add(decode ? URLDecoder.decode(args[x], "UTF-8") : args[x]); } if (logger.isDebugEnabled()) @@ -203,7 +243,6 @@ public abstract class BaseServlet extends HttpServlet NodeRef companyHome = new NodeRef(Repository.getStoreRef(), Application.getCompanyRootId()); - WebApplicationContext wc = FacesContextUtils.getRequiredWebApplicationContext(context); FileFolderService ffs = (FileFolderService)wc.getBean("FileFolderService"); file = ffs.resolveNamePath(companyHome, paths); nodeRef = file.getNodeRef(); @@ -228,4 +267,50 @@ public abstract class BaseServlet extends HttpServlet return nodeRef; } + + /** + * Resolve a name based into a NodeRef and Filename string + * + * @param sc ServletContext + * @param path 'cm:name' based path using the '/' character as a separator + * + * @return PathRefInfo structure containing the resolved NodeRef and filename + * + * @throws IllegalArgumentException + */ + public final static PathRefInfo resolveNamePath(ServletContext sc, String path) + { + StringTokenizer t = new StringTokenizer(path, "/"); + int tokenCount = t.countTokens(); + String[] elements = new String[tokenCount]; + for (int i=0; i args = new HashMap(8, 1.0f); + Enumeration names = req.getParameterNames(); + while (names.hasMoreElements()) + { + String name = (String)names.nextElement(); + args.put(name, req.getParameter(name)); } try @@ -113,13 +125,13 @@ public class CommandServlet extends BaseServlet CommandProcessor processor = createCommandProcessor(procName); // validate that the processor has everything it needs to run the command - ServiceRegistry serviceRegistry = getServiceRegistry(getServletContext()); - if (processor.validateArguments(serviceRegistry, command, args) == false) + if (processor.validateArguments(getServletContext(), command, args, urlElements) == false) { redirectToLoginPage(req, res, getServletContext()); return; } + ServiceRegistry serviceRegistry = getServiceRegistry(getServletContext()); UserTransaction txn = null; try { diff --git a/source/java/org/alfresco/web/app/servlet/DownloadContentServlet.java b/source/java/org/alfresco/web/app/servlet/DownloadContentServlet.java index 961e45a290..ea9835c25b 100644 --- a/source/java/org/alfresco/web/app/servlet/DownloadContentServlet.java +++ b/source/java/org/alfresco/web/app/servlet/DownloadContentServlet.java @@ -52,9 +52,11 @@ import org.apache.commons.logging.LogFactory; *
/alfresco/download/attach/workspace/SpacesStore/0000-0000-0000-0000/myfile.pdf
* or *
/alfresco/download/direct/workspace/SpacesStore/0000-0000-0000-0000/myfile.pdf
- *

- * The store protocol, followed by the store ID, followed by the content Node Id - * the last element is used for mimetype calculation and browser default filename. + * or + *

/alfresco/download/[direct|attach]?path=/Company%20Home/MyFolder/myfile.pdf
+ * 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. *

* 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. @@ -86,6 +88,7 @@ public class DownloadContentServlet extends BaseServlet private static final String ARG_PROPERTY = "property"; private static final String ARG_ATTACH = "attach"; + private static final String ARG_PATH = "path"; /** * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) @@ -93,11 +96,6 @@ public class DownloadContentServlet extends BaseServlet protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { - // The URL contains multiple parts - // /alfresco/download/attach/workspace/SpacesStore/0000-0000-0000-0000/myfile.pdf - // the protocol, followed by the store, followed by the Id - // the last part is only used for mimetype and browser use - // may be followed by valid ticket for pre-authenticated usage: ?ticket=1234567890 String uri = req.getRequestURI(); if (logger.isDebugEnabled()) @@ -114,34 +112,53 @@ public class DownloadContentServlet extends BaseServlet // 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, "/"); - if (t.countTokens() < 6) - { - throw new IllegalArgumentException("Download URL did not contain all required args: " + 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); - StoreRef storeRef = new StoreRef(t.nextToken(), t.nextToken()); - String id = t.nextToken(); - String filename = t.nextToken(); + // get or calculate the noderef and filename to download as + NodeRef nodeRef; + String filename; - // get property qualified name - QName propertyQName = null; - String property = req.getParameter(ARG_PROPERTY); - if (property == null || property.length() == 0) + // do we have a path parameter instead of a NodeRef? + String path = req.getParameter(ARG_PATH); + if (path != null && path.length() != 0) { - propertyQName = ContentModel.PROP_CONTENT; + // 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 { - propertyQName = QName.createQName(property); + // 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(); } - // build noderef from the appropriate URL elements - NodeRef nodeRef = new NodeRef(storeRef, id); + // 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()) { @@ -199,7 +216,9 @@ public class DownloadContentServlet extends BaseServlet } } } + // 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 diff --git a/source/java/org/alfresco/web/app/servlet/TemplateContentServlet.java b/source/java/org/alfresco/web/app/servlet/TemplateContentServlet.java index 46cb40b794..8d51790e69 100644 --- a/source/java/org/alfresco/web/app/servlet/TemplateContentServlet.java +++ b/source/java/org/alfresco/web/app/servlet/TemplateContentServlet.java @@ -27,7 +27,6 @@ import java.util.StringTokenizer; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; import javax.transaction.UserTransaction; import org.alfresco.error.AlfrescoRuntimeException; @@ -56,10 +55,15 @@ import org.apache.commons.logging.LogFactory; *

/alfresco/template/workspace/SpacesStore/0000-0000-0000-0000
* or *
/alfresco/template/workspace/SpacesStore/0000-0000-0000-0000/workspace/SpacesStore/0000-0000-0000-0000
+ * or + *
/alfresco/template?templatePath=/Company%20Home/Data%20Dictionary/Presentation%20Templates/doc_info.ftl&contextPath=/Company%20Home/mydoc.txt
*

* The store protocol, followed by the store ID, followed by the content Node Id used to * identify the node to execute the default template for. The second set of elements encode - * the store and node Id of the template to used if a default is not set or not requested. + * the store and node Id of the template to used if a default is not set or not requested. Instead + * of using NodeRef references to the template and context, path arguments can be used. The URL args + * of 'templatePath' and 'contextPath' can be used instead to specify name based encoded Paths to the + * template and its context. *

* The URL may be followed by a 'mimetype' argument specifying the mimetype to return the result as * on the stream. Otherwise it is assumed that HTML is the default response mimetype. @@ -85,6 +89,8 @@ public class TemplateContentServlet extends BaseServlet 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"; /** * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) @@ -106,25 +112,54 @@ public class TemplateContentServlet extends BaseServlet uri = uri.substring(req.getContextPath().length()); StringTokenizer t = new StringTokenizer(uri, "/"); int tokenCount = t.countTokens(); - if (tokenCount < 4) - { - throw new IllegalArgumentException("Template Servlet URL did not contain all required args: " + uri); - } t.nextToken(); // skip servlet name - // get NodeRef to the content - StoreRef storeRef = new StoreRef(t.nextToken(), t.nextToken()); - NodeRef nodeRef = new NodeRef(storeRef, t.nextToken()); + NodeRef nodeRef = null; + NodeRef templateRef = null; + + String contentPath = req.getParameter(ARG_CONTEXT_PATH); + if (contentPath != null && contentPath.length() != 0) + { + // process the name based path to resolve the NodeRef + PathRefInfo pathInfo = resolveNamePath(getServletContext(), contentPath); + + nodeRef = pathInfo.NodeRef; + } + else if (tokenCount > 3) + { + // get NodeRef to the content from the URL elements + StoreRef storeRef = new StoreRef(t.nextToken(), t.nextToken()); + nodeRef = new NodeRef(storeRef, t.nextToken()); + } // get NodeRef to the template if supplied - NodeRef templateRef = null; - if (tokenCount >= 7) + String templatePath = req.getParameter(ARG_TEMPLATE_PATH); + if (templatePath != null && templatePath.length() != 0) { - storeRef = new StoreRef(t.nextToken(), t.nextToken()); + // process the name based path to resolve the NodeRef + PathRefInfo pathInfo = resolveNamePath(getServletContext(), templatePath); + + templateRef = pathInfo.NodeRef; + } + else if (tokenCount == 7) + { + StoreRef storeRef = new StoreRef(t.nextToken(), t.nextToken()); templateRef = new NodeRef(storeRef, t.nextToken()); } + // if no context is specified, use the template itself + // TODO: should this default to something else? + if (nodeRef == null && templateRef != null) + { + nodeRef = templateRef; + } + + if (nodeRef == null) + { + throw new TemplateException("Not enough arguments supplied in URL."); + } + // get the services we need to retrieve the content ServiceRegistry serviceRegistry = getServiceRegistry(getServletContext()); NodeService nodeService = serviceRegistry.getNodeService(); diff --git a/source/java/org/alfresco/web/app/servlet/command/BaseNodeCommandProcessor.java b/source/java/org/alfresco/web/app/servlet/command/BaseNodeCommandProcessor.java index 7e91411979..15a326f457 100644 --- a/source/java/org/alfresco/web/app/servlet/command/BaseNodeCommandProcessor.java +++ b/source/java/org/alfresco/web/app/servlet/command/BaseNodeCommandProcessor.java @@ -16,11 +16,15 @@ */ package org.alfresco.web.app.servlet.command; -import org.alfresco.service.ServiceRegistry; +import java.util.Map; + +import javax.servlet.ServletContext; + import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.security.AccessStatus; import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.web.bean.repository.Repository; /** * Initial implementation of a Command Processor that is always passed enough URL elements @@ -37,21 +41,21 @@ public abstract class BaseNodeCommandProcessor implements CommandProcessor protected NodeRef targetRef; /** - * @see org.alfresco.web.app.servlet.command.CommandProcessor#validateArguments(org.alfresco.service.ServiceRegistry, java.lang.String, java.lang.String[]) + * @see org.alfresco.web.app.servlet.command.CommandProcessor#validateArguments(javax.servlet.ServletContext, java.lang.String, java.util.Map, java.lang.String[]) */ - public boolean validateArguments(ServiceRegistry serviceRegistry, String command, String[] args) + public boolean validateArguments(ServletContext sc, String command, Map args, String[] urlElements) { - if (args.length < 3) + if (urlElements.length < 3) { throw new IllegalArgumentException("Not enough URL arguments passed to command servlet."); } // get NodeRef to the node with the workflow attached to it - StoreRef storeRef = new StoreRef(args[0], args[1]); - this.targetRef = new NodeRef(storeRef, args[2]); + StoreRef storeRef = new StoreRef(urlElements[0], urlElements[1]); + this.targetRef = new NodeRef(storeRef, urlElements[2]); // get the services we need to execute the workflow command - PermissionService permissionService = serviceRegistry.getPermissionService(); + PermissionService permissionService = Repository.getServiceRegistry(sc).getPermissionService(); // check that the user has at least READ access on the node - else redirect to the login page return (permissionService.hasPermission(this.targetRef, PermissionService.READ) == AccessStatus.ALLOWED); diff --git a/source/java/org/alfresco/web/app/servlet/command/CommandProcessor.java b/source/java/org/alfresco/web/app/servlet/command/CommandProcessor.java index 17e3701c13..08dca516bb 100644 --- a/source/java/org/alfresco/web/app/servlet/command/CommandProcessor.java +++ b/source/java/org/alfresco/web/app/servlet/command/CommandProcessor.java @@ -17,9 +17,10 @@ package org.alfresco.web.app.servlet.command; import java.io.PrintWriter; +import java.util.Map; +import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpSession; import org.alfresco.service.ServiceRegistry; @@ -45,13 +46,15 @@ public interface CommandProcessor * convert the supplied arguments to the objects it expects, and also check any permissions * that are required by the current user to execute the command. * - * @param serviceRegistry ServiceRegistry instance + * @param sc ServletContext, can be used to retrieve ServiceRegistry instance + * from the Repository bean. * @param command Name of the command the arguments are for - * @param args String[] of the remaining URL arguments to the command servlet. + * @param args Map of URL args passed to the command servlet + * @param urlElements String[] of the remaining URL arguments to the command servlet * * @return true if the command can be executed by the current user given the supplied args. */ - public boolean validateArguments(ServiceRegistry serviceRegistry, String command, String[] args); + public boolean validateArguments(ServletContext sc, String command, Map args, String[] urlElements); /** * Process the supplied command name. It is the responsibility of the Command Processor diff --git a/source/java/org/alfresco/web/app/servlet/command/ScriptCommandProcessor.java b/source/java/org/alfresco/web/app/servlet/command/ScriptCommandProcessor.java index ab6e2293e2..1d48b714ac 100644 --- a/source/java/org/alfresco/web/app/servlet/command/ScriptCommandProcessor.java +++ b/source/java/org/alfresco/web/app/servlet/command/ScriptCommandProcessor.java @@ -21,6 +21,7 @@ import java.util.Enumeration; import java.util.HashMap; import java.util.Map; +import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import org.alfresco.error.AlfrescoRuntimeException; @@ -31,6 +32,8 @@ import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.security.AccessStatus; import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.web.app.Application; +import org.alfresco.web.app.servlet.BaseServlet; +import org.alfresco.web.bean.repository.Repository; import org.alfresco.web.bean.repository.User; /** @@ -42,6 +45,9 @@ import org.alfresco.web.bean.repository.User; */ public final class ScriptCommandProcessor implements CommandProcessor { + private static final String ARG_SCRIPT_PATH = "scriptPath"; + private static final String ARG_CONTEXT_PATH = "contextPath"; + private NodeRef scriptRef; private NodeRef docRef; private Object result; @@ -52,29 +58,47 @@ public final class ScriptCommandProcessor implements CommandProcessor CommandFactory.getInstance().registerCommand("execute", ExecuteScriptCommand.class); } + /** - * @see org.alfresco.web.app.servlet.command.CommandProcessor#validateArguments(org.alfresco.service.ServiceRegistry, java.lang.String, java.lang.String[]) + * @see org.alfresco.web.app.servlet.command.CommandProcessor#validateArguments(javax.servlet.ServletContext, java.lang.String, java.util.Map, java.lang.String[]) */ - public boolean validateArguments(ServiceRegistry serviceRegistry, String command, String[] args) + public boolean validateArguments(ServletContext sc, String command, Map args, String[] urlElements) { - if (args.length < 3) + boolean allowed = false; + String scriptPath = args.get(ARG_SCRIPT_PATH); + if (scriptPath != null) { - throw new IllegalArgumentException("Not enough URL arguments passed to command servlet."); + // resolve path to a node + this.scriptRef = BaseServlet.resolveNamePath(sc, scriptPath).NodeRef; + + // same for the document context path if specified + String docPath = args.get(ARG_CONTEXT_PATH); + if (docPath != null) + { + this.docRef = BaseServlet.resolveNamePath(sc, docPath).NodeRef; + } } - - // get NodeRef to the node script to execute - StoreRef storeRef = new StoreRef(args[0], args[1]); - this.scriptRef = new NodeRef(storeRef, args[2]); - - if (args.length >= 6) + else { - storeRef = new StoreRef(args[3], args[4]); - this.docRef = new NodeRef(storeRef, args[5]); + if (urlElements.length < 3) + { + throw new IllegalArgumentException("Not enough URL arguments passed to command servlet."); + } + + // get NodeRef to the node script to execute + StoreRef storeRef = new StoreRef(urlElements[0], urlElements[1]); + this.scriptRef = new NodeRef(storeRef, urlElements[2]); + + if (urlElements.length >= 6) + { + storeRef = new StoreRef(urlElements[3], urlElements[4]); + this.docRef = new NodeRef(storeRef, urlElements[5]); + } } // check we can access the nodes specified - PermissionService ps = serviceRegistry.getPermissionService(); - boolean allowed = (ps.hasPermission(this.scriptRef, PermissionService.READ) == AccessStatus.ALLOWED); + PermissionService ps = Repository.getServiceRegistry(sc).getPermissionService(); + allowed = (ps.hasPermission(this.scriptRef, PermissionService.READ) == AccessStatus.ALLOWED); if (this.docRef != null) { allowed &= (ps.hasPermission(this.docRef, PermissionService.READ) == AccessStatus.ALLOWED); diff --git a/source/java/org/alfresco/web/app/servlet/command/WorkflowCommandProcessor.java b/source/java/org/alfresco/web/app/servlet/command/WorkflowCommandProcessor.java index 8ec1110c3b..d7f14edbf0 100644 --- a/source/java/org/alfresco/web/app/servlet/command/WorkflowCommandProcessor.java +++ b/source/java/org/alfresco/web/app/servlet/command/WorkflowCommandProcessor.java @@ -21,7 +21,6 @@ import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpSession; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.service.ServiceRegistry;