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