From a9e9e57b6d6eb445044d392972f51b6378ecdc17 Mon Sep 17 00:00:00 2001 From: Kevin Roast Date: Thu, 30 Mar 2006 14:09:08 +0000 Subject: [PATCH] . Workflow Command Servlet implementation . Refactored common simple workflow code into a util class. . Example template that demonstrates both querying for workflow information on a document (useful anyway) and executes the servlet to perform both the Approve and Reject actions if appropriate for the document. . TemplateNode template API object now exposes the browse navigation servlet URL for a Space as the "url" API call . Fix long standing bug in UIRichList (since PR1!) - subtle behaviour issue where under very high server loads, the richlist component would render the wrong number of items per row. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@2590 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../alfresco/templates/workflow_details.ftl | 27 ++ .../app/servlet/TemplateContentServlet.java | 2 +- .../app/servlet/WorkflowActionServlet.java | 411 ++++++++++++++++++ .../web/bean/DocumentDetailsBean.java | 83 +--- .../org/alfresco/web/bean/WorkflowUtil.java | 149 +++++++ .../web/bean/clipboard/ClipboardBean.java | 12 +- .../ui/common/component/data/UIRichList.java | 9 +- source/web/WEB-INF/faces-config-beans.xml | 2 +- source/web/WEB-INF/web_TEMPLATE.xml | 10 + 9 files changed, 613 insertions(+), 92 deletions(-) create mode 100644 config/alfresco/templates/workflow_details.ftl create mode 100644 source/java/org/alfresco/web/app/servlet/WorkflowActionServlet.java create mode 100644 source/java/org/alfresco/web/bean/WorkflowUtil.java diff --git a/config/alfresco/templates/workflow_details.ftl b/config/alfresco/templates/workflow_details.ftl new file mode 100644 index 0000000000..b8a0fd2ebf --- /dev/null +++ b/config/alfresco/templates/workflow_details.ftl @@ -0,0 +1,27 @@ +<#if document?exists> + <#if hasAspect(document, "app:simpleworkflow") = 1> + This document has the following workflow:
+ <#if document.properties["app:approveStep"]?exists> + <#assign ref=document.nodeRef> + <#assign workspace=ref[0..ref?index_of("://")-1]> + <#assign storenode=ref[ref?index_of("://")+3..]> +   Approve Step: ${document.properties["app:approveStep"]}
+ + <#if document.properties["app:approveFolder"]?exists> +   Approve Folder: ${document.properties["app:approveFolder"].name}
+ + <#if document.properties["app:rejectStep"]?exists> + <#assign ref=document.nodeRef> + <#assign workspace=ref[0..ref?index_of("://")-1]> + <#assign storenode=ref[ref?index_of("://")+3..]> +   Reject Step: ${document.properties["app:rejectStep"]}
+ + <#if document.properties["app:rejectFolder"]?exists> +   Reject Folder: {document.properties["app:rejectFolder"].name}
+ + <#else> + This document has no workflow.
+ +<#else> + No document found! + diff --git a/source/java/org/alfresco/web/app/servlet/TemplateContentServlet.java b/source/java/org/alfresco/web/app/servlet/TemplateContentServlet.java index b64c19b0a5..296e1d3c38 100644 --- a/source/java/org/alfresco/web/app/servlet/TemplateContentServlet.java +++ b/source/java/org/alfresco/web/app/servlet/TemplateContentServlet.java @@ -103,7 +103,7 @@ public class TemplateContentServlet extends BaseServlet int tokenCount = t.countTokens(); if (tokenCount < 5) { - throw new IllegalArgumentException("Download URL did not contain all required args: " + uri); + throw new IllegalArgumentException("Template Servlet URL did not contain all required args: " + uri); } t.nextToken(); // skip web app name diff --git a/source/java/org/alfresco/web/app/servlet/WorkflowActionServlet.java b/source/java/org/alfresco/web/app/servlet/WorkflowActionServlet.java new file mode 100644 index 0000000000..4df7a9691c --- /dev/null +++ b/source/java/org/alfresco/web/app/servlet/WorkflowActionServlet.java @@ -0,0 +1,411 @@ +/* + * 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.PrintWriter; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.text.MessageFormat; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.StringTokenizer; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.transaction.UserTransaction; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.service.ServiceRegistry; +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.WorkflowUtil; +import org.alfresco.web.ui.common.Utils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Servlet responsible for executing workflow commands upon a node. + *

