Search code examples
workflowalfrescoalfresco-shareactivitialfresco-webscripts

How to add additional attachments in the workflow?


In my document management process it is often necessary to provide some additional documents (e.g. list of comments, list of differences, some screenshot, etc).

These additional documents would be convenient to add in the Activiti forms. I would like to be able to add documents at the initiation stage of a business process and at the stage of the revise.

For this, I added an aspect with associations in the workflow-model.xml (relevant part):

...
<type name="mswf:activitiRevise">
    ...
    <mandatory-aspects>
        ...
        <aspect>mswf:attachments</aspect>
    </mandatory-aspects>
</type>        
...
<aspect name="mswf:attachments">
    <associations>
        <association name="mswf:package">
            <source>
                <mandatory>false</mandatory>
                <many>true</many>
            </source>
            <target>
                <class>cm:content</class>
                <mandatory>false</mandatory>
                <many>true</many>
            </target>
        </association>
    </associations>      
</aspect>   
...
etc

In share-config-custom.xml I have the following (relevant part):

...
<config evaluator="task-type" condition="bpm:startTask">
    <forms>
        <form id="workflow-details">
            <field-visibility>
                ...
                <show id="mswf:package" />
            </field-visibility>
            <appearance>
                ...
                <set id="attachments" appearance="title" label-id="Additional docs" />
                <field id="mswf:package" set="attachments" />
            </appearance>
        </form>
        <form>
            <field-visibility>
                <show id="mswf:package" />
                ...
            </field-visibility>
            <appearance>
                <set id="attachments" appearance="title" label-id="Additional docs" />
                <field id="mswf:package" set="attachments" />
                ...
            </appearance>
        </form>
    </forms>
</config>
...
etc

Now I have an additional field where I can choose the appropriate documents.

enter image description here

It works - I added some documents and can see them at all of the stages of the document management process.

The problem occurs when I try to change a set of initially selected files. For example, when I try to add a new one. If I add a new one (or remove) - changes are not saved and in the next task, I see the same documents that were selected at the beginning.

To get control of this behavior, I developed WebScript, in which I try to manage properties. I call the WebScript from Share in the getAddedItems() method:

/**
* Returns items that have been added to the current value
*
* @method getAddedItems
* @return {array}
*/
getAddedItems: function ObjectFinder_getAddedItems() {
    var addedItems = [],
    currentItems = Alfresco.util.arrayToObject(this.options.currentValue.split(","));

    for (var item in this.selectedItems) {
        if (this.selectedItems.hasOwnProperty(item)) {
            if (!(item in currentItems)) {
                addedItems.push(item);                     
            }
        }
    }

    ...

    // here the call to the WebScript

    return addedItems;
},

Part of my Java-backed WebScript:

...
public class WorkflowAttachmentsManipulator extends DeclarativeWebScript  {
    private static final String WORKFLOW_MODEL_URI = "...";
    private WorkflowService workflowService;

