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