org.alfresco.web.bean.workflow.StartWorkflowWizard.java Source code

Java tutorial

Introduction

Here is the source code for org.alfresco.web.bean.workflow.StartWorkflowWizard.java

Source

/*
 * #%L
 * Alfresco Repository WAR Community
 * %%
 * Copyright (C) 2005 - 2016 Alfresco Software Limited
 * %%
 * This file is part of the Alfresco software. 
 * If the software was purchased under a paid Alfresco license, the terms of 
 * the paid license agreement will prevail.  Otherwise, the software is 
 * provided under the following open source license terms:
 * 
 * Alfresco is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Alfresco is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 * #L%
 */
package org.alfresco.web.bean.workflow;

import java.io.Serializable;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.StringTokenizer;

import javax.faces.context.FacesContext;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.ActionEvent;
import javax.faces.model.SelectItem;
import javax.transaction.UserTransaction;

import org.alfresco.model.ApplicationModel;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.policy.BehaviourFilter;
import org.alfresco.repo.publishing.PublishingEventHelper;
import org.alfresco.repo.workflow.WorkflowModel;
import org.alfresco.repo.workflow.activiti.ActivitiConstants;
import org.alfresco.repo.workflow.jbpm.JBPMEngine;
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
import org.alfresco.service.cmr.invitation.InvitationService;
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.workflow.WorkflowDefinition;
import org.alfresco.service.cmr.workflow.WorkflowPath;
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.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.web.app.Application;
import org.alfresco.web.app.servlet.FacesHelper;
import org.alfresco.web.bean.repository.MapNode;
import org.alfresco.web.bean.repository.Node;
import org.alfresco.web.bean.repository.Repository;
import org.alfresco.web.bean.repository.TransientNode;
import org.alfresco.web.bean.wizard.BaseWizardBean;
import org.alfresco.web.data.IDataContainer;
import org.alfresco.web.data.QuickSort;
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;
import org.springframework.extensions.config.ConfigElement;

/**
 * Bean implementation for the Start Workflow Wizard.
 * 
 * @author gavinc
 */
public class StartWorkflowWizard extends BaseWizardBean {
    private static final long serialVersionUID = -4370844066621902880L;

    protected String selectedWorkflow;
    protected String previouslySelectedWorkflow;

    transient protected List<SelectItem> availableWorkflows;
    transient private Map<String, WorkflowDefinition> workflows;

    protected List<String> excludedWorkflows;
    protected List<String> invitationWorkflows;
    protected List<String> publishingWorkflows;

    transient private WorkflowService workflowService;
    transient private InvitationService invitationService;
    transient private BehaviourFilter policyBehaviourFilter;

    protected Node startTaskNode;
    protected List<Node> resources;
    protected List<String> packageItemsToAdd;
    protected UIRichList packageItemsRichList;
    protected String[] itemsToAdd;
    protected boolean isItemBeingAdded = false;
    protected boolean nextButtonDisabled = false;

    transient private NodeService unprotectedNodeService;

    private static final Log logger = LogFactory.getLog(StartWorkflowWizard.class);

    public void setUnprotectedNodeService(NodeService unprotectedNodeService) {
        this.unprotectedNodeService = unprotectedNodeService;
    }

    protected NodeService getUnprotectedNodeService() {
        if (this.unprotectedNodeService == null) {
            this.unprotectedNodeService = (NodeService) FacesHelper
                    .getManagedBean(FacesContext.getCurrentInstance(), "nodeService");
        }
        return this.unprotectedNodeService;
    }

    public void setPolicyBehaviourFilter(BehaviourFilter policyBehaviourFilter) {
        this.policyBehaviourFilter = policyBehaviourFilter;
    }

    protected BehaviourFilter getBehaviourFilter() {
        if (this.policyBehaviourFilter == null) {
            this.policyBehaviourFilter = (BehaviourFilter) FacesHelper
                    .getManagedBean(FacesContext.getCurrentInstance(), "policyBehaviourFilter");
        }
        return this.policyBehaviourFilter;
    }

    protected Map<String, WorkflowDefinition> getWorkflows() {
        if (this.workflows == null) {
            initializeWorkflows();
        }
        return this.workflows;
    }

    // ------------------------------------------------------------------------------
    // Wizard implementation

