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