    @Override
    protected Map<String, Object> executeImpl(WebScriptRequest req, Status status) {
        Map<String, Object> model = new HashMap<>();

        String taskId = req.getParameter("taskId");
        String addedItem = req.getParameter("addedItem");

        WorkflowTask workflowTask = workflowService.getTaskById(taskId);
        Map<QName, Serializable> taskProperties = workflowTask.getProperties();

        ...
        taskProperties.replace(
            QName.createQName(WORKFLOW_MODEL_URI, "package"), oldValue, addedItem);
        workflowService.updateTask(taskId, taskProperties, null, null);

...

I'm trying to replace the selected files with some arbitrary and the replace(...) method returns true. In alfrescotomcat-stdout.2017-09-06.log I also see that the property has been replaced:

Before calling the WebScript (two files in the package):

key: {WORKFLOW_MODEL_URI_HERE}package
value: [workspace://SpacesStore/7f980005-2a1b-49a5-a8ff-ce9dff31a98a, 
        workspace://SpacesStore/30d9122f-4467-451b-aeab-ca8b164f7769]

After calling the WebScript (one file in the package):

key: {WORKFLOW_MODEL_URI_HERE}package
value: workspace://SpacesStore/1a0b110f-1e09-4ca2-b367-fe25e4964a4e

After updating the form at the current task I see my new file.

But the value is not saved (lost) after revise / review and in the next task I see the same files. Let's say, the task ID for the current user was activiti$204587, then it became equals activiti$204647...

I added some debugging code to the BPMN diagram and found that the contents of mswf_package did not change after the calling the WebScript.

enter image description here

In 'Submit', main config:

for(var i = 0; i < mswf_package.size(); i++) {  
    logger.log(mswf_package.get(i).nodeRef);
}

Output:

DEBUG [repo.jscript.ScriptLogger] [http-apr-8080-exec-9] workspace://SpacesStore/7f980005-2a1b-49a5-a8ff-ce9dff31a98a
DEBUG [repo.jscript.ScriptLogger] [http-apr-8080-exec-9] workspace://SpacesStore/30d9122f-4467-451b-aeab-ca8b164f7769

In 'Review Task', listeners of the create and complete events:

for(var i = 0; i < mswf_package.size(); i++) {  
    logger.log(mswf_package.get(i).nodeRef);
}

Output:

DEBUG [repo.jscript.ScriptLogger] [http-apr-8080-exec-9] workspace://SpacesStore/7f980005-2a1b-49a5-a8ff-ce9dff31a98a
DEBUG [repo.jscript.ScriptLogger] [http-apr-8080-exec-9] workspace://SpacesStore/30d9122f-4467-451b-aeab-ca8b164f7769

How to add additional attachments in the workflow? Is it possible?


Solution

  • A set of strings with NodeRefs can be passed to the following WebScript, for example:

    public class WorkflowAttachmentsManipulator extends DeclarativeWebScript  {
        private static final String WORKFLOW_MODEL_URI = "...";
        private WorkflowService workflowService;
    
        @Override
        protected Map<String, Object> executeImpl(WebScriptRequest req, Status status) {
            Map<String, Object> model = new HashMap<>();
    
            String taskId = req.getParameter("taskId");
            String addedItems = req.getParameter("addedItems");
    
            String oldValue = "";
    
            WorkflowTask workflowTask = workflowService.getTaskById(taskId);
            Map<QName, Serializable> taskProperties = workflowTask.getProperties();
    
            Iterator taskIterator = taskProperties.entrySet().iterator();
            while(taskIterator.hasNext()) {
                Map.Entry taskPair = (Map.Entry)taskIterator.next();
                Object key = taskPair.getKey();
                if(key != null && 
                    key.toString().equalsIgnoreCase("{" + WORKFLOW_MODEL_URI + "}package")) {
    
                    if(taskPair.getValue() != null) {
                        oldValue = taskPair.getValue().toString();
                        if(!oldValue.equals("[]")) {
                            oldValue = oldValue.replaceAll("[\\[\\]]", "");
                            addedItems = "[" + oldValue + "," + addedItems + "]";
                        } else {
    
                            if(addedItems.indexOf(",") > 0) {
                                addedItems = "[" + addedItems + "]";    
                            }
                        }
                    }
    
                    taskProperties.replace(
                        QName.createQName(WORKFLOW_MODEL_URI, "package"), 
                        oldValue, 
                        addedItems);
    
                    workflowService.updateTask(workflowTask.getId(), 
                        taskProperties, null, null);
    
                    break;
                }
            }
            ...
        }
    
        public WorkflowService getWorkflowService() {
            return workflowService;
        }
    
        public void setWorkflowService(WorkflowService workflowService) {
            this.workflowService = workflowService;
        }
    }
    

    This code overrides attachments for the certain task.

    Additional files need to differentiate from those that are involved in the document management process. It can be done, for example, as follows:

    /**
    * Returns items that have been added to the current value
    *
    * @method getAddedItems
    * @return {array}
    */
    getAddedItems: function ObjectFinder_getAddedItems() {
        var addedItems = [],
        currentItems = Alfresco.util.arrayToObject(this.options.currentValue.split(","));
    
        var attachments = [];
    
        for (var item in this.selectedItems) {
            if (this.selectedItems.hasOwnProperty(item)) {          
                if (!(item in currentItems)) {
                    // modified for differentiation
                    if (this.options.displayMode == "items") {
                        attachments.push(item);
                    } else {
                        addedItems.push(item);                     
                    }
                }
            }
        }
    
        ...
    
        // call to the WebScript with attachments
    
        // modified for merge
        return addedItems.concat(attachments);
    },
    

    For saving the overridden attachments in the process variable it is necessary to define the listener of the complete event.

    Moreover, it is possible to "pass" files by the chain from task to task (with changes) by using this technique:

    Listener of the complete event:

    public class TaskCompleteListener implements TaskListener {
        @Override
        public void notify(DelegateTask delegateTask) {
            DelegateExecution execution = delegateTask.getExecution();      
            execution.setVariable("mswf_package", delegateTask.getVariable("mswf_package"));
        }
    }
    

    Listener of the create event:

    public class TaskCreateListener implements TaskListener {
        @Override
        public void notify(DelegateTask delegateTask) { 
            DelegateExecution execution = delegateTask.getExecution();
            delegateTask.setVariable("mswf_package", execution.getVariable("mswf_package"));
        }
    }
    

    This solved my issue.