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.lang.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.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        Principal pal = coreSession.getPrincipal();
115        if (!(pal instanceof NuxeoPrincipal)) {
116            throw new OperationException("Principal is not an instance of NuxeoPrincipal");
117        }
118
119        DocumentRouteStep step = (DocumentRouteStep) ctx.get(DocumentRoutingConstants.OPERATION_STEP_DOCUMENT_KEY);
120        DocumentModel stepDocument = step.getDocument();
121        TaskStep taskStep = stepDocument.getAdapter(TaskStep.class);
122        List<String> actors = taskStep.getActors();
123
124        if (actors.isEmpty()) {
125            // no actors: do nothing
126            log.debug("No actors could be resolved => do not create any task");
127            return document;
128        }
129
130        // create the task, passing operation chains in task variables
131        Map<String, String> taskVariables = new HashMap<String, String>();
132        taskVariables.put(DocumentRoutingConstants.OPERATION_STEP_DOCUMENT_KEY, step.getDocument().getId());
133        taskVariables.put(OperationTaskVariableName.createdFromCreateTaskOperation.name(), "true");
134        if (!StringUtils.isEmpty(acceptOperationChain)) {
135            taskVariables.put(OperationTaskVariableName.acceptOperationChain.name(), acceptOperationChain);
136        }
137        if (!StringUtils.isEmpty(rejectOperationChain)) {
138            taskVariables.put(OperationTaskVariableName.rejectOperationChain.name(), rejectOperationChain);
139        }
140
141        // disable notification service
142        taskVariables.put(TaskEventNames.DISABLE_NOTIFICATION_SERVICE, "true");
143
144        if (routing == null) {
145            throw new OperationException("Service routingTaskService not found");
146        }
147        if (mappingTaskVariables != null) {
148            mapPropertiesToTaskVariables(taskVariables, stepDocument, document, mappingTaskVariables);
149        }
150        // TODO: call method with number of comments after NXP-8068 is merged
151        List<Task> tasks = taskService.createTask(coreSession, (NuxeoPrincipal) pal, document, taskStep.getName(),
152                actors, false, taskStep.getDirective(), null, taskStep.getDueDate(), taskVariables, null);
153        routing.makeRoutingTasks(coreSession, tasks);
154        DocumentModelList docList = new DocumentModelListImpl(tasks.size());
155        for (Task task : tasks) {
156            docList.add(((mappingProperties == null) ? (task.getDocument()) : mapPropertiesToTaskDocument(coreSession,
157                    stepDocument, task.getDocument(), document, mappingProperties)));
158        }
159
160        // all the actors should be able to validate the step creating the task
161        for (String actor : actors) {
162            step.setCanReadStep(coreSession, actor);
163            step.setCanValidateStep(coreSession, actor);
164            step.setCanUpdateStep(coreSession, actor);
165        }
166        ctx.put(OperationTaskVariableName.taskDocuments.name(), docList);
167
168        ctx.put(RoutingTaskConstants.ROUTING_TASK_ACTORS_KEY, new StringList(getAllActors(actors)));
169        return document;
170    }
171
172    protected void mapPropertiesToTaskVariables(Map<String, String> taskVariables, DocumentModel stepDoc,
173            DocumentModel inputDoc, Properties mappingProperties) {
174        for (Map.Entry<String, String> prop : mappingProperties.entrySet()) {
175            String getter = prop.getKey();
176            String setter = prop.getValue();
177            DocumentModel setterDoc = null;
178            if (setter.startsWith(DOCUMENT_PREFIX)) {
179                setterDoc = inputDoc;
180                setter = setter.substring(DOCUMENT_PREFIX.length());
181            } else if (setter.startsWith(STEP_PREFIX)) {
182                setterDoc = stepDoc;
183                setter = setter.substring(STEP_PREFIX.length());
184            }
185            try {
186                taskVariables.put(getter, (String) setterDoc.getPropertyValue(setter));
187            } catch (PropertyException e) {
188                log.error("Could not map property on the task document in the taskVariables ", e);
189            }
190        }
191    }
192
193    DocumentModel mapPropertiesToTaskDocument(CoreSession session, DocumentModel stepDoc, DocumentModel taskDoc,
194            DocumentModel inputDoc, Properties mappingProperties) {
195        for (Map.Entry<String, String> prop : mappingProperties.entrySet()) {
196            String getter = prop.getKey();
197            String setter = prop.getValue();
198            DocumentModel setterDoc = null;
199            if (setter.startsWith(DOCUMENT_PREFIX)) {
200                setterDoc = inputDoc;
201                setter = setter.substring(DOCUMENT_PREFIX.length());
202            } else if (setter.startsWith(STEP_PREFIX)) {
203                setterDoc = stepDoc;
204                setter = setter.substring(STEP_PREFIX.length());
205            }
206            try {
207                taskDoc.setPropertyValue(getter, setterDoc.getPropertyValue(setter));
208            } catch (PropertyException e) {
209                log.error("Could not map property on the task document in the taskVariables ", e);
210            }
211        }
212        return session.saveDocument(taskDoc);
213    }
214
215    protected List<String> getAllActors(List<String> actors) {
216        List<String> allActors = new ArrayList<String>();
217        for (String actor : actors) {
218            if (userManager.getGroup(actor) != null) {
219                List<String> allSimpleUsers = userManager.getUsersInGroupAndSubGroups(actor);
220                for (String string : allSimpleUsers) {
221                    if (!allActors.contains(string)) {
222                        allActors.add(string);
223                    }
224                }
225                continue;
226            }
227            if (!allActors.contains(actor)) {
228                allActors.add(actor);
229            }
230        }
231        return allActors;
232    }
233
234}