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.security.Principal;
023import java.util.ArrayList;
024import java.util.Date;
025import java.util.HashMap;
026import java.util.List;
027import java.util.Map;
028
029import org.apache.commons.lang3.StringUtils;
030import org.apache.commons.logging.Log;
031import org.apache.commons.logging.LogFactory;
032import org.nuxeo.ecm.automation.OperationContext;
033import org.nuxeo.ecm.automation.OperationException;
034import org.nuxeo.ecm.automation.core.Constants;
035import org.nuxeo.ecm.automation.core.annotations.Context;
036import org.nuxeo.ecm.automation.core.annotations.Operation;
037import org.nuxeo.ecm.automation.core.annotations.OperationMethod;
038import org.nuxeo.ecm.automation.core.annotations.Param;
039import org.nuxeo.ecm.automation.core.collectors.DocumentModelCollector;
040import org.nuxeo.ecm.automation.core.util.StringList;
041import org.nuxeo.ecm.core.api.CoreSession;
042import org.nuxeo.ecm.core.api.DocumentModel;
043import org.nuxeo.ecm.core.api.NuxeoPrincipal;
044import org.nuxeo.ecm.platform.task.TaskEventNames;
045import org.nuxeo.ecm.platform.task.TaskService;
046
047/**
048 * Creates a task
049 *
050 * @author Anahide Tchertchian
051 * @since 5.5
052 */
053@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. "
054        + "<p><b>Directive</b>, <b>comment</b> and <b>due date</b> will be displayed in the task list of the user. "
055        + "In <b>accept operation chain</b> and <b>reject operation chain</b> fields, "
056        + "you can put the operation chain ID of your choice among the one you contributed. "
057        + "Those operations will be executed when the user validates the task, "
058        + "depending on  whether he accepts or rejects the task. "
059        + "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. "
060        + "You can use Get Users and Groups to update a context variable with some users and groups. "
061        + "If you check <b>create one task per actor</b>, each of the actors will have a task to achieve, "
062        + "versus \"the first who achieve the task makes it disappear for the others\".</p>", aliases = {
063                "Workflow.CreateTask" })
064public class CreateTask {
065
066    public static final String ID = "Task.Create";
067
068    private static final Log log = LogFactory.getLog(CreateTask.class);
069
070    public enum OperationTaskVariableName {
071        acceptOperationChain, rejectOperationChain, createdFromCreateTaskOperation, taskDocument
072    }
073
074    @Context
075    protected OperationContext ctx;
076
077    @Context
078    protected CoreSession coreSession;
079
080    @Context
081    protected TaskService TaskService;
082
083    @Param(name = "task name", required = true, order = 0)
084    protected String taskName;
085
086    @Param(name = "due date", required = false, order = 1)
087    protected Date dueDate;
088
089    @Param(name = "directive", required = false, order = 2)
090    protected String directive;
091
092    @Param(name = "comment", required = false, order = 3)
093    protected String comment;
094
095    @Param(name = "accept operation chain", required = false, order = 4)
096    protected String acceptOperationChain;
097
098    @Param(name = "reject operation chain", required = false, order = 5)
099    protected String rejectOperationChain;
100
101    @Param(name = "variable name for actors prefixed ids", required = false, order = 6)
102    protected String keyForActors;
103
104    @Param(name = "additional list of actors prefixed ids", required = false, order = 7)
105    protected StringList additionalPrefixedActors;
106
107    @Param(name = "create one task per actor", required = false, values = "true", order = 8)
108    protected boolean createOneTaskPerActor = true;
109
110    @OperationMethod(collector = DocumentModelCollector.class)
111    @SuppressWarnings("unchecked")
112    public DocumentModel run(DocumentModel document) throws OperationException {
113        Principal pal = coreSession.getPrincipal();
114        if (!(pal instanceof NuxeoPrincipal)) {
115            throw new OperationException("Principal is not an instance of NuxeoPrincipal");
116        }
117
118        List<String> prefixedActorIds = new ArrayList<>();
119        Object actors = ctx.get(keyForActors);
120        if (actors != null) {
121            boolean throwError = false;
122            try {
123                if (actors instanceof List) {
124                    prefixedActorIds.addAll((List<String>) actors);
125                } else if (actors instanceof String[]) {
126                    for (String actor : (String[]) actors) {
127                        prefixedActorIds.add(actor);
128                    }
129                } else if (actors instanceof String) {
130                    prefixedActorIds.add((String) actors);
131                } else {
132                    throwError = true;
133                }
134            } catch (ClassCastException e) {
135                throwError = true;
136            }
137            if (throwError) {
138                throw new OperationException(String.format("Invalid key to retrieve a list, array or single "
139                        + "string of prefixed actor " + "ids '%s', value is not correct: %s", keyForActors, actors));
140            }
141        }
142
143        if (additionalPrefixedActors != null) {
144            prefixedActorIds.addAll(additionalPrefixedActors);
145        }
146
147        if (prefixedActorIds.isEmpty()) {
148            // no actors: do nothing
149            log.debug("No actors could be resolved => do not create any task");
150            return document;
151        }
152
153        // create the task, passing operation chains in task variables
154        Map<String, String> taskVariables = new HashMap<>();
155        taskVariables.put(OperationTaskVariableName.createdFromCreateTaskOperation.name(), "true");
156        if (!StringUtils.isEmpty(acceptOperationChain)) {
157            taskVariables.put(OperationTaskVariableName.acceptOperationChain.name(), acceptOperationChain);
158        }
159        if (!StringUtils.isEmpty(rejectOperationChain)) {
160            taskVariables.put(OperationTaskVariableName.rejectOperationChain.name(), rejectOperationChain);
161        }
162
163        // disable notification service
164        taskVariables.put(TaskEventNames.DISABLE_NOTIFICATION_SERVICE, "true");
165
166        if (TaskService == null) {
167            throw new OperationException("Service jbpmTaskService not found");
168        }
169        TaskService.createTask(coreSession, (NuxeoPrincipal) pal, document, taskName, prefixedActorIds,
170                createOneTaskPerActor, directive, comment, dueDate, taskVariables, null);
171
172        coreSession.save();
173
174        return document;
175    }
176
177}