001/*
002 * (C) Copyright 2012-2019 Nuxeo (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, Antoine Taillefer
018 *      Nuno Cunha (ncunha@nuxeo.com)
019 */
020package org.nuxeo.ecm.platform.task.core.service;
021
022import java.io.Serializable;
023import java.util.ArrayList;
024import java.util.Date;
025import java.util.HashMap;
026import java.util.List;
027import java.util.Map;
028
029import org.apache.commons.lang3.StringUtils;
030import org.nuxeo.common.utils.Path;
031import org.nuxeo.ecm.core.api.ConcurrentUpdateException;
032import org.nuxeo.ecm.core.api.CoreSession;
033import org.nuxeo.ecm.core.api.DocumentModel;
034import org.nuxeo.ecm.core.api.DocumentNotFoundException;
035import org.nuxeo.ecm.core.api.DocumentRef;
036import org.nuxeo.ecm.core.api.DocumentSecurityException;
037import org.nuxeo.ecm.core.api.IdRef;
038import org.nuxeo.ecm.core.api.NuxeoException;
039import org.nuxeo.ecm.core.api.NuxeoGroup;
040import org.nuxeo.ecm.core.api.NuxeoPrincipal;
041import org.nuxeo.ecm.core.api.PathRef;
042import org.nuxeo.ecm.core.api.SortInfo;
043import org.nuxeo.ecm.core.api.UnrestrictedSessionRunner;
044import org.nuxeo.ecm.core.api.security.ACE;
045import org.nuxeo.ecm.core.api.security.ACL;
046import org.nuxeo.ecm.core.api.security.ACP;
047import org.nuxeo.ecm.core.api.security.SecurityConstants;
048import org.nuxeo.ecm.platform.ec.notification.NotificationConstants;
049import org.nuxeo.ecm.platform.task.Task;
050import org.nuxeo.ecm.platform.task.TaskConstants;
051import org.nuxeo.ecm.platform.task.TaskEventNames;
052import org.nuxeo.ecm.platform.task.TaskPersisterDescriptor;
053import org.nuxeo.ecm.platform.task.TaskProvider;
054import org.nuxeo.ecm.platform.task.TaskProviderDescriptor;
055import org.nuxeo.ecm.platform.task.TaskService;
056import org.nuxeo.runtime.model.ComponentContext;
057import org.nuxeo.runtime.model.ComponentInstance;
058import org.nuxeo.runtime.model.ComponentName;
059import org.nuxeo.runtime.model.DefaultComponent;
060
061/**
062 * @author <a href="mailto:ldoguin@nuxeo.com">Laurent Doguin</a>
063 * @since 5.5
064 */
065public class TaskServiceImpl extends DefaultComponent implements TaskService {
066
067    private static final long serialVersionUID = 1L;
068
069    public static final ComponentName NAME = new ComponentName("org.nuxeo.ecm.platform.task.core.TaskService");
070
071    public static final String DEFAULT_TASK_PROVIDER = "documentTaskProvider";
072
073    private static final String TASK_PROVIDER_XP = "taskProvider";
074
075    private static final String TASK_PERSISTER_XP = "taskPersister";
076
077    private Map<String, TaskProvider> tasksProviders;
078
079    private String parentPath = "/task-root";
080
081    @Override
082    public void activate(ComponentContext context) {
083        super.activate(context);
084        tasksProviders = new HashMap<>();
085    }
086
087    @Override
088    public void deactivate(ComponentContext context) {
089        super.deactivate(context);
090        tasksProviders = null;
091    }
092
093    @Override
094    public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
095        if (extensionPoint.equals(TASK_PROVIDER_XP)) {
096            if (contribution instanceof TaskProviderDescriptor) {
097                TaskProviderDescriptor taskProviderDescriptor = (TaskProviderDescriptor) contribution;
098                String providerId = taskProviderDescriptor.getId();
099                if (taskProviderDescriptor.isEnabled()) {
100                    tasksProviders.put(providerId, taskProviderDescriptor.getNewInstance());
101                } else {
102                    if (tasksProviders.get(providerId) != null) {
103                        tasksProviders.remove(providerId);
104                    }
105                }
106            }
107        } else if (extensionPoint.equals(TASK_PERSISTER_XP)) {
108            if (contribution instanceof TaskPersisterDescriptor) {
109                TaskPersisterDescriptor taskPersisterDescriptor = (TaskPersisterDescriptor) contribution;
110                parentPath = taskPersisterDescriptor.getPath();
111            }
112        }
113    }
114
115    @Override
116    public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
117        if (extensionPoint.equals(TASK_PROVIDER_XP)) {
118            if (contribution instanceof TaskProviderDescriptor) {
119                TaskProviderDescriptor taskProviderDescriptor = (TaskProviderDescriptor) contribution;
120                String providerId = taskProviderDescriptor.getId();
121                if (tasksProviders.get(providerId) != null) {
122                    tasksProviders.remove(providerId);
123                }
124            }
125        }
126    }
127
128    @Override
129    public List<Task> createTask(CoreSession coreSession, NuxeoPrincipal principal, DocumentModel document,
130            String taskName, List<String> actorIds, boolean createOneTaskPerActor, String directive, String comment,
131            Date dueDate, Map<String, String> taskVariables, String parentPath) {
132        return createTask(coreSession, principal, document, taskName, null, null, actorIds, createOneTaskPerActor,
133                directive, comment, dueDate, taskVariables, parentPath);
134    }
135
136    /**
137     * @since 5.6
138     */
139    @Override
140    public List<Task> createTask(CoreSession coreSession, NuxeoPrincipal principal, DocumentModel document,
141            String taskDocumentType, String taskName, String taskType, String processId, List<String> actorIds,
142            boolean createOneTaskPerActor, String directive, String comment, Date dueDate,
143            Map<String, String> taskVariables, String parentPath, Map<String, Serializable> eventInfo) {
144        List<DocumentModel> docs = new ArrayList<>();
145        docs.add(document);
146        return createTaskForProcess(coreSession, principal, docs, taskDocumentType, taskName, taskType, processId, null,
147                actorIds, createOneTaskPerActor, directive, comment, dueDate, taskVariables, parentPath, eventInfo);
148    }
149
150    /**
151     * @since 5.6
152     */
153    @Override
154    public List<Task> createTask(CoreSession coreSession, NuxeoPrincipal principal, DocumentModel document,
155            String taskName, String taskType, String processId, List<String> prefixedActorIds,
156            boolean createOneTaskPerActor, String directive, String comment, Date dueDate,
157            Map<String, String> taskVariables, String parentPath) {
158        return createTask(coreSession, principal, document, TaskConstants.TASK_TYPE_NAME, taskName, taskType, processId,
159                prefixedActorIds, createOneTaskPerActor, directive, comment, dueDate, taskVariables, parentPath, null);
160    }
161
162    @Override
163    public String acceptTask(CoreSession coreSession, NuxeoPrincipal principal, Task task, String comment) {
164        return endTask(coreSession, principal, task, comment, TaskEventNames.WORKFLOW_TASK_COMPLETED, true);
165    }
166
167    @Override
168    public String rejectTask(CoreSession coreSession, NuxeoPrincipal principal, Task task, String comment) {
169        return endTask(coreSession, principal, task, comment, TaskEventNames.WORKFLOW_TASK_REJECTED, false);
170    }
171
172    /**
173     * Use the task provider held by the {@link Task#TASK_PROVIDER_KEY} task variable to end the {@code task}. If null
174     * use the {@link #DEFAULT_TASK_PROVIDER}.
175     */
176    @Override
177    public String endTask(CoreSession coreSession, NuxeoPrincipal principal, Task task, String comment,
178            String eventName, boolean isValidated) {
179
180        if (!checkPermissions(principal, task)) {
181            throw new DocumentSecurityException(
182                    String.format("User with id '%s' cannot end this task", principal.getName()));
183        } else if (!isTaskActive(task)) {
184            throw new ConcurrentUpdateException(String.format("The task '%s' is not active", task.getName()));
185        }
186        String taskProviderId = task.getVariable(Task.TASK_PROVIDER_KEY);
187        if (taskProviderId == null) {
188            taskProviderId = DEFAULT_TASK_PROVIDER;
189        }
190        TaskProvider taskProvider = tasksProviders.get(taskProviderId);
191        if (taskProvider == null) {
192            throw new NuxeoException(String.format(
193                    "No task provider registered, cannot end task. Please contribute at least the default task provider: %s.",
194                    DEFAULT_TASK_PROVIDER));
195        }
196        return taskProvider.endTask(coreSession, principal, task, comment, eventName, isValidated);
197    }
198
199    @Override
200    public boolean canEndTask(NuxeoPrincipal principal, Task task) {
201        return isTaskActive(task) && checkPermissions(principal, task);
202    }
203
204    protected boolean checkPermissions(NuxeoPrincipal principal, Task task) {
205        if (task != null && principal != null) {
206            return principal.isAdministrator() || principal.getName().equals(task.getInitiator())
207                    || isTaskAssignedToUser(task, principal, true);
208        }
209        return false;
210    }
211
212    protected boolean isTaskActive(Task task) {
213        if (task != null) {
214            return !task.isCancelled() && !task.hasEnded();
215        }
216        return false;
217    }
218
219    protected boolean isTaskAssignedToUser(Task task, NuxeoPrincipal user, boolean checkDelegatedActors) {
220        if (task != null && user != null) {
221            // user actors
222            List<String> actors = user.getAllGroups();
223            actors.add(user.getName());
224
225            // initiator
226            if (actors.contains(task.getInitiator())) {
227                return true;
228            }
229            // users
230            List<String> users = task.getActors();
231            if (checkDelegatedActors) {
232                users.addAll(task.getDelegatedActors());
233            }
234            if (users != null) {
235                for (String userName : users) {
236                    if (userName.startsWith(NuxeoPrincipal.PREFIX)) {
237                        if (actors.contains(userName.substring(NuxeoPrincipal.PREFIX.length()))) {
238                            return true;
239                        }
240                    } else if (userName.startsWith(NuxeoGroup.PREFIX)) {
241                        if (actors.contains(userName.substring(NuxeoGroup.PREFIX.length()))) {
242                            return true;
243                        }
244                    } else if (actors.contains(userName)) {
245                        return true;
246                    }
247                }
248            }
249        }
250        return false;
251    }
252
253    @Override
254    public Task getTask(CoreSession coreSession, String taskId) {
255        DocumentRef docRef = new IdRef(taskId);
256        DocumentModel taskDoc = coreSession.getDocument(docRef);
257        if (taskDoc != null) {
258            return taskDoc.getAdapter(Task.class);
259        }
260        return null;
261    }
262
263    @Override
264    public void deleteTask(CoreSession coreSession, String taskId) {
265        final DocumentRef docRef = new IdRef(taskId);
266        UnrestrictedSessionRunner runner = new UnrestrictedSessionRunner(coreSession) {
267            @Override
268            public void run() {
269                session.removeDocument(docRef);
270            }
271        };
272        runner.runUnrestricted();
273    }
274
275    @Override
276    public DocumentModel getTargetDocumentModel(Task task, CoreSession coreSession) {
277        try {
278            // TODO handle while target documents from task
279            return coreSession.getDocument(new IdRef(task.getTargetDocumentsIds().get(0)));
280        } catch (DocumentNotFoundException e) {
281            return null;
282        }
283    }
284
285    @Override
286    public List<Task> getCurrentTaskInstances(CoreSession coreSession) {
287        List<Task> tasks = new ArrayList<>();
288        List<Task> newTasks;
289        for (TaskProvider taskProvider : tasksProviders.values()) {
290            newTasks = taskProvider.getCurrentTaskInstances(coreSession);
291            if (newTasks != null) {
292                tasks.addAll(newTasks);
293            }
294        }
295        return tasks;
296    }
297
298    /**
299     * Provide @param sortInfo to handle sort page-provider contributions (see {@link #getCurrentTaskInstances})
300     *
301     * @since 5.9.3
302     */
303    @Override
304    public List<Task> getCurrentTaskInstances(CoreSession coreSession, List<SortInfo> sortInfos) {
305        List<Task> tasks = new ArrayList<>();
306        List<Task> newTasks;
307        for (TaskProvider taskProvider : tasksProviders.values()) {
308            newTasks = taskProvider.getCurrentTaskInstances(coreSession, sortInfos);
309            if (newTasks != null) {
310                tasks.addAll(newTasks);
311            }
312        }
313        return tasks;
314    }
315
316    @Override
317    public List<Task> getAllCurrentTaskInstances(CoreSession coreSession, List<SortInfo> sortInfos) {
318        List<Task> tasks = new ArrayList<>();
319        List<Task> newTasks;
320        for (TaskProvider taskProvider : tasksProviders.values()) {
321            newTasks = taskProvider.getAllCurrentTaskInstances(coreSession, sortInfos);
322            if (newTasks != null) {
323                tasks.addAll(newTasks);
324            }
325        }
326        return tasks;
327    }
328
329    /**
330     * Returns a list of task instances assigned to one of the actors in the list or to its pool.
331     *
332     * @param actors a list used as actorId to retrieve the tasks.
333     */
334    @Override
335    public List<Task> getCurrentTaskInstances(List<String> actors, CoreSession coreSession) {
336        List<Task> tasks = new ArrayList<>();
337        List<Task> newTasks;
338        for (TaskProvider taskProvider : tasksProviders.values()) {
339            newTasks = taskProvider.getCurrentTaskInstances(actors, coreSession);
340            if (newTasks != null) {
341                tasks.addAll(newTasks);
342            }
343        }
344        return tasks;
345    }
346
347    /**
348     * Provide @param sortInfo to handle sort page-provider contributions (see {@link #getCurrentTaskInstances})
349     *
350     * @since 5.9.3
351     */
352    @Override
353    public List<Task> getCurrentTaskInstances(List<String> actors, CoreSession coreSession, List<SortInfo> sortInfos) {
354        List<Task> tasks = new ArrayList<>();
355        List<Task> newTasks;
356        for (TaskProvider taskProvider : tasksProviders.values()) {
357            newTasks = taskProvider.getCurrentTaskInstances(actors, coreSession, sortInfos);
358            if (newTasks != null) {
359                tasks.addAll(newTasks);
360            }
361        }
362        return tasks;
363    }
364
365    @Override
366    public List<Task> getTaskInstances(DocumentModel dm, NuxeoPrincipal user, CoreSession coreSession) {
367        List<Task> tasks = new ArrayList<>();
368        List<Task> newTasks;
369        for (TaskProvider taskProvider : tasksProviders.values()) {
370            newTasks = taskProvider.getTaskInstances(dm, user, coreSession);
371            if (newTasks != null) {
372                tasks.addAll(newTasks);
373            }
374        }
375        return tasks;
376    }
377
378    @Override
379    public List<Task> getTaskInstances(DocumentModel dm, List<String> actors, CoreSession coreSession) {
380        List<Task> tasks = new ArrayList<>();
381        List<Task> newTasks;
382        for (TaskProvider taskProvider : tasksProviders.values()) {
383            newTasks = taskProvider.getTaskInstances(dm, actors, coreSession);
384            if (newTasks != null) {
385                tasks.addAll(newTasks);
386            }
387        }
388        return tasks;
389    }
390
391    @Override
392    public List<Task> getAllTaskInstances(String processId, CoreSession session) {
393        List<Task> tasks = new ArrayList<>();
394        List<Task> newTasks;
395        for (TaskProvider taskProvider : tasksProviders.values()) {
396            newTasks = taskProvider.getAllTaskInstances(processId, session);
397            if (newTasks != null) {
398                tasks.addAll(newTasks);
399            }
400        }
401        return tasks;
402    }
403
404    @Override
405    public List<Task> getAllTaskInstances(String processId, NuxeoPrincipal user, CoreSession session) {
406        List<Task> tasks = new ArrayList<>();
407        List<Task> newTasks;
408        for (TaskProvider taskProvider : tasksProviders.values()) {
409            newTasks = taskProvider.getAllTaskInstances(processId, user, session);
410            if (newTasks != null) {
411                tasks.addAll(newTasks);
412            }
413        }
414        return tasks;
415    }
416
417    @Override
418    public List<Task> getAllTaskInstances(String processId, List<String> actors, CoreSession session) {
419        List<Task> tasks = new ArrayList<>();
420        List<Task> newTasks;
421        for (TaskProvider taskProvider : tasksProviders.values()) {
422            newTasks = taskProvider.getAllTaskInstances(processId, actors, session);
423            if (newTasks != null) {
424                tasks.addAll(newTasks);
425            }
426        }
427        return tasks;
428    }
429
430    @Override
431    public String getTaskRootParentPath(CoreSession coreSession) {
432        GetTaskRootParentPathUnrestricted runner = new GetTaskRootParentPathUnrestricted(coreSession);
433        runner.runUnrestricted();
434        return runner.getParentPath();
435    }
436
437    public class GetTaskRootParentPathUnrestricted extends UnrestrictedSessionRunner {
438
439        protected DocumentModel taskRootDoc;
440
441        public GetTaskRootParentPathUnrestricted(CoreSession session) {
442            super(session);
443        }
444
445        @Override
446        public void run() {
447            DocumentRef pathRef = new PathRef(parentPath);
448            if (session.exists(pathRef)) {
449                taskRootDoc = session.getDocument(pathRef);
450            } else {
451                Path path = new Path(parentPath);
452                taskRootDoc = session.createDocumentModel(path.removeLastSegments(1).toString(), path.lastSegment(),
453                        TaskConstants.TASK_ROOT_TYPE_NAME);
454                taskRootDoc = session.createDocument(taskRootDoc);
455                ACP acp = taskRootDoc.getACP();
456                ACL acl = acp.getOrCreateACL(ACL.LOCAL_ACL);
457                acl.add(new ACE("Everyone", "Everything", false));
458                taskRootDoc.setACP(acp, true);
459                taskRootDoc = session.saveDocument(taskRootDoc);
460            }
461        }
462
463        public DocumentModel getTaskRootDoc() {
464            return taskRootDoc;
465        }
466
467        public String getParentPath() {
468            return taskRootDoc.getPathAsString();
469        }
470    }
471
472    @Override
473    public List<Task> getAllTaskInstances(String processId, String nodeId, CoreSession session) {
474        List<Task> tasks = new ArrayList<>();
475        List<Task> newTasks;
476        for (TaskProvider taskProvider : tasksProviders.values()) {
477            newTasks = taskProvider.getAllTaskInstances(processId, nodeId, session);
478            if (newTasks != null) {
479                tasks.addAll(newTasks);
480            }
481        }
482        return tasks;
483    }
484
485    @Override
486    public void reassignTask(CoreSession session, final String taskId, final List<String> newActors,
487            final String comment, Map<String, Serializable> eventInfo) {
488
489        new UnrestrictedSessionRunner(session) {
490
491            @Override
492            public void run() {
493                DocumentModel taskDoc = session.getDocument(new IdRef(taskId));
494                Task task = taskDoc.getAdapter(Task.class);
495                if (task == null) {
496                    throw new NuxeoException("Invalid taskId: " + taskId);
497                }
498                List<String> currentAssignees = task.getActors();
499                List<String> currentActors = new ArrayList<>();
500                for (String currentAssignee : currentAssignees) {
501                    if (currentAssignee.startsWith(NotificationConstants.GROUP_PREFIX)
502                            || currentAssignee.startsWith(NotificationConstants.USER_PREFIX)) {
503                        // prefixed assignees with "user:" or "group:"
504                        currentActors.add(currentAssignee.substring(currentAssignee.indexOf(":") + 1));
505                    } else {
506                        currentActors.add(currentAssignee);
507                    }
508                }
509                String taskInitator = task.getInitiator();
510
511                // remove ACLs set for current assignees
512                ACP acp = taskDoc.getACP();
513                ACL acl = acp.getOrCreateACL(ACL.LOCAL_ACL);
514                List<ACE> toRemove = new ArrayList<>();
515
516                for (ACE ace : acl.getACEs()) {
517                    if (currentActors.contains(ace.getUsername()) || taskInitator.equals(ace.getUsername())) {
518                        toRemove.add(ace);
519                    }
520                }
521                acl.removeAll(toRemove);
522
523                // grant EVERYTHING on task doc to the new actors
524                List<String> actorIds = new ArrayList<>();
525                for (String actor : newActors) {
526                    if (actor.startsWith(NotificationConstants.GROUP_PREFIX)
527                            || actor.startsWith(NotificationConstants.USER_PREFIX)) {
528                        // prefixed assignees with "user:" or "group:"
529                        actorIds.add(actor.substring(actor.indexOf(":") + 1));
530                    } else {
531                        actorIds.add(actor);
532                    }
533                }
534                for (String actorId : actorIds) {
535                    acl.add(new ACE(actorId, SecurityConstants.EVERYTHING, true));
536                }
537
538                taskDoc.setACP(acp, true);
539                task.setActors(actorIds);
540                String currentUser = session.getPrincipal().getActingUser();
541                task.addComment(currentUser, comment);
542                session.saveDocument(taskDoc);
543
544                List<DocumentModel> docs = new ArrayList<>();
545                for (String string : task.getTargetDocumentsIds()) {
546                    docs.add(session.getDocument(new IdRef(string)));
547
548                }
549                notifyEvent(session, task, docs, TaskEventNames.WORKFLOW_TASK_REASSIGNED, eventInfo, comment,
550                        session.getPrincipal(), actorIds);
551
552            }
553        }.runUnrestricted();
554
555    }
556
557    @Override
558    public void delegateTask(CoreSession session, final String taskId, final List<String> delegatedActors,
559            final String comment, Map<String, Serializable> eventInfo) {
560
561        new UnrestrictedSessionRunner(session) {
562            @Override
563            public void run() {
564                DocumentModel taskDoc = session.getDocument(new IdRef(taskId));
565                Task task = taskDoc.getAdapter(Task.class);
566                if (task == null) {
567                    throw new NuxeoException("Invalid taskId: " + taskId);
568                }
569                // grant EVERYTHING on task doc to the delegated actors
570                List<String> actorIds = new ArrayList<>();
571                ACP acp = taskDoc.getACP();
572                ACL acl = acp.getOrCreateACL(ACL.LOCAL_ACL);
573
574                for (String actor : delegatedActors) {
575                    if (actor.startsWith(NotificationConstants.GROUP_PREFIX)
576                            || actor.startsWith(NotificationConstants.USER_PREFIX)) {
577                        // prefixed assignees with "user:" or "group:"
578                        actorIds.add(actor.substring(actor.indexOf(":") + 1));
579                    } else {
580                        actorIds.add(actor);
581                    }
582                }
583                for (String actorId : actorIds) {
584                    ACE ace = new ACE(actorId, SecurityConstants.EVERYTHING, true);
585                    if (!acl.contains(ace)) {
586                        acl.add(ace);
587                    }
588                }
589                taskDoc.setACP(acp, true);
590
591                List<String> allDelegatedActors = new ArrayList<>(task.getDelegatedActors());
592                for (String actor : actorIds) {
593                    if (!allDelegatedActors.contains(actor)) {
594                        allDelegatedActors.add(actor);
595                    }
596                }
597                task.setDelegatedActors(allDelegatedActors);
598
599                String currentUser = session.getPrincipal().getActingUser();
600                task.addComment(currentUser, comment);
601                session.saveDocument(taskDoc);
602                List<DocumentModel> docs = new ArrayList<>();
603                for (String string : task.getTargetDocumentsIds()) {
604                    docs.add(session.getDocument(new IdRef(string)));
605
606                }
607                notifyEvent(session, task, docs, TaskEventNames.WORKFLOW_TASK_DELEGATED, eventInfo,
608                        String.format("Task delegated by '%s' to '%s'", currentUser, StringUtils.join(actorIds, ","))
609                                + (!StringUtils.isEmpty(comment) ? " with the following comment: " + comment : ""),
610                        session.getPrincipal(), actorIds);
611            }
612
613        }.runUnrestricted();
614    }
615
616    protected void notifyEvent(CoreSession session, Task task, List<DocumentModel> docs, String event,
617            Map<String, Serializable> eventInfo, String comment, NuxeoPrincipal principal, List<String> actorIds) {
618        Map<String, Serializable> eventProperties = new HashMap<>();
619        List<String> notificationRecipients = new ArrayList<>(actorIds);
620        eventProperties.put(NotificationConstants.RECIPIENTS_KEY,
621                notificationRecipients.toArray(new String[notificationRecipients.size()]));
622        if (eventInfo != null) {
623            eventProperties.putAll(eventInfo);
624        }
625        for (DocumentModel doc : docs) {
626            TaskEventNotificationHelper.notifyEvent(session, doc, principal, task, event, eventProperties, comment,
627                    null);
628        }
629    }
630
631    @Override
632    public List<Task> getTaskInstances(DocumentModel dm, List<String> actors, boolean includeDelegatedTasks,
633            CoreSession session) {
634        List<Task> tasks = new ArrayList<>();
635        for (TaskProvider taskProvider : tasksProviders.values()) {
636            tasks.addAll(taskProvider.getTaskInstances(dm, actors, includeDelegatedTasks, session));
637        }
638        return tasks;
639    }
640
641    /**
642     * @since 5.8
643     * @deprecated since 7.4 use
644     *             {@link #createTaskForProcess(CoreSession, NuxeoPrincipal, List, String, String, String, String, String, List, boolean, String, String, Date, Map, String, Map)}
645     *             instead
646     */
647    @Override
648    @Deprecated
649    public List<Task> createTask(CoreSession coreSession, NuxeoPrincipal principal, List<DocumentModel> documents,
650            String taskDocumentType, String taskName, String taskType, String processId, List<String> actorIds,
651            boolean createOneTaskPerActor, String directive, String comment, Date dueDate,
652            Map<String, String> taskVariables, String parentPath, Map<String, Serializable> eventInfo) {
653        return createTaskForProcess(coreSession, principal, documents, taskDocumentType, taskName, taskType, processId,
654                null, actorIds, createOneTaskPerActor, directive, comment, dueDate, taskVariables, parentPath,
655                eventInfo);
656    }
657
658    /**
659     * @since 7.4
660     */
661    @Override
662    public List<Task> createTaskForProcess(CoreSession coreSession, NuxeoPrincipal principal,
663            List<DocumentModel> documents, String taskDocumentType, String taskName, String taskType, String processId,
664            String processName, List<String> actorIds, boolean createOneTaskPerActor, String directive, String comment,
665            Date dueDate, Map<String, String> taskVariables, String parentPath, Map<String, Serializable> eventInfo) {
666        if (StringUtils.isBlank(parentPath)) {
667            parentPath = getTaskRootParentPath(coreSession);
668        }
669        CreateTaskUnrestricted runner = new CreateTaskUnrestricted(coreSession, principal, documents, taskDocumentType,
670                taskName, taskType, processId, processName, actorIds, createOneTaskPerActor, directive, comment,
671                dueDate, taskVariables, parentPath);
672        runner.runUnrestricted();
673
674        List<Task> tasks = runner.getTasks();
675
676        for (Task task : tasks) {
677            // notify
678            notifyEvent(coreSession, task, documents, TaskEventNames.WORKFLOW_TASK_ASSIGNED, eventInfo, comment,
679                    principal, task.getActors());
680        }
681        return tasks;
682    }
683}