+ * The URL to the servlet should be generated thus: + *

/alfresco/workflow/command/workspace/SpacesStore/0000-0000-0000-0000
+ * 

+ * The 'command' identifies the workflow action to execute upon the node (e.g. "approve" or "reject"). + * The store protocol, followed by the store ID, followed by the content Node Id used to + * identify the node to execute the workflow action upon. + *

+ * A 'return-page' URL argument can be specified as the redirect page to navigate too after processing. + *

+ * Like most Alfresco servlets, the URL may be followed by a valid 'ticket' argument for authentication: + * ?ticket=1234567890 + *

+ * And/or also followed by the "?guest=true" argument to force guest access login for the URL. + * + * @author Kevin Roast + */ +public class WorkflowActionServlet extends BaseServlet +{ + private static final long serialVersionUID = -3111407921997365999L; + + private static Log logger = LogFactory.getLog(WorkflowActionServlet.class); + + private static CommandFactory commandfactory = CommandFactory.getInstance(); + + public static final String ARG_RETURNPAGE = "return-page"; + + public static final String CMD_APPROVE = "approve"; + public static final String CMD_REJECT = "reject"; + + private static final String DEFAULT_URL = "/workflow/{0}/{1}/{2}/{3}"; + private static final String RETURN_URL = "/workflow/{0}/{1}/{2}/{3}?" + ARG_RETURNPAGE + "={4}"; + + static + { + // register the available Workflow commands + commandfactory.registerCommand(CMD_APPROVE, ApproveCommand.class); + commandfactory.registerCommand(CMD_REJECT, RejectCommand.class); + } + + + /** + * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) + */ + 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()) : "")); + + AuthenticationStatus status = servletAuthenticate(req, res); + if (status == AuthenticationStatus.Failure) + { + return; + } + + StringTokenizer t = new StringTokenizer(uri, "/"); + int tokenCount = t.countTokens(); + if (tokenCount < 5) + { + throw new IllegalArgumentException("Workflow Servlet URL did not contain all required args: " + uri); + } + + t.nextToken(); // skip web app name + t.nextToken(); // skip servlet name + + // get the command to perform e.g. "approve" + String command = t.nextToken(); + + // get NodeRef to the node with the workflow attached to it + StoreRef storeRef = new StoreRef(t.nextToken(), t.nextToken()); + NodeRef nodeRef = new NodeRef(storeRef, t.nextToken()); + + // get the services we need to execute the workflow command + ServiceRegistry serviceRegistry = getServiceRegistry(getServletContext()); + PermissionService permissionService = serviceRegistry.getPermissionService(); + + // check that the user has at least READ access on the node - else redirect to the login page + if (permissionService.hasPermission(nodeRef, PermissionService.READ) == AccessStatus.DENIED) + { + redirectToLoginPage(req, res, getServletContext()); + return; + } + + try + { + UserTransaction txn = null; + try + { + txn = serviceRegistry.getTransactionService().getUserTransaction(); + txn.begin(); + + // find the workflow command from the registered list of commands + // the CommandFactory supplies use with an instance of the command to use + Map properties = new HashMap(1, 1.0f); + properties.put("target", nodeRef); + Command cmd = commandfactory.createCommand(command, properties); + if (cmd == null) + { + throw new AlfrescoRuntimeException("Unknown workflow command specified: " + command); + } + cmd.execute(serviceRegistry); + + // commit the transaction + txn.commit(); + } + catch (Throwable txnErr) + { + try { if (txn != null) {txn.rollback();} } catch (Exception tex) {} + throw txnErr; + } + } + catch (Throwable err) + { + // TODO: could show error status output here instead of throwing an exception? + throw new AlfrescoRuntimeException("Error during workflow servlet processing: " + err.getMessage(), err); + } + + String returnPage = req.getParameter(ARG_RETURNPAGE); + if (returnPage != null && returnPage.length() != 0) + { + if (logger.isDebugEnabled()) + logger.debug("Redirecting to specified return page: " + returnPage); + + res.sendRedirect(returnPage); + } + else + { + if (logger.isDebugEnabled()) + logger.debug("No return page specified, displaying status output."); + + res.setContentType("text/html"); + PrintWriter out = res.getWriter(); + out.print("Workflow command: '"); + out.print(command); + out.print("' executed against node: "); + out.println(nodeRef.toString()); + out.close(); + } + } + + /** + * Helper to generate a URL to process a workflow action against a node. + *

+ * The result of the workflow action is supplied returned as the response. + * + * @param nodeRef NodeRef of the node to generate URL for (cannot be null) + * @param action Workflow action (See constants) to execute on the node + * + * @return URL to process the workflow action + */ + public final static String generateURL(NodeRef nodeRef, String action) + { + return MessageFormat.format(DEFAULT_URL, new Object[] { + action, + nodeRef.getStoreRef().getProtocol(), + nodeRef.getStoreRef().getIdentifier(), + nodeRef.getId() } ); + } + + /** + * Helper to generate a URL to process a workflow action against a node. + *

+ * The result of the workflow action is supplied returned as the response. + * + * @param nodeRef NodeRef of the node to generate URL for (cannot be null) + * @param action Workflow action (See constants) to execute on the node + * @param returnPage Return page URL to redirect after success (e.g. /alfresco/navigate/browse) + * + * @return URL to process the workflow action + */ + public final static String generateURL(NodeRef nodeRef, String action, String returnPage) + { + try + { + return MessageFormat.format(RETURN_URL, new Object[] { + action, + nodeRef.getStoreRef().getProtocol(), + nodeRef.getStoreRef().getIdentifier(), + nodeRef.getId(), + Utils.replace(URLEncoder.encode(returnPage, "UTF-8"), "+", "%20")} ); + } + catch (UnsupportedEncodingException uee) + { + throw new AlfrescoRuntimeException("Failed to encode workflow URL for node: " + nodeRef, uee); + } + } + + + /** + * Simple command pattern interface + */ + interface Command + { + /** + * Execute the command + * + * @param serviceRegistry The ServiceRegistry instance + */ + void execute(ServiceRegistry serviceRegistry); + + /** + * @param properties bag of named properties for the command + */ + void setProperties(Map properties); + } + + + /** + * Base command class + */ + static abstract class BaseCommand implements Command + { + Map properties = Collections.emptyMap(); + + /** + * @see org.alfresco.web.app.servlet.WorkflowActionServlet.Command#setProperties(java.util.Map) + */ + public void setProperties(Map properties) + { + if (properties != null) + { + this.properties = properties; + } + } + } + + + /** + * Approve Workflow command implementation + */ + static final class ApproveCommand extends BaseCommand + { + static final String PROP_TARGET = "target"; + + /** + * @see org.alfresco.web.app.servlet.WorkflowActionServlet.Command#execute(org.alfresco.service.ServiceRegistry) + */ + public void execute(ServiceRegistry serviceRegistry) + { + NodeRef nodeRef = (NodeRef)this.properties.get(PROP_TARGET); + if (nodeRef == null) + { + throw new IllegalArgumentException( + "Unable to execute ApproveCommand - mandatory parameter not supplied: " + PROP_TARGET); + } + + WorkflowUtil.approve(nodeRef, serviceRegistry.getNodeService(), serviceRegistry.getCopyService()); + } + } + + + /** + * Reject Workflow command implementation + */ + static final class RejectCommand extends BaseCommand + { + static final String PROP_TARGET = "target"; + + /** + * @see org.alfresco.web.app.servlet.WorkflowActionServlet.Command#execute(org.alfresco.service.ServiceRegistry) + */ + public void execute(ServiceRegistry serviceRegistry) + { + NodeRef nodeRef = (NodeRef)this.properties.get(PROP_TARGET); + if (nodeRef == null) + { + throw new IllegalArgumentException( + "Unable to execute RejectCommand - mandatory parameter not supplied: " + PROP_TARGET); + } + + WorkflowUtil.reject(nodeRef, serviceRegistry.getNodeService(), serviceRegistry.getCopyService()); + } + } + + + /** + * Command Factory helper + */ + static final class CommandFactory + { + private static CommandFactory instance = new CommandFactory(); + + private static Map registry = new HashMap(4, 1.0f); + + /** + * Private constructor - protect the singleton instance + */ + private CommandFactory() + { + } + + /** + * @return the singleton CommandFactory instance + */ + static CommandFactory getInstance() + { + return instance; + } + + /** + * Register a command name against an implementation + * + * @param name Unique name of the command + * @param clazz Class implementation of the command + */ + void registerCommand(String name, Class clazz) + { + registry.put(name, clazz); + } + + /** + * Create a command instance of the specified command name + * + * @param name Name of the command to create (must be registered) + * + * @return the Command instance or null if not found + */ + Command createCommand(String name) + { + return createCommand(name, null); + } + + /** + * Create a command instance of the specified command name + * + * @param name Name of the command to create (must be registered) + * @param properties Bag of name/value properties to pass to command upon creation + * + * @return the Command instance or null if not found + */ + Command createCommand(String name, Map properties) + { + Command result = null; + + // lookup command by name in the registry + Class clazz = registry.get(name); + if (clazz != null) + { + try + { + Object obj = clazz.newInstance(); + if (obj instanceof Command) + { + result = (Command)obj; + if (properties != null) + { + result.setProperties(properties); + } + } + } + catch (Throwable err) + { + // return default if this occurs + logger.warn("Unable to create workflow command instance '" + name + + "' with classname '" + clazz.getName() + "' due to error: " + err.getMessage()); + } + } + + return result; + } + } +} diff --git a/source/java/org/alfresco/web/bean/DocumentDetailsBean.java b/source/java/org/alfresco/web/bean/DocumentDetailsBean.java index 604d9ccd09..4f82622101 100644 --- a/source/java/org/alfresco/web/bean/DocumentDetailsBean.java +++ b/source/java/org/alfresco/web/bean/DocumentDetailsBean.java @@ -40,7 +40,6 @@ import org.alfresco.service.cmr.repository.TemplateNode; import org.alfresco.service.cmr.version.Version; import org.alfresco.service.cmr.version.VersionHistory; import org.alfresco.service.cmr.version.VersionService; -import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.web.app.Application; import org.alfresco.web.app.context.UIContextService; @@ -662,18 +661,6 @@ public class DocumentDetailsBean extends BaseDetailsBean } NodeRef docNodeRef = new NodeRef(Repository.getStoreRef(), id); - Node docNode = new Node(docNodeRef); - - if (docNode.hasAspect(ContentModel.ASPECT_SIMPLE_WORKFLOW) == false) - { - throw new AlfrescoRuntimeException("You can not approve a document that is not part of a workflow"); - } - - // get the simple workflow aspect properties - Map props = docNode.getProperties(); - - Boolean approveMove = (Boolean)props.get(ContentModel.PROP_APPROVE_MOVE.toString()); - NodeRef approveFolder = (NodeRef)props.get(ContentModel.PROP_APPROVE_FOLDER.toString()); UserTransaction tx = null; try @@ -681,23 +668,8 @@ public class DocumentDetailsBean extends BaseDetailsBean tx = Repository.getUserTransaction(FacesContext.getCurrentInstance()); tx.begin(); - // first we need to take off the simpleworkflow aspect - this.nodeService.removeAspect(docNodeRef, ContentModel.ASPECT_SIMPLE_WORKFLOW); - - if (approveMove.booleanValue()) - { - // move the document to the specified folder - String qname = QName.createValidLocalName(docNode.getName()); - this.nodeService.moveNode(docNodeRef, approveFolder, ContentModel.ASSOC_CONTAINS, - QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, qname)); - } - else - { - // copy the document to the specified folder - String qname = QName.createValidLocalName(docNode.getName()); - this.copyService.copy(docNodeRef, approveFolder, ContentModel.ASSOC_CONTAINS, - QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, qname)); - } + // call the service to perform the approve + WorkflowUtil.approve(docNodeRef, this.nodeService, this.copyService); // commit the transaction tx.commit(); @@ -710,13 +682,6 @@ public class DocumentDetailsBean extends BaseDetailsBean // also make sure the UI will get refreshed UIContextService.getInstance(FacesContext.getCurrentInstance()).notifyBeans(); - - if (logger.isDebugEnabled()) - { - String movedCopied = approveMove ? "moved" : "copied"; - logger.debug("Document has been approved and " + movedCopied + " to folder with id of " + - approveFolder.getId()); - } } catch (Throwable e) { @@ -761,24 +726,6 @@ public class DocumentDetailsBean extends BaseDetailsBean } NodeRef docNodeRef = new NodeRef(Repository.getStoreRef(), id); - Node docNode = new Node(docNodeRef); - - if (docNode.hasAspect(ContentModel.ASPECT_SIMPLE_WORKFLOW) == false) - { - throw new AlfrescoRuntimeException("You can not reject a document that is not part of a workflow"); - } - - // get the simple workflow aspect properties - Map props = docNode.getProperties(); - - String rejectStep = (String)props.get(ContentModel.PROP_REJECT_STEP.toString()); - Boolean rejectMove = (Boolean)props.get(ContentModel.PROP_REJECT_MOVE.toString()); - NodeRef rejectFolder = (NodeRef)props.get(ContentModel.PROP_REJECT_FOLDER.toString()); - - if (rejectStep == null && rejectMove == null && rejectFolder == null) - { - throw new AlfrescoRuntimeException("The workflow does not have a reject step defined"); - } UserTransaction tx = null; try @@ -786,23 +733,8 @@ public class DocumentDetailsBean extends BaseDetailsBean tx = Repository.getUserTransaction(FacesContext.getCurrentInstance()); tx.begin(); - // first we need to take off the simpleworkflow aspect - this.nodeService.removeAspect(docNodeRef, ContentModel.ASPECT_SIMPLE_WORKFLOW); - - if (rejectMove.booleanValue()) - { - // move the document to the specified folder - String qname = QName.createValidLocalName(docNode.getName()); - this.nodeService.moveNode(docNodeRef, rejectFolder, ContentModel.ASSOC_CONTAINS, - QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, qname)); - } - else - { - // copy the document to the specified folder - String qname = QName.createValidLocalName(docNode.getName()); - this.copyService.copy(docNodeRef, rejectFolder, ContentModel.ASSOC_CONTAINS, - QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, qname)); - } + // call the service to perform the reject + WorkflowUtil.reject(docNodeRef, this.nodeService, this.copyService); // commit the transaction tx.commit(); @@ -815,13 +747,6 @@ public class DocumentDetailsBean extends BaseDetailsBean // also make sure the UI will get refreshed UIContextService.getInstance(FacesContext.getCurrentInstance()).notifyBeans(); - - if (logger.isDebugEnabled()) - { - String movedCopied = rejectMove ? "moved" : "copied"; - logger.debug("Document has been rejected and " + movedCopied + " to folder with id of " + - rejectFolder.getId()); - } } catch (Throwable e) { diff --git a/source/java/org/alfresco/web/bean/WorkflowUtil.java b/source/java/org/alfresco/web/bean/WorkflowUtil.java new file mode 100644 index 0000000000..86e0cc2e92 --- /dev/null +++ b/source/java/org/alfresco/web/bean/WorkflowUtil.java @@ -0,0 +1,149 @@ +/* + * 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.bean; + +import java.util.Map; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.service.cmr.repository.CopyService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.web.bean.repository.Node; +import org.apache.log4j.Logger; + +/** + * Helper class for common Simple Workflow functionality. + *

+ * This class should be replaced with calls to a WorkflowService once it is available. + * + * @author Kevin Roast + */ +public class WorkflowUtil +{ + private static Logger logger = Logger.getLogger(WorkflowUtil.class); + + /** + * Execute the Approve step for the Simple Workflow on a node. + * + * @param ref NodeRef to the node with the workflow + * @param nodeService NodeService instance + * @param copyService CopyService instance + * + * @throws AlfrescoRuntimeException + */ + public static void approve(NodeRef ref, NodeService nodeService, CopyService copyService) + throws AlfrescoRuntimeException + { + Node docNode = new Node(ref); + + if (docNode.hasAspect(ContentModel.ASPECT_SIMPLE_WORKFLOW) == false) + { + throw new AlfrescoRuntimeException("Cannot approve a document that is not part of a workflow."); + } + + // get the simple workflow aspect properties + Map props = docNode.getProperties(); + + Boolean approveMove = (Boolean)props.get(ContentModel.PROP_APPROVE_MOVE.toString()); + NodeRef approveFolder = (NodeRef)props.get(ContentModel.PROP_APPROVE_FOLDER.toString()); + + // first we need to take off the simpleworkflow aspect + nodeService.removeAspect(ref, ContentModel.ASPECT_SIMPLE_WORKFLOW); + + if (approveMove.booleanValue()) + { + // move the document to the specified folder + String qname = QName.createValidLocalName(docNode.getName()); + nodeService.moveNode(ref, approveFolder, ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, qname)); + } + else + { + // copy the document to the specified folder + String qname = QName.createValidLocalName(docNode.getName()); + copyService.copy(ref, approveFolder, ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, qname)); + } + + if (logger.isDebugEnabled()) + { + String movedCopied = approveMove ? "moved" : "copied"; + logger.debug("Document has been approved and " + movedCopied + " to folder with id of " + + approveFolder.getId()); + } + } + + /** + * Execute the Reject step for the Simple Workflow on a node. + * + * @param ref NodeRef to the node with the workflow + * @param nodeService NodeService instance + * @param copyService CopyService instance + * + * @throws AlfrescoRuntimeException + */ + public static void reject(NodeRef ref, NodeService nodeService, CopyService copyService) + throws AlfrescoRuntimeException + { + Node docNode = new Node(ref); + + if (docNode.hasAspect(ContentModel.ASPECT_SIMPLE_WORKFLOW) == false) + { + throw new AlfrescoRuntimeException("Cannot reject a document that is not part of a workflow."); + } + + // get the simple workflow aspect properties + Map props = docNode.getProperties(); + + String rejectStep = (String)props.get(ContentModel.PROP_REJECT_STEP.toString()); + Boolean rejectMove = (Boolean)props.get(ContentModel.PROP_REJECT_MOVE.toString()); + NodeRef rejectFolder = (NodeRef)props.get(ContentModel.PROP_REJECT_FOLDER.toString()); + + if (rejectStep == null && rejectMove == null && rejectFolder == null) + { + throw new AlfrescoRuntimeException("The workflow does not have a reject step defined,"); + } + + // first we need to take off the simpleworkflow aspect + nodeService.removeAspect(ref, ContentModel.ASPECT_SIMPLE_WORKFLOW); + + if (rejectMove.booleanValue()) + { + // move the document to the specified folder + String qname = QName.createValidLocalName(docNode.getName()); + nodeService.moveNode(ref, rejectFolder, ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, qname)); + } + else + { + // copy the document to the specified folder + String qname = QName.createValidLocalName(docNode.getName()); + copyService.copy(ref, rejectFolder, ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, qname)); + } + + if (logger.isDebugEnabled()) + { + String movedCopied = rejectMove ? "moved" : "copied"; + logger.debug("Document has been rejected and " + movedCopied + " to folder with id of " + + rejectFolder.getId()); + } + } +} diff --git a/source/java/org/alfresco/web/bean/clipboard/ClipboardBean.java b/source/java/org/alfresco/web/bean/clipboard/ClipboardBean.java index 54c505e206..fe5bbe4163 100644 --- a/source/java/org/alfresco/web/bean/clipboard/ClipboardBean.java +++ b/source/java/org/alfresco/web/bean/clipboard/ClipboardBean.java @@ -73,11 +73,11 @@ public class ClipboardBean } /** - * @param nodeOperationsService The NodeOperationsService to set. + * @param copyService The CopyService to set. */ - public void setNodeOperationsService(CopyService nodeOperationsService) + public void setCopyService(CopyService copyService) { - this.nodeOperationsService = nodeOperationsService; + this.copyService = copyService; } /** @@ -316,7 +316,7 @@ public class ClipboardBean } else { - this.nodeOperationsService.copy( + this.copyService.copy( item.Node.getNodeRef(), destRef, ContentModel.ASSOC_CONTAINS, @@ -415,8 +415,8 @@ public class ClipboardBean /** The FileFolderService to be used by the bean */ protected FileFolderService fileFolderService; - /** The NodeOperationsService to be used by the bean */ - protected CopyService nodeOperationsService; + /** The CopyService to be used by the bean */ + protected CopyService copyService; /** The NavigationBean reference */ protected NavigationBean navigator; diff --git a/source/java/org/alfresco/web/ui/common/component/data/UIRichList.java b/source/java/org/alfresco/web/ui/common/component/data/UIRichList.java index 1559e8ac61..e75c924781 100644 --- a/source/java/org/alfresco/web/ui/common/component/data/UIRichList.java +++ b/source/java/org/alfresco/web/ui/common/component/data/UIRichList.java @@ -62,11 +62,10 @@ public class UIRichList extends UIComponentBase implements IDataContainer { Class clazz = Class.forName(view); IRichListRenderer renderer = (IRichListRenderer)clazz.newInstance(); - UIRichList.viewRenderers.put(renderer.getViewModeID(), renderer); + viewRenderers.put(renderer.getViewModeID(), renderer); if (logger.isDebugEnabled()) - logger.debug("Added view '" + renderer.getViewModeID() + - "' to UIRichList"); + logger.debug("Added view '" + renderer.getViewModeID() + "' to UIRichList"); } catch (Exception e) { @@ -449,7 +448,7 @@ public class UIRichList extends UIComponentBase implements IDataContainer IRichListRenderer renderer = null; if (getViewMode() != null) { - renderer = (IRichListRenderer)UIRichList.viewRenderers.get(getViewMode()); + renderer = (IRichListRenderer)viewRenderers.get(getViewMode()); } return renderer; } @@ -509,7 +508,7 @@ public class UIRichList extends UIComponentBase implements IDataContainer // Private data /** map of available IRichListRenderer instances */ - private final static Map viewRenderers = new HashMap(5); + private final Map viewRenderers = new HashMap(4, 1.0f); // component state private int currentPage = 0; diff --git a/source/web/WEB-INF/faces-config-beans.xml b/source/web/WEB-INF/faces-config-beans.xml index 2f69f31c4e..5f87bd708a 100644 --- a/source/web/WEB-INF/faces-config-beans.xml +++ b/source/web/WEB-INF/faces-config-beans.xml @@ -162,7 +162,7 @@ #{FileFolderService} - nodeOperationsService + copyService #{CopyService} diff --git a/source/web/WEB-INF/web_TEMPLATE.xml b/source/web/WEB-INF/web_TEMPLATE.xml index 44377ac56f..9c25b5f0c9 100644 --- a/source/web/WEB-INF/web_TEMPLATE.xml +++ b/source/web/WEB-INF/web_TEMPLATE.xml @@ -150,6 +150,11 @@ org.alfresco.web.app.servlet.TemplateContentServlet + + workflowAction + org.alfresco.web.app.servlet.WorkflowActionServlet + + axis org.apache.axis.transport.http.AxisServlet @@ -195,6 +200,11 @@ /template/* + + workflowAction + /workflow/* + + axis /api/*