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