    @Override
    public void init(Map<String, String> parameters) {
        super.init(parameters);

        // reset the selected workflow
        if (this.availableWorkflows != null && this.availableWorkflows.size() > 0) {
            this.selectedWorkflow = (String) this.availableWorkflows.get(0).getValue();
        } else {
            this.selectedWorkflow = null;
        }

        this.previouslySelectedWorkflow = null;
        this.startTaskNode = null;
        this.resources = null;
        this.itemsToAdd = null;
        this.packageItemsToAdd = new ArrayList<String>();
        this.isItemBeingAdded = false;
        resetRichList();

        // add the item the workflow wizard was started on to the list of resources
        String itemToWorkflowId = this.parameters.get("item-to-workflow");
        try {
            if (itemToWorkflowId != null && itemToWorkflowId.length() > 0) {
                // create the node ref for the item and determine its type
                NodeRef itemToWorkflow = new NodeRef(Repository.getStoreRef(), itemToWorkflowId);
                QName type = this.getNodeService().getType(itemToWorkflow);

                if (this.getDictionaryService().isSubClass(type, ContentModel.TYPE_CONTENT)
                        || this.getDictionaryService().isSubClass(type, ApplicationModel.TYPE_FILELINK)) {
                    this.packageItemsToAdd.add(itemToWorkflow.toString());
                }
            }
        } catch (InvalidNodeRefException refErr) {
            Utils.addErrorMessage(MessageFormat.format(
                    Application.getMessage(FacesContext.getCurrentInstance(), Repository.ERROR_NODEREF),
                    new Object[] { itemToWorkflowId }));
            throw new AbortProcessingException("Invalid node reference");
        }
    }

    @Override
    public void restored() {
        // reset the workflow package rich list so everything gets re-evaluated
        resetRichList();
    }

    @Override
    protected String finishImpl(FacesContext context, String outcome) throws Exception {
        // TODO: Deal with workflows that don't require any data

        if (logger.isDebugEnabled())
            logger.debug("Starting workflow: " + this.selectedWorkflow);

        // prepare the parameters from the current state of the property sheet
        Map<QName, Serializable> params = WorkflowUtil.prepareTaskParams(this.startTaskNode);

        if (logger.isDebugEnabled())
            logger.debug("Starting workflow with parameters: " + params);

        // create a workflow package for the attached items and add them
        NodeRef workflowPackage = this.getWorkflowService().createPackage(null);
        params.put(WorkflowModel.ASSOC_PACKAGE, workflowPackage);

        for (String addedItem : this.packageItemsToAdd) {
            NodeRef addedNodeRef = new NodeRef(addedItem);
            //MNT-11522. fix bug. add behavior filter for add items in workflow package.
            try {
                this.getBehaviourFilter().disableBehaviour(addedNodeRef, ContentModel.ASPECT_AUDITABLE);
                this.getUnprotectedNodeService().addChild(workflowPackage, addedNodeRef,
                        WorkflowModel.ASSOC_PACKAGE_CONTAINS,
                        QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, QName.createValidLocalName(
                                (String) this.getNodeService().getProperty(addedNodeRef, ContentModel.PROP_NAME))));
            } finally {
                this.getBehaviourFilter().enableBehaviour(addedNodeRef, ContentModel.ASPECT_AUDITABLE);
            }
        }

        // 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.getWorkflowService().startWorkflow(this.selectedWorkflow, params);
        if (path != null) {
            // extract the start task
            List<WorkflowTask> tasks = this.getWorkflowService().getTasksForWorkflowPath(path.id);
            if (tasks.size() == 1) {
                WorkflowTask startTask = tasks.get(0);

                if (logger.isDebugEnabled())
                    logger.debug("Found start task:" + startTask);

                if (startTask.state == WorkflowTaskState.IN_PROGRESS) {
                    // end the start task to trigger the first 'proper'
                    // task in the workflow
                    this.getWorkflowService().endTask(startTask.id, null);
                }
            }

            if (logger.isDebugEnabled())
                logger.debug("Started workflow: " + this.selectedWorkflow);
        }

