001/* 002 * Copyright 2016 Anyware Services 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 */ 016package org.ametys.plugins.workspaces.tasks; 017 018import java.time.LocalDate; 019import java.time.ZonedDateTime; 020import java.time.format.DateTimeFormatter; 021import java.util.ArrayList; 022import java.util.HashMap; 023import java.util.List; 024import java.util.Map; 025import java.util.Optional; 026import java.util.stream.Collectors; 027 028import org.apache.avalon.framework.service.ServiceException; 029import org.apache.avalon.framework.service.ServiceManager; 030import org.apache.cocoon.servlet.multipart.Part; 031import org.apache.commons.lang.IllegalClassException; 032import org.apache.commons.lang3.StringUtils; 033 034import org.ametys.cms.repository.comment.Comment; 035import org.ametys.core.observation.Event; 036import org.ametys.core.right.RightManager.RightResult; 037import org.ametys.core.ui.Callable; 038import org.ametys.core.user.User; 039import org.ametys.core.user.UserIdentity; 040import org.ametys.plugins.explorer.resources.ModifiableResourceCollection; 041import org.ametys.plugins.repository.AmetysObject; 042import org.ametys.plugins.repository.AmetysRepositoryException; 043import org.ametys.plugins.repository.ModifiableTraversableAmetysObject; 044import org.ametys.plugins.workspaces.ObservationConstants; 045import org.ametys.plugins.workspaces.members.ProjectMemberManager; 046import org.ametys.plugins.workspaces.project.objects.Project; 047import org.ametys.plugins.workspaces.tags.ProjectTagProviderExtensionPoint; 048import org.ametys.plugins.workspaces.tasks.Task.CheckItem; 049import org.ametys.plugins.workspaces.tasks.jcr.JCRTask; 050import org.ametys.plugins.workspaces.tasks.jcr.JCRTaskFactory; 051import org.ametys.plugins.workspaces.tasks.json.TaskJSONHelper; 052 053/** 054 * DAO for interacting with tasks of a project 055 */ 056public class WorkspaceTaskDAO extends AbstractWorkspaceTaskDAO 057{ 058 /** The Avalon role */ 059 public static final String ROLE = WorkspaceTaskDAO.class.getName(); 060 061 /** The project member manager */ 062 protected ProjectMemberManager _projectMemberManager; 063 064 /** The tag provider extension point */ 065 protected ProjectTagProviderExtensionPoint _tagProviderExtPt; 066 067 /** The task JSON helper */ 068 protected TaskJSONHelper _taskJSONHelper; 069 070 /** The task list DAO */ 071 protected WorkspaceTasksListDAO _workspaceTasksListDAO; 072 073 @Override 074 public void service(ServiceManager manager) throws ServiceException 075 { 076 super.service(manager); 077 _projectMemberManager = (ProjectMemberManager) manager.lookup(ProjectMemberManager.ROLE); 078 _tagProviderExtPt = (ProjectTagProviderExtensionPoint) manager.lookup(ProjectTagProviderExtensionPoint.ROLE); 079 _taskJSONHelper = (TaskJSONHelper) manager.lookup(TaskJSONHelper.ROLE); 080 _workspaceTasksListDAO = (WorkspaceTasksListDAO) manager.lookup(WorkspaceTasksListDAO.ROLE); 081 } 082 083 /** 084 * Get the tasks from project 085 * @return the list of tasks 086 * @throws IllegalAccessException If an error occurs when checking the rights 087 */ 088 @Callable 089 public List<Map<String, Object>> getTasks() throws IllegalAccessException 090 { 091 String projectName = _getProjectName(); 092 093 ModifiableResourceCollection moduleRoot = _getModuleRoot(projectName); 094 if (!_rightManager.currentUserHasReadAccess(moduleRoot)) 095 { 096 throw new IllegalAccessException("User '" + _currentUserProvider.getUser() + "' tried to get tasks without reader right"); 097 } 098 099 List<Map<String, Object>> tasksInfo = new ArrayList<>(); 100 Project project = _projectManager.getProject(projectName); 101 for (Task task : getProjectTasks(project)) 102 { 103 tasksInfo.add(_taskJSONHelper.taskAsJSON(task, _getSitemapLanguage(), _getSiteName())); 104 } 105 106 return tasksInfo; 107 } 108 109 /** 110 * Add a new task to the tasks list 111 * @param tasksListId the tasks list id 112 * @param parameters The task parameters 113 * @param newFiles the files to add 114 * @param newFileNames the file names to add 115 * @return The task data 116 * @throws IllegalAccessException If an error occurs when checking the rights 117 */ 118 @Callable 119 public Map<String, Object> addTask(String tasksListId, Map<String, Object> parameters, List<Part> newFiles, List<String> newFileNames) throws IllegalAccessException 120 { 121 String projectName = _getProjectName(); 122 123 if (StringUtils.isBlank(tasksListId)) 124 { 125 throw new IllegalArgumentException("Tasks list id is mandatory to create a new task"); 126 } 127 128 ModifiableTraversableAmetysObject tasksRoot = _getTasksRoot(projectName); 129 130 // Check user right 131 if (_rightManager.hasRight(_currentUserProvider.getUser(), RIGHTS_HANDLE_TASK, tasksRoot) != RightResult.RIGHT_ALLOW) 132 { 133 throw new IllegalAccessException("User '" + _currentUserProvider.getUser() + "' tried to add task without convenient right [" + RIGHTS_HANDLE_TASK + "]"); 134 } 135 136 int index = 1; 137 String name = "task-1"; 138 while (tasksRoot.hasChild(name)) 139 { 140 index++; 141 name = "task-" + index; 142 } 143 144 JCRTask task = (JCRTask) tasksRoot.createChild(name, JCRTaskFactory.TASK_NODETYPE); 145 task.setTasksListId(tasksListId); 146 task.setPosition(Long.valueOf(_workspaceTasksListDAO.getChildTask(tasksListId).size())); 147 148 ZonedDateTime now = ZonedDateTime.now(); 149 task.setCreationDate(now); 150 task.setLastModified(now); 151 task.setAuthor(_currentUserProvider.getUser()); 152 153 Map<String, Object> attributesResults = _setTaskAttributes(task, parameters, newFiles, newFileNames, new ArrayList<>()); 154 155 tasksRoot.saveChanges(); 156 157 Map<String, Object> eventParams = new HashMap<>(); 158 eventParams.put(ObservationConstants.ARGS_TASK, task); 159 eventParams.put(org.ametys.plugins.explorer.ObservationConstants.ARGS_ID, task.getId()); 160 161 _observationManager.notify(new Event(ObservationConstants.EVENT_TASK_CREATED, _currentUserProvider.getUser(), eventParams)); 162 163 Map<String, Object> results = new HashMap<>(); 164 results.put("task", _taskJSONHelper.taskAsJSON(task, _getSitemapLanguage(), _getSiteName())); 165 results.putAll(attributesResults); 166 return results; 167 } 168 169 /** 170 * Edit a task 171 * @param taskId The id of the task to edit 172 * @param parameters The JS parameters 173 * @param newFiles the new file to add 174 * @param newFileNames the file names to add 175 * @param deleteFiles the file to delete 176 * @return The task data 177 * @throws IllegalAccessException If an error occurs when checking the rights 178 */ 179 @Callable 180 public Map<String, Object> editTask(String taskId, Map<String, Object> parameters, List<Part> newFiles, List<String> newFileNames, List<String> deleteFiles) throws IllegalAccessException 181 { 182 AmetysObject object = _resolver.resolveById(taskId); 183 if (!(object instanceof JCRTask)) 184 { 185 throw new IllegalClassException(JCRTask.class, object.getClass()); 186 } 187 188 // Check user right 189 ModifiableTraversableAmetysObject tasksRoot = object.getParent(); 190 if (_rightManager.hasRight(_currentUserProvider.getUser(), RIGHTS_HANDLE_TASK, tasksRoot) != RightResult.RIGHT_ALLOW) 191 { 192 throw new IllegalAccessException("User '" + _currentUserProvider.getUser() + "' tried to edit task without convenient right [" + RIGHTS_HANDLE_TASK + "]"); 193 } 194 195 JCRTask task = (JCRTask) object; 196 Map<String, Object> attributesResults = _setTaskAttributes(task, parameters, newFiles, newFileNames, deleteFiles); 197 198 ZonedDateTime now = ZonedDateTime.now(); 199 task.setLastModified(now); 200 201 task.saveChanges(); 202 203 Map<String, Object> eventParams = new HashMap<>(); 204 eventParams.put(ObservationConstants.ARGS_TASK, task); 205 eventParams.put(org.ametys.plugins.explorer.ObservationConstants.ARGS_ID, taskId); 206 207 _observationManager.notify(new Event(ObservationConstants.EVENT_TASK_UPDATED, _currentUserProvider.getUser(), eventParams)); 208 209 // Closed status has changed 210 if (attributesResults.containsKey("isClosed")) 211 { 212 _observationManager.notify(new Event(ObservationConstants.EVENT_TASK_CLOSED_STATUS_CHANGED, _currentUserProvider.getUser(), eventParams)); 213 } 214 215 // Assigments have changed 216 if (attributesResults.containsKey("changedAssignments")) 217 { 218 _observationManager.notify(new Event(ObservationConstants.EVENT_TASK_ASSIGNED, _currentUserProvider.getUser(), eventParams)); 219 } 220 221 222 Map<String, Object> results = new HashMap<>(); 223 results.put("task", _taskJSONHelper.taskAsJSON(task, _getSitemapLanguage(), _getSiteName())); 224 results.putAll(attributesResults); 225 return results; 226 } 227 228 /** 229 * Move task to new position 230 * @param tasksListId the tasks list id 231 * @param taskId the task id to move 232 * @param newPosition the new position 233 * @return The task data 234 * @throws IllegalAccessException If an error occurs when checking the rights 235 */ 236 @Callable 237 public Map<String, Object> moveTask(String tasksListId, String taskId, long newPosition) throws IllegalAccessException 238 { 239 AmetysObject object = _resolver.resolveById(taskId); 240 if (!(object instanceof Task)) 241 { 242 throw new IllegalClassException(Task.class, object.getClass()); 243 } 244 245 // Check user right 246 ModifiableTraversableAmetysObject tasksRoot = object.getParent(); 247 if (_rightManager.hasRight(_currentUserProvider.getUser(), RIGHTS_HANDLE_TASK, tasksRoot) != RightResult.RIGHT_ALLOW) 248 { 249 throw new IllegalAccessException("User '" + _currentUserProvider.getUser() + "' tried to move task without convenient right [" + RIGHTS_HANDLE_TASK + "]"); 250 } 251 252 Task task = (Task) object; 253 if (tasksListId != task.getTaskListId()) 254 { 255 List<Task> childTasks = _workspaceTasksListDAO.getChildTask(task.getTaskListId()); 256 long position = 0; 257 for (Task childTask : childTasks) 258 { 259 if (!childTask.getId().equals(taskId)) 260 { 261 childTask.setPosition(position); 262 position++; 263 } 264 } 265 } 266 267 task.setTasksListId(tasksListId); 268 List<Task> childTasks = _workspaceTasksListDAO.getChildTask(tasksListId); 269 int size = childTasks.size(); 270 if (newPosition > size) 271 { 272 throw new IllegalArgumentException("New position (" + newPosition + ") can't be greater than tasks child size (" + size + ")"); 273 } 274 275 long position = 0; 276 task.setPosition(newPosition); 277 for (Task childTask : childTasks) 278 { 279 if (position == newPosition) 280 { 281 position++; 282 } 283 284 if (childTask.getId().equals(taskId)) 285 { 286 childTask.setPosition(newPosition); 287 } 288 else 289 { 290 childTask.setPosition(position); 291 position++; 292 } 293 } 294 295 tasksRoot.saveChanges(); 296 297 return _taskJSONHelper.taskAsJSON(task, _getSitemapLanguage(), _getSiteName()); 298 } 299 300 /** 301 * Remove a task 302 * @param taskId the task id to remove 303 * @return The task data 304 * @throws IllegalAccessException If an error occurs when checking the rights 305 */ 306 @Callable 307 public Map<String, Object> deleteTask(String taskId) throws IllegalAccessException 308 { 309 AmetysObject object = _resolver.resolveById(taskId); 310 if (!(object instanceof Task)) 311 { 312 throw new IllegalClassException(Task.class, object.getClass()); 313 } 314 315 // Check user right 316 ModifiableTraversableAmetysObject tasksRoot = object.getParent(); 317 if (_rightManager.hasRight(_currentUserProvider.getUser(), RIGHTS_DELETE_TASK, tasksRoot) != RightResult.RIGHT_ALLOW) 318 { 319 throw new IllegalAccessException("User '" + _currentUserProvider.getUser() + "' tried to delete task without convenient right [" + RIGHTS_DELETE_TASK + "]"); 320 } 321 322 Map<String, Object> results = new HashMap<>(); 323 Task jcrTask = (Task) object; 324 325 Map<String, Object> eventParams = new HashMap<>(); 326 eventParams.put(ObservationConstants.ARGS_TASK, jcrTask); 327 eventParams.put(org.ametys.plugins.explorer.ObservationConstants.ARGS_ID, taskId); 328 _observationManager.notify(new Event(ObservationConstants.EVENT_TASK_DELETING, _currentUserProvider.getUser(), eventParams)); 329 330 String tasksListId = jcrTask.getTaskListId(); 331 jcrTask.remove(); 332 333 // Reorder tasks position 334 long position = 0; 335 for (Task childTask : _workspaceTasksListDAO.getChildTask(tasksListId)) 336 { 337 childTask.setPosition(position); 338 position++; 339 } 340 341 tasksRoot.saveChanges(); 342 343 eventParams = new HashMap<>(); 344 eventParams.put(org.ametys.plugins.explorer.ObservationConstants.ARGS_ID, taskId); 345 _observationManager.notify(new Event(ObservationConstants.EVENT_TASK_DELETED, _currentUserProvider.getUser(), eventParams)); 346 347 return results; 348 } 349 350 /** 351 * Comment a task 352 * @param taskId the task id 353 * @param commentText the comment text 354 * @return The task data 355 * @throws IllegalAccessException If an error occurs when checking the rights 356 */ 357 @Callable 358 public Map<String, Object> commentTask(String taskId, String commentText) throws IllegalAccessException 359 { 360 AmetysObject object = _resolver.resolveById(taskId); 361 if (!(object instanceof Task)) 362 { 363 throw new IllegalClassException(Task.class, object.getClass()); 364 } 365 366 // Check user right 367 ModifiableTraversableAmetysObject tasksRoot = object.getParent(); 368 if (_rightManager.hasRight(_currentUserProvider.getUser(), RIGHTS_COMMENT_TASK, tasksRoot) != RightResult.RIGHT_ALLOW) 369 { 370 throw new IllegalAccessException("User '" + _currentUserProvider.getUser() + "' tried to comment task without convenient right [" + RIGHTS_COMMENT_TASK + "]"); 371 } 372 373 Task task = (Task) object; 374 375 createComment(task, commentText, tasksRoot); 376 377 return _taskJSONHelper.taskAsJSON(task, _getSitemapLanguage(), _getSiteName()); 378 } 379 380 /** 381 * Edit a task comment 382 * @param taskId the task id 383 * @param commentId the comment Id 384 * @param commentText the comment text 385 * @return The task data 386 * @throws IllegalAccessException If an error occurs when checking the rights 387 */ 388 @Callable 389 public Map<String, Object> editCommentTask(String taskId, String commentId, String commentText) throws IllegalAccessException 390 { 391 AmetysObject object = _resolver.resolveById(taskId); 392 if (!(object instanceof Task)) 393 { 394 throw new IllegalClassException(Task.class, object.getClass()); 395 } 396 397 // Check user right 398 ModifiableTraversableAmetysObject tasksRoot = object.getParent(); 399 if (_rightManager.hasRight(_currentUserProvider.getUser(), RIGHTS_COMMENT_TASK, tasksRoot) != RightResult.RIGHT_ALLOW) 400 { 401 throw new IllegalAccessException("User '" + _currentUserProvider.getUser() + "' tried to edit task without convenient right [" + RIGHTS_COMMENT_TASK + "]"); 402 } 403 404 Task task = (Task) object; 405 406 editComment(task, commentId, commentText, tasksRoot); 407 408 return _taskJSONHelper.taskAsJSON(task, _getSitemapLanguage(), _getSiteName()); 409 } 410 411 /** 412 * Answer to a task's comment 413 * @param taskId the task id 414 * @param commentId the comment id 415 * @param commentText the comment text 416 * @return The task data 417 * @throws IllegalAccessException If an error occurs when checking the rights 418 */ 419 @Callable 420 public Map<String, Object> answerCommentTask(String taskId, String commentId, String commentText) throws IllegalAccessException 421 { 422 AmetysObject object = _resolver.resolveById(taskId); 423 if (!(object instanceof Task)) 424 { 425 throw new IllegalClassException(Task.class, object.getClass()); 426 } 427 428 // Check user right 429 ModifiableTraversableAmetysObject tasksRoot = object.getParent(); 430 if (_rightManager.hasRight(_currentUserProvider.getUser(), RIGHTS_COMMENT_TASK, tasksRoot) != RightResult.RIGHT_ALLOW) 431 { 432 throw new IllegalAccessException("User '" + _currentUserProvider.getUser() + "' tried to comment task without convenient right [" + RIGHTS_COMMENT_TASK + "]"); 433 } 434 435 Task task = (Task) object; 436 437 answerComment(task, commentId, commentText, tasksRoot); 438 439 return _taskJSONHelper.taskAsJSON(task, _getSitemapLanguage(), _getSiteName()); 440 } 441 442 /** 443 * Delete a task's comment 444 * @param taskId the task id 445 * @param commentId the comment id 446 * @return The task data 447 * @throws IllegalAccessException If an error occurs when checking the rights 448 */ 449 @Callable 450 public Map<String, Object> deleteCommentTask(String taskId, String commentId) throws IllegalAccessException 451 { 452 AmetysObject object = _resolver.resolveById(taskId); 453 if (!(object instanceof Task)) 454 { 455 throw new IllegalClassException(Task.class, object.getClass()); 456 } 457 458 // Check user right 459 ModifiableTraversableAmetysObject tasksRoot = object.getParent(); 460 461 UserIdentity userIdentity = _currentUserProvider.getUser(); 462 User user = _userManager.getUser(userIdentity); 463 464 Task task = (Task) object; 465 Comment comment = task.getComment(commentId); 466 String authorEmail = comment.getAuthorEmail(); 467 if (!authorEmail.equals(user.getEmail())) 468 { 469 if (_rightManager.hasRight(_currentUserProvider.getUser(), RIGHTS_COMMENT_TASK, tasksRoot) != RightResult.RIGHT_ALLOW) 470 { 471 throw new IllegalAccessException("User '" + userIdentity + "' tried to delete an other user's comment task"); 472 } 473 } 474 475 deleteComment(task, commentId, tasksRoot); 476 477 return _taskJSONHelper.taskAsJSON(task, _getSitemapLanguage(), _getSiteName()); 478 } 479 480 /** 481 * Like or unlike a task's comment 482 * @param taskId the task id 483 * @param commentId the comment id 484 * @param liked true if the comment is liked, otherwise the comment is unliked 485 * @return The task data 486 * @throws IllegalAccessException If an error occurs when checking the rights 487 */ 488 @Callable 489 public Map<String, Object> likeOrUnlikeCommentTask(String taskId, String commentId, Boolean liked) throws IllegalAccessException 490 { 491 AmetysObject object = _resolver.resolveById(taskId); 492 if (!(object instanceof Task)) 493 { 494 throw new IllegalClassException(Task.class, object.getClass()); 495 } 496 497 // Check user right 498 ModifiableTraversableAmetysObject tasksRoot = object.getParent(); 499 if (_rightManager.hasRight(_currentUserProvider.getUser(), RIGHTS_COMMENT_TASK, tasksRoot) != RightResult.RIGHT_ALLOW) 500 { 501 throw new IllegalAccessException("User '" + _currentUserProvider.getUser() + "' tried to react a comment task without convenient right [" + RIGHTS_COMMENT_TASK + "]"); 502 } 503 504 Task task = (Task) object; 505 506 likeOrUnlikeComment(task, commentId, liked, tasksRoot); 507 508 return _taskJSONHelper.taskAsJSON(task, _getSitemapLanguage(), _getSiteName()); 509 } 510 511 /** 512 * Set task's attributes 513 * @param task The task to edit 514 * @param parameters The JS parameters 515 * @param newFiles the new file to add to the task 516 * @param newFileNames the new file names to add to the task 517 * @param deleteFiles the file to remove from the task 518 * @return the map of results 519 */ 520 protected Map<String, Object> _setTaskAttributes(JCRTask task, Map<String, Object> parameters, List<Part> newFiles, List<String> newFileNames, List<String> deleteFiles) 521 { 522 Map<String, Object> results = new HashMap<>(); 523 524 String label = (String) parameters.get(JCRTask.ATTRIBUTE_LABEL); 525 task.setLabel(label); 526 527 String description = (String) parameters.get(JCRTask.ATTRIBUTE_DESCRIPTION); 528 task.setDescription(description); 529 530 _setTaskDates(task, parameters); 531 _setTaskCloseInfo(task, parameters, results); 532 _setAttachments(task, newFiles, newFileNames, deleteFiles); 533 534 @SuppressWarnings("unchecked") 535 List<Map<String, Object>> assignmentIds = (List<Map<String, Object>>) parameters.getOrDefault(JCRTask.ATTRIBUTE_ASSIGNMENTS, new ArrayList<>()); 536 List<UserIdentity> users = assignmentIds.stream() 537 .map(m -> (String) m.get("id")) 538 .map(UserIdentity::stringToUserIdentity) 539 .collect(Collectors.toList()); 540 541 if (!task.getAssignments().equals(users)) 542 { 543 task.setAssignments(users); 544 results.put("changedAssignments", true); 545 } 546 547 @SuppressWarnings("unchecked") 548 List<Map<String, Object>> checkListItems = (List<Map<String, Object>>) parameters.getOrDefault(JCRTask.ATTRIBUTE_CHECKLIST, new ArrayList<>()); 549 List<CheckItem> checkItems = checkListItems.stream() 550 .map(e -> new CheckItem((String) e.get(JCRTask.ATTRIBUTE_CHECKLIST_LABEL), (boolean) e.get(JCRTask.ATTRIBUTE_CHECKLIST_ISCHECKED))) 551 .collect(Collectors.toList()); 552 task.setCheckListItem(checkItems); 553 554 @SuppressWarnings("unchecked") 555 List<Object> tags = (List<Object>) parameters.getOrDefault(JCRTask.ATTRIBUTE_TAGS, new ArrayList<>()); 556 557 List<Map<String, Object>> createdTagsJson = _handleTags(task, tags); 558 559 results.put("newTags", createdTagsJson); 560 return results; 561 } 562 563 private void _setTaskDates(JCRTask task, Map<String, Object> parameters) 564 { 565 String startDateAsStr = (String) parameters.get(JCRTask.ATTRIBUTE_STARTDATE); 566 LocalDate startDate = Optional.ofNullable(startDateAsStr) 567 .map(date -> LocalDate.parse(date, DateTimeFormatter.ISO_LOCAL_DATE)) 568 .orElse(null); 569 task.setStartDate(startDate); 570 571 String dueDateAsStr = (String) parameters.get(JCRTask.ATTRIBUTE_DUEDATE); 572 LocalDate dueDate = Optional.ofNullable(dueDateAsStr) 573 .map(date -> LocalDate.parse(date, DateTimeFormatter.ISO_LOCAL_DATE)) 574 .orElse(null); 575 task.setDueDate(dueDate); 576 } 577 578 private void _setTaskCloseInfo(JCRTask task, Map<String, Object> parameters, Map<String, Object> results) 579 { 580 @SuppressWarnings("unchecked") 581 Map<String, Object> closeInfo = (Map<String, Object>) parameters.get("closeInfo"); 582 if (closeInfo != null && !task.isClosed()) 583 { 584 task.close(true); 585 task.setCloseAuthor(_currentUserProvider.getUser()); 586 task.setCloseDate(LocalDate.now()); 587 588 results.put("isClosed", true); 589 } 590 else if (closeInfo == null && task.isClosed()) 591 { 592 task.close(false); 593 task.setCloseAuthor(null); 594 task.setCloseDate(null); 595 596 results.put("isClosed", false); 597 } 598 } 599 600 601 /** 602 * Get all tasks from given projets 603 * @param project the project 604 * @return All tasks as JSON 605 */ 606 public List<Task> getProjectTasks(Project project) 607 { 608 TasksWorkspaceModule taskModule = _workspaceModuleEP.getModule(TasksWorkspaceModule.TASK_MODULE_ID); 609 ModifiableTraversableAmetysObject tasksRoot = taskModule.getTasksRoot(project, true); 610 return tasksRoot.getChildren() 611 .stream() 612 .filter(Task.class::isInstance) 613 .map(Task.class::cast) 614 .collect(Collectors.toList()); 615 } 616 617 /** 618 * Get the total number of tasks of the project 619 * @param project The project 620 * @return The number of tasks, or null if the module is not activated 621 */ 622 public Long getTasksCount(Project project) 623 { 624 return Long.valueOf(getProjectTasks(project).size()); 625 } 626 627 /** 628 * Get project members 629 * @return the project members 630 * @throws IllegalAccessException if an error occurred 631 * @throws AmetysRepositoryException if an error occurred 632 */ 633 @Callable 634 public Map<String, Object> getProjectMembers() throws IllegalAccessException, AmetysRepositoryException 635 { 636 String projectName = _getProjectName(); 637 String lang = _getSitemapLanguage(); 638 639 return _projectMemberManager.getProjectMembers(projectName, lang, true); 640 } 641}