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