        return outcome;
    }

    @Override
    public String next() {
        String stepName = Application.getWizardManager().getCurrentStepName();

        if ("options".equals(stepName)
                && (this.selectedWorkflow.equals(this.previouslySelectedWorkflow) == false)) {
            // retrieve the start task for the selected workflow, get the task
            // definition and create a transient node to allow the property
            // sheet to collect the required data.

            WorkflowDefinition flowDef = this.getWorkflows().get(this.selectedWorkflow);

            if (logger.isDebugEnabled())
                logger.debug("Selected workflow: " + flowDef);

            WorkflowTaskDefinition taskDef = flowDef.getStartTaskDefinition();
            if (taskDef != null) {
                if (logger.isDebugEnabled())
                    logger.debug("Start task definition: " + taskDef);

                // create an instance of a task from the data dictionary
                this.startTaskNode = TransientNode.createNew(getDictionaryService(), taskDef.metadata,
                        "task_" + System.currentTimeMillis(), null);
            }

            // we also need to reset the resources list so that the actions get re-evaluated
            resetRichList();
        }

        return null;
    }

    @Override
    public String back() {
        String stepName = Application.getWizardManager().getCurrentStepName();

        // if we have come back to the "choose-workflow" step remember
        // the current workflow selection
        if ("choose-workflow".equals(stepName)) {
            this.previouslySelectedWorkflow = this.selectedWorkflow;
        }

        return null;
    }

    @Override
    public boolean getNextButtonDisabled() {
        return this.nextButtonDisabled;
    }

    @Override
    public String getContainerTitle() {
        String wizTitle = null;

        ResourceBundle bundle = Application.getBundle(FacesContext.getCurrentInstance());
        String stepName = Application.getWizardManager().getCurrentStepName();

        if ("choose-workflow".equals(stepName) == false && this.selectedWorkflow != null) {
            String titlePattern = bundle.getString("start_named_workflow_wizard");
            WorkflowDefinition workflowDef = this.getWorkflows().get(this.selectedWorkflow);
            wizTitle = MessageFormat.format(titlePattern, new Object[] { workflowDef.title });
        } else {
            wizTitle = bundle.getString("start_workflow_wizard");
        }

        return wizTitle;
    }

    // ------------------------------------------------------------------------------
    // Event Handlers

    /**
     * Prepares the dialog to allow the user to add an item to the workflow package
     * 
     * @param event The event
     */
    public void prepareForAdd(ActionEvent event) {
        this.isItemBeingAdded = true;
    }

    /**
     * Cancels the adding of an item to the workflow package
     * 
     * @param event The event
     */
    public void cancelAddPackageItems(ActionEvent event) {
        this.isItemBeingAdded = false;
    }

    /**
     * Adds items to the workflow package
     * 
     * @param event The event
     */
    public void addPackageItems(ActionEvent event) {
        if (this.itemsToAdd != null) {
            for (String item : this.itemsToAdd) {
                this.packageItemsToAdd.add(item);

                if (logger.isDebugEnabled())
                    logger.debug("Added item to the added list: " + item);
            }

            // reset the rich list so it re-renders
            this.packageItemsRichList.setValue(null);
        }

        this.isItemBeingAdded = false;
        this.itemsToAdd = null;
    }

    /**
     * Removes an item from the workflow package
     * 
     * @param event The event containing a reference to the item to remove
     */
    public void removePackageItem(ActionEvent event) {
        UIActionLink link = (UIActionLink) event.getComponent();
        Map<String, String> params = link.getParameterMap();
        String nodeRef = new NodeRef(Repository.getStoreRef(), params.get("id")).toString();

        if (this.packageItemsToAdd.contains(nodeRef)) {
            // remove the item from the added list if it was added in this dialog session
            this.packageItemsToAdd.remove(nodeRef);

            if (logger.isDebugEnabled())
                logger.debug("Removed item from the added list: " + nodeRef);
        }

        // reset the rich list so it re-renders
        this.packageItemsRichList.setValue(null);
    }

    // ------------------------------------------------------------------------------
    // Bean Getters and Setters

    /**
     * Returns a String array of NodeRef's that are being added to the workflow package
     * 
     * @return String array of NodeRef's
     */
    public String[] getItemsToAdd() {
        return this.itemsToAdd;
    }

    /**
     * Sets the NodeRef's to add as items to the workflow package
     * 
     * @param itemsToAdd NodeRef's to add to the workflow package
     */
    public void setItemsToAdd(String[] itemsToAdd) {
        this.itemsToAdd = itemsToAdd;
    }

    /**
     * Determines whether an item is currently being added to the workflow package
     * 
     * @return true if an item is being added
     */
    public boolean isItemBeingAdded() {
        return this.isItemBeingAdded;
    }

    /**
     * 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 workflow selected by the user
     * 
     * @return The selected workflow
     */
    public String getSelectedWorkflow() {
        return selectedWorkflow;
    }

    /**
     * Sets the selected workflow
     * 
     * @param selectedWorkflow The workflow selected
     */
    public void setSelectedWorkflow(String selectedWorkflow) {
        this.selectedWorkflow = selectedWorkflow;
    }

    /**
     * Returns the Node representing the start task metadata required
     * 
     * @return The Node for the start task
     */
    public Node getTaskMetadataNode() {
        return this.startTaskNode;
    }

    /**
     * Returns the action group the current task uses for the workflow package
     * 
     * @return action group id
     */
    public String getPackageActionGroup() {
        String actionGroup = null;

        WorkflowDefinition flowDef = this.getWorkflows().get(this.selectedWorkflow);
        WorkflowTaskDefinition taskDef = flowDef.getStartTaskDefinition();
        if (taskDef != null) {
            PropertyDefinition propDef = taskDef.metadata.getProperties()
                    .get(WorkflowModel.PROP_PACKAGE_ACTION_GROUP);
            if (propDef != null) {
                actionGroup = propDef.getDefaultValue();
            }
        }

        return actionGroup;
    }

    /**
     * Returns the action group the current task uses for each workflow package item
     * 
     * @return action group id
     */
    public String getPackageItemActionGroup() {
        String actionGroup = null;

        WorkflowDefinition flowDef = this.getWorkflows().get(this.selectedWorkflow);
        WorkflowTaskDefinition taskDef = flowDef.getStartTaskDefinition();
        if (taskDef != null) {
            PropertyDefinition propDef = taskDef.metadata.getProperties()
                    .get(WorkflowModel.PROP_PACKAGE_ITEM_ACTION_GROUP);
            if (propDef != null) {
                actionGroup = propDef.getDefaultValue();
            }
        }

        return actionGroup;
    }

    /**
     * @return Returns the summary data for the wizard.
     */
    public String getSummary() {
        ResourceBundle bundle = Application.getBundle(FacesContext.getCurrentInstance());

        String workflowName = null;
        for (SelectItem item : this.availableWorkflows) {
            if (item.getValue().equals(this.selectedWorkflow)) {
                workflowName = item.getLabel();
                break;
            }
        }

        return buildSummary(new String[] { bundle.getString("start_workflow") }, new String[] { workflowName });
    }

    /**
     * Determines whether there are any workflows to start
     * 
     * @return true if there are startable workflows
     */
    public boolean getHasStartableWorkflows() {
        // get the list of startable workflow, this will intialise the list if necessary
        List<SelectItem> workflows = getStartableWorkflows();
        return (workflows.size() > 0);
    }

    private void initializeWorkflows() {
        // NOTE: we don't cache the list of startable workflows as they could get
        //       updated, in which case we need the latest instance id, they could
        //       theoretically also get removed.

        this.availableWorkflows = new ArrayList<SelectItem>(4);
        this.workflows = new HashMap<String, WorkflowDefinition>(4);

        List<String> configuredInvitationWorkflows = this.getInvitationServiceWorkflowNames();
        List<String> publishingWorkflows = this.getPublishingWorkflowNames();
        List<String> excludedWorkflows = this.getExcludedWorkflows();

        List<WorkflowDefinition> workflowDefs = this.getWorkflowService().getDefinitions();
        for (WorkflowDefinition workflowDef : workflowDefs) {
            String name = workflowDef.name;

            if (configuredInvitationWorkflows.contains(name) == false && publishingWorkflows.contains(name) == false
                    && excludedWorkflows.contains(name) == false) {
                // add the workflow if it is not a WCM specific workflow
                String label = workflowDef.title;
                if (workflowDef.description != null && workflowDef.description.length() > 0) {
                    label = label + " (" + workflowDef.description + ")";
                }
                this.availableWorkflows.add(new SelectItem(workflowDef.id, label));
                this.workflows.put(workflowDef.id, workflowDef);
            }
        }

        // disable the next button if there are no workflows
        if (this.availableWorkflows.size() == 0) {
            this.nextButtonDisabled = true;
        }
    }

    /**
     * Returns a list of workflows that can be started.
     * 
     * @return List of SelectItem objects representing the workflows
     */
    public List<SelectItem> getStartableWorkflows() {
        if (availableWorkflows == null) {
            initializeWorkflows();
        }

        // Alphabetical list sorting
        // Fix bug reported in https://issues.alfresco.com/browse/ETWOTWO-302

        QuickSort sorter = new QuickSort(availableWorkflows, "label", true, IDataContainer.SORT_CASEINSENSITIVE);
        sorter.sort();

        // select the first workflow in the list
        if (this.availableWorkflows.size() > 0 && previouslySelectedWorkflow == null) {
            this.selectedWorkflow = (String) this.availableWorkflows.get(0).getValue();
        }

        return availableWorkflows;
    }

    /**
     * Returns the URL to the Workflow Definition Image of the current task
     * 
     * @return  the url
     */
    public String getWorkflowDefinitionImageUrl() {
        String url = null;
        if (selectedWorkflow != null) {
            WorkflowDefinition def = getWorkflows().get(selectedWorkflow);
            url = "/workflowdefinitionimage/" + def.id;
        }
        return url;
    }

    /**
     * Returns a list of resources associated with this task
     * i.e. the children of the workflow package
     * 
     * @return The list of nodes
     */
    public List<Node> getResources() {
        this.resources = new ArrayList<Node>(4);

        UserTransaction tx = null;
        try {
            FacesContext context = FacesContext.getCurrentInstance();
            tx = Repository.getUserTransaction(context, true);
            tx.begin();

            for (String newItem : this.packageItemsToAdd) {
                NodeRef nodeRef = new NodeRef(newItem);
                if (this.getNodeService().exists(nodeRef)) {
                    // create our Node representation
                    MapNode node = new MapNode(nodeRef, this.getNodeService(), true);
                    this.browseBean.setupCommonBindingProperties(node);

                    // add property resolvers to show path information
                    node.addPropertyResolver("path", this.browseBean.resolverPath);
                    node.addPropertyResolver("displayPath", this.browseBean.resolverDisplayPath);

                    this.resources.add(node);
                } else {
                    if (logger.isDebugEnabled())
                        logger.debug("Ignoring " + nodeRef + " as it has been removed from the repository");
                }
            }

            // commit the transaction
            tx.commit();
        } catch (Throwable err) {
            Utils.addErrorMessage(MessageFormat.format(
                    Application.getMessage(FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC),
                    err.getMessage()), err);
            this.resources = Collections.<Node>emptyList();
            try {
                if (tx != null) {
                    tx.rollback();
                }
            } catch (Exception tex) {
            }
        }

        return this.resources;
    }

    /**
     * Sets the workflow service to use
     * 
     * @param workflowService WorkflowService instance
     */
    public void setWorkflowService(WorkflowService workflowService) {
        this.workflowService = workflowService;
    }

    protected WorkflowService getWorkflowService() {
        if (this.workflowService == null) {
            this.workflowService = Repository.getServiceRegistry(FacesContext.getCurrentInstance())
                    .getWorkflowService();
        }
        return this.workflowService;
    }

    // ------------------------------------------------------------------------------
    // Helper methods

    /**
     * Resets the rich list
     */
    protected void resetRichList() {
        if (this.packageItemsRichList != null) {
            this.packageItemsRichList.setValue(null);
            this.packageItemsRichList = null;
        }
    }

    /**
     * Get the Names of globally excluded workflow-names.
     * 
     * @return The names of the workflows to exclude.
     */
    protected List<String> getExcludedWorkflows() {
        if ((excludedWorkflows == null) || (Application.isDynamicConfig(FacesContext.getCurrentInstance()))) {
            FacesContext fc = FacesContext.getCurrentInstance();
            ConfigElement config = Application.getConfigService(fc).getGlobalConfig()
                    .getConfigElement("excluded-workflows");
            if (config != null) {
                StringTokenizer t = new StringTokenizer(config.getValue().trim(), ", ");
                excludedWorkflows = new ArrayList<String>(t.countTokens());
                while (t.hasMoreTokens()) {
                    String wfName = t.nextToken();
                    excludedWorkflows.add(wfName);
                }
            } else {
                excludedWorkflows = Collections.emptyList();
            }
        }
        return excludedWorkflows;
    }

    /**
     * Get the Names of the Invitation Service Workflows
     * 
     * @return The names of the Invitation Service workflows
     */
    protected List<String> getInvitationServiceWorkflowNames() {
        if (invitationWorkflows == null) {
            if (invitationService != null) {
                invitationWorkflows = invitationService.getInvitationServiceWorkflowNames();
            }
        }
        return invitationWorkflows;
    }

    /**
     * Get the names of the publishing workflows
     * 
     * @return The names of the publishing workflows
     */
    protected List<String> getPublishingWorkflowNames() {
        if (publishingWorkflows == null) {
            publishingWorkflows = new ArrayList<String>(2);

            publishingWorkflows.add(JBPMEngine.ENGINE_ID + "$" + PublishingEventHelper.WORKFLOW_DEFINITION_NAME);
            publishingWorkflows
                    .add(ActivitiConstants.ENGINE_ID + "$" + PublishingEventHelper.WORKFLOW_DEFINITION_NAME);
        }

        return publishingWorkflows;
    }

    public void setInvitationService(InvitationService invitationService) {
        this.invitationService = invitationService;
    }

    public InvitationService getInvitationService() {
        return invitationService;
    }
}