001/*
002 * (C) Copyright 2010-2011 Nuxeo SA (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.lang.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 = { "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        Principal pal = coreSession.getPrincipal();
113        if (!(pal instanceof NuxeoPrincipal)) {
114            throw new OperationException("Principal is not an instance of NuxeoPrincipal");
115        }
116
117        List<String> prefixedActorIds = new ArrayList<String>();
118        Object actors = ctx.get(keyForActors);
119        if (actors != null) {
120            boolean throwError = false;
121            try {
122                if (actors instanceof List) {
123                    prefixedActorIds.addAll((List<String>) actors);
124                } else if (actors instanceof String[]) {
125                    for (String actor : (String[]) actors) {
126                        prefixedActorIds.add(actor);
127                    }
128                } else if (actors instanceof String) {
129                    prefixedActorIds.add((String) actors);
130                } else {
131                    throwError = true;
132                }
133            } catch (ClassCastException e) {
134                throwError = true;
135            }
136            if (throwError) {
137                throw new OperationException(String.format("Invalid key to retrieve a list, array or single "
138                        + "string of prefixed actor " + "ids '%s', value is not correct: %s", keyForActors, actors));
139            }
140        }
141
142        if (additionalPrefixedActors != null) {
143            prefixedActorIds.addAll(additionalPrefixedActors);
144        }
145
146        if (prefixedActorIds.isEmpty()) {
147            // no actors: do nothing
148            log.debug("No actors could be resolved => do not create any task");
149            return document;
150        }
151
152        // create the task, passing operation chains in task variables
153        Map<String, String> taskVariables = new HashMap<String, String>();
154        taskVariables.put(OperationTaskVariableName.createdFromCreateTaskOperation.name(), "true");
155        if (!StringUtils.isEmpty(acceptOperationChain)) {
156            taskVariables.put(OperationTaskVariableName.acceptOperationChain.name(), acceptOperationChain);
157        }
158        if (!StringUtils.isEmpty(rejectOperationChain)) {
159            taskVariables.put(OperationTaskVariableName.rejectOperationChain.name(), rejectOperationChain);
160        }
161
162        // disable notification service
163        taskVariables.put(TaskEventNames.DISABLE_NOTIFICATION_SERVICE, "true");
164
165        if (TaskService == null) {
166            throw new OperationException("Service jbpmTaskService not found");
167        }
168        TaskService.createTask(coreSession, (NuxeoPrincipal) pal, document, taskName, prefixedActorIds,
169                createOneTaskPerActor, directive, comment, dueDate, taskVariables, null);
170
171        return document;
172    }
173
174}