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 *     ldoguin
016 */
017
018package org.nuxeo.ecm.platform.routing.dm.operation;
019
020import java.security.Principal;
021import java.util.ArrayList;
022import java.util.HashMap;
023import java.util.List;
024import java.util.Map;
025
026import org.apache.commons.lang.StringUtils;
027import org.apache.commons.logging.Log;
028import org.apache.commons.logging.LogFactory;
029import org.nuxeo.ecm.automation.OperationContext;
030import org.nuxeo.ecm.automation.OperationException;
031import org.nuxeo.ecm.automation.core.Constants;
032import org.nuxeo.ecm.automation.core.annotations.Context;
033import org.nuxeo.ecm.automation.core.annotations.Operation;
034import org.nuxeo.ecm.automation.core.annotations.OperationMethod;
035import org.nuxeo.ecm.automation.core.annotations.Param;
036import org.nuxeo.ecm.automation.core.collectors.DocumentModelCollector;
037import org.nuxeo.ecm.automation.core.util.Properties;
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.DocumentModelList;
042import org.nuxeo.ecm.core.api.NuxeoPrincipal;
043import org.nuxeo.ecm.core.api.PropertyException;
044import org.nuxeo.ecm.core.api.impl.DocumentModelListImpl;
045import org.nuxeo.ecm.platform.routing.api.DocumentRouteStep;
046import org.nuxeo.ecm.platform.routing.api.DocumentRoutingConstants;
047import org.nuxeo.ecm.platform.routing.api.DocumentRoutingService;
048import org.nuxeo.ecm.platform.routing.dm.adapter.TaskStep;
049import org.nuxeo.ecm.platform.routing.dm.api.RoutingTaskConstants;
050import org.nuxeo.ecm.platform.task.Task;
051import org.nuxeo.ecm.platform.task.TaskEventNames;
052import org.nuxeo.ecm.platform.task.TaskService;
053import org.nuxeo.ecm.platform.usermanager.UserManager;
054
055/**
056 * Creates a routing task
057 *
058 * @author ldoguin
059 * @since 5.6
060 * @deprecated since 5.9.2 - Use only routes of type 'graph'
061 */
062@Deprecated
063@Operation(id = CreateRoutingTask.ID, category = Constants.CAT_SERVICES, label = "Create task", since = "5.6", description = "Enable to create a routingTask bound to a route and its document. "
064        + "In <b>accept operation chain</b> and <b>reject operation chain</b> fields, "
065        + "you can put the operation chain ID of your choice among the one you contributed. "
066        + "Those operations will be executed when the user validates the task, "
067        + "depending on  whether he accepts or rejects the task. "
068        + "Extra (String) properties can be set on the taskVariables from the input document or from the step.", addToStudio = false)
069public class CreateRoutingTask {
070
071    public static final String ID = "Workflow.CreateRoutingTask";
072
073    private static final Log log = LogFactory.getLog(CreateRoutingTask.class);
074
075    public enum OperationTaskVariableName {
076        acceptOperationChain, rejectOperationChain, createdFromCreateTaskOperation, taskDocuments
077    }
078
079    public static final String STEP_PREFIX = "StepTask:";
080
081    public static final String DOCUMENT_PREFIX = "Document:";
082
083    @Context
084    protected OperationContext ctx;
085
086    @Context
087    protected CoreSession coreSession;
088
089    @Context
090    UserManager userManager;
091
092    @Context
093    protected TaskService taskService;
094
095    @Context
096    protected DocumentRoutingService routing;
097
098    @Param(name = "accept operation chain", required = false, order = 4)
099    protected String acceptOperationChain;
100
101    @Param(name = "reject operation chain", required = false, order = 5)
102    protected String rejectOperationChain;
103
104    @Param(name = "mappingTaskVariables", required = false)
105    protected Properties mappingTaskVariables;
106
107    @Param(name = "mappingProperties", required = false)
108    protected Properties mappingProperties;
109
110    @OperationMethod(collector = DocumentModelCollector.class)
111    public DocumentModel createTask(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        DocumentRouteStep step = (DocumentRouteStep) ctx.get(DocumentRoutingConstants.OPERATION_STEP_DOCUMENT_KEY);
118        DocumentModel stepDocument = step.getDocument();
119        TaskStep taskStep = stepDocument.getAdapter(TaskStep.class);
120        List<String> actors = taskStep.getActors();
121
122        if (actors.isEmpty()) {
123            // no actors: do nothing
124            log.debug("No actors could be resolved => do not create any task");
125            return document;
126        }
127
128        // create the task, passing operation chains in task variables
129        Map<String, String> taskVariables = new HashMap<String, String>();
130        taskVariables.put(DocumentRoutingConstants.OPERATION_STEP_DOCUMENT_KEY, step.getDocument().getId());
131        taskVariables.put(OperationTaskVariableName.createdFromCreateTaskOperation.name(), "true");
132        if (!StringUtils.isEmpty(acceptOperationChain)) {
133            taskVariables.put(OperationTaskVariableName.acceptOperationChain.name(), acceptOperationChain);
134        }
135        if (!StringUtils.isEmpty(rejectOperationChain)) {
136            taskVariables.put(OperationTaskVariableName.rejectOperationChain.name(), rejectOperationChain);
137        }
138
139        // disable notification service
140        taskVariables.put(TaskEventNames.DISABLE_NOTIFICATION_SERVICE, "true");
141
142        if (routing == null) {
143            throw new OperationException("Service routingTaskService not found");
144        }
145        if (mappingTaskVariables != null) {
146            mapPropertiesToTaskVariables(taskVariables, stepDocument, document, mappingTaskVariables);
147        }
148        // TODO: call method with number of comments after NXP-8068 is merged
149        List<Task> tasks = taskService.createTask(coreSession, (NuxeoPrincipal) pal, document, taskStep.getName(),
150                actors, false, taskStep.getDirective(), null, taskStep.getDueDate(), taskVariables, null);
151        routing.makeRoutingTasks(coreSession, tasks);
152        DocumentModelList docList = new DocumentModelListImpl(tasks.size());
153        for (Task task : tasks) {
154            docList.add(((mappingProperties == null) ? (task.getDocument()) : mapPropertiesToTaskDocument(coreSession,
155                    stepDocument, task.getDocument(), document, mappingProperties)));
156        }
157
158        // all the actors should be able to validate the step creating the task
159        for (String actor : actors) {
160            step.setCanReadStep(coreSession, actor);
161            step.setCanValidateStep(coreSession, actor);
162            step.setCanUpdateStep(coreSession, actor);
163        }
164        ctx.put(OperationTaskVariableName.taskDocuments.name(), docList);
165
166        ctx.put(RoutingTaskConstants.ROUTING_TASK_ACTORS_KEY, new StringList(getAllActors(actors)));
167        return document;
168    }
169
170    protected void mapPropertiesToTaskVariables(Map<String, String> taskVariables, DocumentModel stepDoc,
171            DocumentModel inputDoc, Properties mappingProperties) {
172        for (Map.Entry<String, String> prop : mappingProperties.entrySet()) {
173            String getter = prop.getKey();
174            String setter = prop.getValue();
175            DocumentModel setterDoc = null;
176            if (setter.startsWith(DOCUMENT_PREFIX)) {
177                setterDoc = inputDoc;
178                setter = setter.substring(DOCUMENT_PREFIX.length());
179            } else if (setter.startsWith(STEP_PREFIX)) {
180                setterDoc = stepDoc;
181                setter = setter.substring(STEP_PREFIX.length());
182            }
183            try {
184                taskVariables.put(getter, (String) setterDoc.getPropertyValue(setter));
185            } catch (PropertyException e) {
186                log.error("Could not map property on the task document in the taskVariables ", e);
187            }
188        }
189    }
190
191    DocumentModel mapPropertiesToTaskDocument(CoreSession session, DocumentModel stepDoc, DocumentModel taskDoc,
192            DocumentModel inputDoc, Properties mappingProperties) {
193        for (Map.Entry<String, String> prop : mappingProperties.entrySet()) {
194            String getter = prop.getKey();
195            String setter = prop.getValue();
196            DocumentModel setterDoc = null;
197            if (setter.startsWith(DOCUMENT_PREFIX)) {
198                setterDoc = inputDoc;
199                setter = setter.substring(DOCUMENT_PREFIX.length());
200            } else if (setter.startsWith(STEP_PREFIX)) {
201                setterDoc = stepDoc;
202                setter = setter.substring(STEP_PREFIX.length());
203            }
204            try {
205                taskDoc.setPropertyValue(getter, setterDoc.getPropertyValue(setter));
206            } catch (PropertyException e) {
207                log.error("Could not map property on the task document in the taskVariables ", e);
208            }
209        }
210        return session.saveDocument(taskDoc);
211    }
212
213    protected List<String> getAllActors(List<String> actors) {
214        List<String> allActors = new ArrayList<String>();
215        for (String actor : actors) {
216            if (userManager.getGroup(actor) != null) {
217                List<String> allSimpleUsers = userManager.getUsersInGroupAndSubGroups(actor);
218                for (String string : allSimpleUsers) {
219                    if (!allActors.contains(string)) {
220                        allActors.add(string);
221                    }
222                }
223                continue;
224            }
225            if (!allActors.contains(actor)) {
226                allActors.add(actor);
227            }
228        }
229        return allActors;
230    }
231
232}