diff --git a/config/alfresco/messages/webclient.properties b/config/alfresco/messages/webclient.properties index dd2c2b6c5c..05c97b8142 100644 --- a/config/alfresco/messages/webclient.properties +++ b/config/alfresco/messages/webclient.properties @@ -868,6 +868,7 @@ title_my_alfresco=My Alfresco dashboard_info=My Alfresco Dashboard dashboard_description=Configure this view and build your personal Alfresco dashboard configure=Configure +error_dashboard=An error occurred in one of the dashlets. # My Alfresco Layout Manager wizard messages configure_dashboard_title=Configure Dashboard Wizard @@ -945,6 +946,9 @@ manage_workitem_desc=This dialog allows the work item to be managed workitem_properties=Work Item Properties id=Id status=Status +completed=Completed +source=Source +priority=Priority my_workitems_todo_title=My Work Items To Do my_workitems_todo_desc=List of your workflow items still to complete my_workitems_completed_title=My Completed Work Items @@ -953,7 +957,7 @@ due_date=Due Date completed_on=Completed on outcome=Outcome reassign=Reassign - +cancel_workflow=Cancel Workflow # Admin Console messages title_admin_console=Administration Console diff --git a/config/alfresco/web-client-config-workflow.xml b/config/alfresco/web-client-config-workflow.xml index 6234f356fa..f6e3d58e64 100644 --- a/config/alfresco/web-client-config-workflow.xml +++ b/config/alfresco/web-client-config-workflow.xml @@ -15,10 +15,21 @@ + + + + + + + + + + + - + @@ -43,10 +54,25 @@ reassign - /images/icons/create_forum.gif + /images/icons/recover.gif null + + cancel_workflow + /images/icons/reject.gif + null + + + + remove + /images/icons/delete.gif + #{DialogManager.bean.removePackageItem} + + #{actionContext.id} + + + @@ -55,9 +81,32 @@ - + + + + + + + + + + + + + + + + + + + + + + + + @@ -103,7 +152,7 @@ + actions-config-id="manage_workitem_actions" /> diff --git a/source/java/org/alfresco/web/bean/dialog/BaseDialogBean.java b/source/java/org/alfresco/web/bean/dialog/BaseDialogBean.java index 45c1c906a4..fa3b203b6f 100644 --- a/source/java/org/alfresco/web/bean/dialog/BaseDialogBean.java +++ b/source/java/org/alfresco/web/bean/dialog/BaseDialogBean.java @@ -97,7 +97,7 @@ public abstract class BaseDialogBean implements IDialogBean // rollback the transaction try { if (tx != null) {tx.rollback();} } catch (Exception ex) {} - Utils.addErrorMessage(formatErrorMessage(e)); + Utils.addErrorMessage(formatErrorMessage(e), e); outcome = getErrorOutcome(e); } } diff --git a/source/java/org/alfresco/web/bean/workflow/ManageWorkItemDialog.java b/source/java/org/alfresco/web/bean/workflow/ManageWorkItemDialog.java index 33d26071b7..d9c88b7ee7 100644 --- a/source/java/org/alfresco/web/bean/workflow/ManageWorkItemDialog.java +++ b/source/java/org/alfresco/web/bean/workflow/ManageWorkItemDialog.java @@ -8,6 +8,7 @@ import java.util.List; import java.util.Map; import javax.faces.context.FacesContext; +import javax.faces.event.ActionEvent; import javax.transaction.UserTransaction; import org.alfresco.model.ContentModel; @@ -25,10 +26,13 @@ import org.alfresco.web.app.Application; import org.alfresco.web.bean.dialog.BaseDialogBean; import org.alfresco.web.bean.repository.MapNode; import org.alfresco.web.bean.repository.Node; +import org.alfresco.web.bean.repository.NodePropertyResolver; import org.alfresco.web.bean.repository.Repository; import org.alfresco.web.bean.repository.TransientNode; import org.alfresco.web.config.DialogsConfigElement.DialogButtonConfig; import org.alfresco.web.ui.common.Utils; +import org.alfresco.web.ui.common.component.UIActionLink; +import org.alfresco.web.ui.common.component.data.UIRichList; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -44,6 +48,8 @@ public class ManageWorkItemDialog extends BaseDialogBean protected WorkflowTask workItem; protected WorkflowTransition[] transitions; protected List resources; + protected WorkItemCompleteResolver completeResolver = new WorkItemCompleteResolver(); + protected UIRichList packageItemsRichList; protected static final String ID_PREFIX = "transition_"; protected static final String CLIENT_ID_PREFIX = "dialog:" + ID_PREFIX; @@ -179,22 +185,124 @@ public class ManageWorkItemDialog extends BaseDialogBean } catch (Throwable e) { - // reset the flag so we can re-attempt the operation - isFinished = false; - // rollback the transaction try { if (tx != null) {tx.rollback();} } catch (Exception ex) {} - Utils.addErrorMessage(formatErrorMessage(e)); + Utils.addErrorMessage(formatErrorMessage(e), e); outcome = this.getErrorOutcome(e); } } return outcome; } + + /** + * Removes an item from the workflow package + * + * @param event The event containing a reference to the item to remove + */ + public void removePackageItem(ActionEvent event) + { + logger.info("remove package item: " + event); + } + + /** + * Toggles the complete flag for a workflow package item + * + * @param event The event containing a reference to the item to toggle the status for + */ + public void togglePackageItemComplete(ActionEvent event) + { + UserTransaction tx = null; + try + { + FacesContext context = FacesContext.getCurrentInstance(); + tx = Repository.getUserTransaction(context); + tx.begin(); + + UIActionLink link = (UIActionLink)event.getComponent(); + Map params = link.getParameterMap(); + + // create the node ref for the item we are toggling + NodeRef nodeRef = new NodeRef(Repository.getStoreRef(), + (String)params.get("id")); + + // get the existing list of completed items + List completedItems = (List)this.workItem.properties.get( + WorkflowModel.PROP_COMPLETED_ITEMS); + + if (completedItems == null) + { + // if it doesn't exist yet create the list and add the noderef + completedItems = new ArrayList(1); + completedItems.add(nodeRef); + this.workItem.properties.put(WorkflowModel.PROP_COMPLETED_ITEMS, + (Serializable)completedItems); + } + else + { + if (completedItems.contains(nodeRef)) + { + // the item is already in the list remove it + completedItems.remove(nodeRef); + + // NOTE: There is a bug somwehere which causes the list to be + // returned as a byte array instead of a list if an empty + // list is persisted, therefore if the list is now empty + // set the completed items back to null + if (completedItems.size() == 0) + { + this.workItem.properties.put(WorkflowModel.PROP_COMPLETED_ITEMS, null); + } + } + else + { + // the noderef is not in the list yet so just add it + completedItems.add(nodeRef); + } + } + + // update the task with the updated parameters + this.workflowService.updateTask(this.workItem.id, this.workItem.properties, + null, null); + + // commit the transaction + tx.commit(); + + // reset the rich list if the change was successful + this.packageItemsRichList.setValue(null); + } + catch (Throwable err) + { + Utils.addErrorMessage(MessageFormat.format(Application.getMessage( + FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), err.getMessage()), err); + this.resources = Collections.emptyList(); + try { if (tx != null) {tx.rollback();} } catch (Exception tex) {} + } + } // ------------------------------------------------------------------------------ // Bean Getters and Setters + /** + * Sets the rich list being used for the workflow package items + * + * @param richList The rich list instance + */ + public void setPackageItemsRichList(UIRichList richList) + { + this.packageItemsRichList = richList; + } + + /** + * Returns the rich list being used for the workflow package items + * + * @return The rich list instance + */ + public UIRichList getPackageItemsRichList() + { + return this.packageItemsRichList; + } + /** * Returns the Node representing the work item * @@ -213,7 +321,17 @@ public class ManageWorkItemDialog extends BaseDialogBean */ public List getResources() { - NodeRef workflowPackage = (NodeRef)this.workItem.properties.get(WorkflowModel.ASSOC_PACKAGE); + NodeRef workflowPackage = null; + Serializable obj = this.workItem.properties.get(WorkflowModel.ASSOC_PACKAGE); + // TODO: remove this workaroud where JBPM may return a String and not the NodeRef + if (obj instanceof NodeRef) + { + workflowPackage = (NodeRef)obj; + } + else if (obj instanceof String) + { + workflowPackage = new NodeRef((String)obj); + } this.resources = new ArrayList(4); @@ -257,6 +375,13 @@ public class ManageWorkItemDialog extends BaseDialogBean MapNode node = new MapNode(nodeRef, this.nodeService, true); this.browseBean.setupCommonBindingProperties(node); + // add property resolvers to show path information + node.addPropertyResolver("path", this.browseBean.resolverPath); + node.addPropertyResolver("displayPath", this.browseBean.resolverDisplayPath); + + // add a property resolver to indicate whether the item has been completed or not + node.addPropertyResolver("completed", this.completeResolver); + this.resources.add(node); } } @@ -297,4 +422,33 @@ public class ManageWorkItemDialog extends BaseDialogBean { this.workflowService = workflowService; } + + // ------------------------------------------------------------------------------ + // Helper methods + + + // ------------------------------------------------------------------------------ + // Inner classes + + /** + * Property resolver to determine if the given node has been flagged as complete + */ + protected class WorkItemCompleteResolver implements NodePropertyResolver + { + public Object get(Node node) + { + String result = Application.getMessage(FacesContext.getCurrentInstance(), "no"); + + List completedItems = (List)workItem.properties.get( + WorkflowModel.PROP_COMPLETED_ITEMS); + + if (completedItems != null && completedItems.size() > 0 && + completedItems.contains(node.getNodeRef())) + { + result = Application.getMessage(FacesContext.getCurrentInstance(), "yes"); + } + + return result; + } + } } diff --git a/source/java/org/alfresco/web/bean/workflow/StartWorkflowWizard.java b/source/java/org/alfresco/web/bean/workflow/StartWorkflowWizard.java index b0c3fc53c9..cfe803bdef 100644 --- a/source/java/org/alfresco/web/bean/workflow/StartWorkflowWizard.java +++ b/source/java/org/alfresco/web/bean/workflow/StartWorkflowWizard.java @@ -87,12 +87,8 @@ public class StartWorkflowWizard extends BaseWizardBean QName type = this.nodeService.getType(itemToWorkflow); NodeRef workflowPackage = null; - if (this.dictionaryService.isSubClass(type, ContentModel.TYPE_FOLDER) || - this.dictionaryService.isSubClass(type, ContentModel.TYPE_FOLDERLINK)) - { - throw new UnsupportedOperationException("Workflow on a folder is not supported yet!"); - } - else + if (this.dictionaryService.isSubClass(type, ContentModel.TYPE_CONTENT) || + this.dictionaryService.isSubClass(type, ContentModel.TYPE_FILELINK)) { // create a workflow package and add the given item to workflow as a child workflowPackage = this.workflowService.createPackage(null); @@ -106,6 +102,13 @@ public class StartWorkflowWizard extends BaseWizardBean params.put(WorkflowModel.ASSOC_PACKAGE, workflowPackage); } + // setup the context for the workflow (this is the space the workflow was launched from) + Node workflowContext = this.navigator.getCurrentNode(); + if (workflowContext != null) + { + params.put(WorkflowModel.PROP_CONTEXT, (Serializable)workflowContext.getNodeRef()); + } + // start the workflow to get access to the start task WorkflowPath path = this.workflowService.startWorkflow(this.selectedWorkflow, params); if (path != null) diff --git a/source/java/org/alfresco/web/bean/workflow/WorkflowBean.java b/source/java/org/alfresco/web/bean/workflow/WorkflowBean.java index f5f487e4b6..4fc878221b 100644 --- a/source/java/org/alfresco/web/bean/workflow/WorkflowBean.java +++ b/source/java/org/alfresco/web/bean/workflow/WorkflowBean.java @@ -7,14 +7,18 @@ import java.util.List; import java.util.Map; import javax.faces.context.FacesContext; +import javax.transaction.UserTransaction; import org.alfresco.model.ContentModel; +import org.alfresco.repo.workflow.WorkflowModel; import org.alfresco.service.cmr.repository.AssociationRef; import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.workflow.WorkflowService; import org.alfresco.service.cmr.workflow.WorkflowTask; import org.alfresco.service.cmr.workflow.WorkflowTaskDefinition; import org.alfresco.service.cmr.workflow.WorkflowTaskState; +import org.alfresco.service.cmr.workflow.WorkflowTransition; import org.alfresco.service.namespace.QName; import org.alfresco.util.ISO9075; import org.alfresco.web.app.Application; @@ -22,6 +26,7 @@ import org.alfresco.web.bean.repository.Node; import org.alfresco.web.bean.repository.Repository; import org.alfresco.web.bean.repository.TransientMapNode; import org.alfresco.web.bean.repository.User; +import org.alfresco.web.ui.common.Utils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -32,6 +37,7 @@ import org.apache.commons.logging.LogFactory; */ public class WorkflowBean { + protected NodeService nodeService; protected WorkflowService workflowService; protected List workItems; protected List completedWorkItems; @@ -50,19 +56,35 @@ public class WorkflowBean public List getWorkItemsToDo() { // get the current username - FacesContext fc = FacesContext.getCurrentInstance(); - User user = Application.getCurrentUser(fc); + FacesContext context = FacesContext.getCurrentInstance(); + User user = Application.getCurrentUser(context); String userName = ISO9075.encode(user.getUserName()); - // get the current in progress tasks for the current user - List tasks = this.workflowService.getAssignedTasks( - userName, WorkflowTaskState.IN_PROGRESS); - - // create a list of transient nodes to represent - this.workItems = new ArrayList(tasks.size()); - for (WorkflowTask task : tasks) + UserTransaction tx = null; + try { - this.workItems.add(createWorkItem(task)); + tx = Repository.getUserTransaction(context, true); + tx.begin(); + + // get the current in progress tasks for the current user + List tasks = this.workflowService.getAssignedTasks( + userName, WorkflowTaskState.IN_PROGRESS); + + // create a list of transient nodes to represent + this.workItems = new ArrayList(tasks.size()); + for (WorkflowTask task : tasks) + { + this.workItems.add(createWorkItem(task)); + } + + // commit the changes + tx.commit(); + } + catch (Throwable e) + { + // rollback the transaction + try { if (tx != null) {tx.rollback();} } catch (Exception ex) {} + Utils.addErrorMessage("Failed to get to do work items: " + e.toString(), e); } return this.workItems; @@ -77,19 +99,35 @@ public class WorkflowBean public List getWorkItemsCompleted() { // get the current username - FacesContext fc = FacesContext.getCurrentInstance(); - User user = Application.getCurrentUser(fc); + FacesContext context = FacesContext.getCurrentInstance(); + User user = Application.getCurrentUser(context); String userName = ISO9075.encode(user.getUserName()); - // get the current in progress tasks for the current user - List tasks = this.workflowService.getAssignedTasks( - userName, WorkflowTaskState.COMPLETED); - - // create a list of transient nodes to represent - this.completedWorkItems = new ArrayList(tasks.size()); - for (WorkflowTask task : tasks) + UserTransaction tx = null; + try { - this.completedWorkItems.add(createWorkItem(task)); + tx = Repository.getUserTransaction(context, true); + tx.begin(); + + // get the current in progress tasks for the current user + List tasks = this.workflowService.getAssignedTasks( + userName, WorkflowTaskState.COMPLETED); + + // create a list of transient nodes to represent + this.completedWorkItems = new ArrayList(tasks.size()); + for (WorkflowTask task : tasks) + { + this.completedWorkItems.add(createWorkItem(task)); + } + + // commit the changes + tx.commit(); + } + catch (Throwable e) + { + // rollback the transaction + try { if (tx != null) {tx.rollback();} } catch (Exception ex) {} + Utils.addErrorMessage("Failed to get completed work items: " + e.toString(), e); } return this.completedWorkItems; @@ -105,6 +143,16 @@ public class WorkflowBean this.workflowService = workflowService; } + /** + * Sets the node service to use + * + * @param nodeService NodeService instance + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + // ------------------------------------------------------------------------------ // Helper methods @@ -169,6 +217,50 @@ public class WorkflowBean node.getProperties().put("type", taskDef.metadata.getTitle()); node.getProperties().put("id", task.id); + // add the name of the source space (if there is one) + // TODO: remove this workaroud where JBPM may return a String and not the NodeRef + Serializable obj = task.properties.get(WorkflowModel.PROP_CONTEXT); + NodeRef context = null; + if (obj instanceof NodeRef) + { + context = (NodeRef)obj; + } + else if (obj instanceof String) + { + context = new NodeRef((String)obj); + } + + if (context != null) + { + String name = Repository.getNameForNode(this.nodeService, context); + node.getProperties().put("sourceSpaceName", name); + node.getProperties().put("sourceSpaceId", context.getId()); + } + + // add the outcome label for any completed task + if (task.state.equals(WorkflowTaskState.COMPLETED)) + { + String outcome = null; + String transition = (String)task.properties.get(WorkflowModel.PROP_OUTCOME); + if (transition != null) + { + WorkflowTransition[] transitions = task.path.node.transitions; + for (WorkflowTransition trans : transitions) + { + if (trans.id.equals(transition)) + { + outcome = trans.title; + break; + } + } + + if (outcome != null) + { + node.getProperties().put("outcome", outcome); + } + } + } + return node; } } diff --git a/source/web/WEB-INF/faces-config-beans.xml b/source/web/WEB-INF/faces-config-beans.xml index 4f0504bcb6..e961805352 100644 --- a/source/web/WEB-INF/faces-config-beans.xml +++ b/source/web/WEB-INF/faces-config-beans.xml @@ -1785,6 +1785,10 @@ WorkflowBean org.alfresco.web.bean.workflow.WorkflowBean session + + nodeService + #{NodeService} + workflowService #{WorkflowService} diff --git a/source/web/jsp/dashboards/container.jsp b/source/web/jsp/dashboards/container.jsp index f5b8745a55..adc53b82af 100644 --- a/source/web/jsp/dashboards/container.jsp +++ b/source/web/jsp/dashboards/container.jsp @@ -100,6 +100,7 @@ + diff --git a/source/web/jsp/workflow/manage-workitem-dialog.jsp b/source/web/jsp/workflow/manage-workitem-dialog.jsp index f23dc7bfb8..77aae378b8 100644 --- a/source/web/jsp/workflow/manage-workitem-dialog.jsp +++ b/source/web/jsp/workflow/manage-workitem-dialog.jsp @@ -32,19 +32,21 @@ border="white" bgcolor="white" titleBorder="blue" titleBgcolor="#D3E6FE" styleClass="mainSubTitle"> - - <%-- Primary column for details view mode --%> - + + <%-- Name column --%> + - + - + <%-- Description column --%> @@ -60,19 +62,51 @@ - + + <%-- Created Date column --%> + + + + + + + + + + <%-- Modified Date column --%> + + + + + + + + + <%-- Actions column --%> - - <%-- actions are configured in web-client-config-actions.xml --%> - + - + <%-- Completed column --%> + <%-- + + + + + + + + + --%> - \ No newline at end of file + + <%-- Put the package actions here --%> + + diff --git a/source/web/jsp/workflow/start-workflow-wizard/workflow-options.jsp b/source/web/jsp/workflow/start-workflow-wizard/workflow-options.jsp index ef6e0942ff..145307dc11 100644 --- a/source/web/jsp/workflow/start-workflow-wizard/workflow-options.jsp +++ b/source/web/jsp/workflow/start-workflow-wizard/workflow-options.jsp @@ -37,8 +37,10 @@ - + + - + \ No newline at end of file diff --git a/source/web/jsp/workflow/workitems-completed-dashlet.jsp b/source/web/jsp/workflow/workitems-completed-dashlet.jsp index a8603f4d6b..67f437b03e 100644 --- a/source/web/jsp/workflow/workitems-completed-dashlet.jsp +++ b/source/web/jsp/workflow/workitems-completed-dashlet.jsp @@ -35,6 +35,14 @@ + <%-- Source column --%> + + + + + + + <%-- Completed date column --%> @@ -48,31 +56,19 @@ <%-- Outcome column --%> - + - + <%-- Actions column --%> - <%-- - - - - - - - - + - --%> diff --git a/source/web/jsp/workflow/workitems-todo-dashlet.jsp b/source/web/jsp/workflow/workitems-todo-dashlet.jsp index 20c6d87b07..d8b406c7df 100644 --- a/source/web/jsp/workflow/workitems-todo-dashlet.jsp +++ b/source/web/jsp/workflow/workitems-todo-dashlet.jsp @@ -43,6 +43,14 @@ + <%-- Source column --%> + + + + + + + <%-- Due date column --%> @@ -61,26 +69,22 @@ + <%-- Priority column --%> + + + + + + + <%-- Actions column --%> - <%-- - - - - - - - - + - --%>