001/*
002 * (C) Copyright 2010-2018 Nuxeo (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Anahide Tchertchian
018 */
019
020package org.nuxeo.ecm.automation.task;
021
022import java.util.ArrayList;
023import java.util.Date;
024import java.util.HashMap;
025import java.util.List;
026import java.util.Map;
027
028import org.apache.commons.lang3.StringUtils;
029import org.apache.commons.logging.Log;
030import org.apache.commons.logging.LogFactory;
031import org.nuxeo.ecm.automation.OperationContext;
032import org.nuxeo.ecm.automation.OperationException;
033import org.nuxeo.ecm.automation.core.Constants;
034import org.nuxeo.ecm.automation.core.annotations.Context;
035import org.nuxeo.ecm.automation.core.annotations.Operation;
036import org.nuxeo.ecm.automation.core.annotations.OperationMethod;
037import org.nuxeo.ecm.automation.core.annotations.Param;
038import org.nuxeo.ecm.automation.core.collectors.DocumentModelCollector;
039import org.nuxeo.ecm.automation.core.util.StringList;
040import org.nuxeo.ecm.core.api.CoreSession;
041import org.nuxeo.ecm.core.api.DocumentModel;
042import org.nuxeo.ecm.core.api.NuxeoPrincipal;
043import org.nuxeo.ecm.platform.task.TaskEventNames;
044import org.nuxeo.ecm.platform.task.TaskService;
045
046/**
047 * Creates a task
048 *
049 * @author Anahide Tchertchian
050 * @since 5.5
051 */
052@Operation(id = CreateTask.ID, category = Constants.CAT_SERVICES, label = "Create task", since = "5.3.2", description = "Enable to create a task bound to the document. "
053        + "<p><b>Directive</b>, <b>comment</b> and <b>due date</b> will be displayed in the task list of the user. "
054        + "In <b>accept operation chain</b> and <b>reject operation chain</b> fields, "
055        + "you can put the operation chain ID of your choice among the one you contributed. "
056        + "Those operations will be executed when the user validates the task, "
057        + "depending on  whether he accepts or rejects the task. "
058        + "You have to specify a variable name (the <b>key for ... </b> parameter) to resolve target users and groups to which the task will be assigned. "
059        + "You can use Get Users and Groups to update a context variable with some users and groups. "
060        + "If you check <b>create one task per actor</b>, each of the actors will have a task to achieve, "
061        + "versus \"the first who achieve the task makes it disappear for the others\".</p>", aliases = {
062                "Workflow.CreateTask" })
063public class CreateTask {
064
065    public static final String ID = "Task.Create";
066
067    private static final Log log = LogFactory.getLog(CreateTask.class);
068
069    public enum OperationTaskVariableName {
070        acceptOperationChain, rejectOperationChain, createdFromCreateTaskOperation, taskDocument
071    }
072
073    @Context
074    protected OperationContext ctx;
075
076    @Context
077    protected CoreSession coreSession;
078
079    @Context
080    protected TaskService TaskService;
081
082    @Param(name = "task name", required = true, order = 0)
083    protected String taskName;
084
085    @Param(name = "due date", required = false, order = 1)
086    protected Date dueDate;
087
088    @Param(name = "directive", required = false, order = 2)
089    protected String directive;
090
091    @Param(name = "comment", required = false, order = 3)
092    protected String comment;
093
094    @Param(name = "accept operation chain", required = false, order = 4)
095    protected String acceptOperationChain;
096
097    @Param(name = "reject operation chain", required = false, order = 5)
098    protected String rejectOperationChain;
099
100    @Param(name = "variable name for actors prefixed ids", required = false, order = 6)
101    protected String keyForActors;
102
103    @Param(name = "additional list of actors prefixed ids", required = false, order = 7)
104    protected StringList additionalPrefixedActors;
105
106    @Param(name = "create one task per actor", required = false, values = "true", order = 8)
107    protected boolean createOneTaskPerActor = true;
108
109    @OperationMethod(collector = DocumentModelCollector.class)
110    @SuppressWarnings("unchecked")
111    public DocumentModel run(DocumentModel document) throws OperationException {
112        NuxeoPrincipal pal = coreSession.getPrincipal();
113
114        List<String> prefixedActorIds = new ArrayList<>();
115        Object actors = ctx.get(keyForActors);
116        if (actors != null) {
117            boolean throwError = false;
118            try {
119                if (actors instanceof List) {
120                    prefixedActorIds.addAll((List<String>) actors);
121                } else if (actors instanceof String[]) {
122                    for (String actor : (String[]) actors) {
123                        prefixedActorIds.add(actor);
124                    }
125                } else if (actors instanceof String) {
126                    prefixedActorIds.add((String) actors);
127                } else {
128                    throwError = true;
129                }
130            } catch (ClassCastException e) {
131                throwError = true;
132            }
133            if (throwError) {
134                throw new OperationException(String.format("Invalid key to retrieve a list, array or single "
135                        + "string of prefixed actor " + "ids '%s', value is not correct: %s", keyForActors, actors));
136            }
137        }
138
139        if (additionalPrefixedActors != null) {
140            prefixedActorIds.addAll(additionalPrefixedActors);
141        }
142
143        if (prefixedActorIds.isEmpty()) {
144            // no actors: do nothing
145            log.debug("No actors could be resolved => do not create any task");
146            return document;
147        }
148
149        // create the task, passing operation chains in task variables
150        Map<String, String> taskVariables = new HashMap<>();
151        taskVariables.put(OperationTaskVariableName.createdFromCreateTaskOperation.name(), "true");
152        if (!StringUtils.isEmpty(acceptOperationChain)) {
153            taskVariables.put(OperationTaskVariableName.acceptOperationChain.name(), acceptOperationChain);
154        }
155        if (!StringUtils.isEmpty(rejectOperationChain)) {
156            taskVariables.put(OperationTaskVariableName.rejectOperationChain.name(), rejectOperationChain);
157        }
158
159        // disable notification service
160        taskVariables.put(TaskEventNames.DISABLE_NOTIFICATION_SERVICE, "true");
161
162        if (TaskService == null) {
163            throw new OperationException("Service jbpmTaskService not found");
164        }
165        TaskService.createTask(coreSession, pal, document, taskName, prefixedActorIds, createOneTaskPerActor, directive,
166                comment, dueDate, taskVariables, null);
167
168        coreSession.save();
169
170        return document;
171    }
172
173}