001/* 002 * Copyright 2015 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.calendars; 017 018import java.util.ArrayList; 019import java.util.HashMap; 020import java.util.LinkedList; 021import java.util.List; 022import java.util.Map; 023import java.util.Objects; 024import java.util.UUID; 025 026import org.apache.avalon.framework.service.ServiceException; 027import org.apache.avalon.framework.service.ServiceManager; 028import org.apache.commons.lang.BooleanUtils; 029import org.apache.commons.lang3.StringUtils; 030import org.apache.jackrabbit.util.Text; 031 032import org.ametys.core.observation.Event; 033import org.ametys.core.right.RightManager.RightResult; 034import org.ametys.core.ui.Callable; 035import org.ametys.core.user.UserIdentity; 036import org.ametys.plugins.explorer.ModifiableExplorerNode; 037import org.ametys.plugins.explorer.ObservationConstants; 038import org.ametys.plugins.explorer.resources.jcr.JCRResourcesCollection; 039import org.ametys.plugins.repository.AmetysObjectIterable; 040import org.ametys.plugins.repository.AmetysObjectIterator; 041import org.ametys.plugins.repository.ModifiableTraversableAmetysObject; 042import org.ametys.plugins.repository.jcr.DefaultTraversableAmetysObject; 043import org.ametys.plugins.repository.query.QueryHelper; 044import org.ametys.plugins.repository.query.expression.Expression; 045import org.ametys.plugins.repository.query.expression.Expression.Operator; 046import org.ametys.plugins.repository.query.expression.StringExpression; 047import org.ametys.plugins.workspaces.calendars.Calendar.CalendarVisibility; 048import org.ametys.plugins.workspaces.calendars.events.CalendarEvent; 049import org.ametys.plugins.workspaces.calendars.events.CalendarEventJSONHelper; 050import org.ametys.plugins.workspaces.calendars.jcr.JCRCalendar; 051import org.ametys.plugins.workspaces.calendars.jcr.JCRCalendarFactory; 052import org.ametys.plugins.workspaces.calendars.task.TaskCalendar; 053import org.ametys.plugins.workspaces.project.objects.Project; 054import org.ametys.plugins.workspaces.tasks.TasksWorkspaceModule; 055import org.ametys.plugins.workspaces.tasks.WorkspaceTaskDAO; 056 057/** 058 * Calendar DAO 059 */ 060public class CalendarDAO extends AbstractCalendarDAO 061{ 062 /** Avalon Role */ 063 public static final String ROLE = CalendarDAO.class.getName(); 064 065 /** The tasks list JSON helper */ 066 protected CalendarEventJSONHelper _calendarEventJSONHelper; 067 068 /** The task DAO */ 069 protected WorkspaceTaskDAO _taskDAO; 070 071 @Override 072 public void service(ServiceManager manager) throws ServiceException 073 { 074 super.service(manager); 075 _calendarEventJSONHelper = (CalendarEventJSONHelper) manager.lookup(CalendarEventJSONHelper.ROLE); 076 _taskDAO = (WorkspaceTaskDAO) manager.lookup(WorkspaceTaskDAO.ROLE); 077 } 078 079 /** 080 * Get calendar info 081 * @param calendar The calendar 082 * @param recursive True to get data for sub calendars 083 * @param includeEvents True to also include child events 084 * @param useICSFormat true to use ICS Format for dates 085 * @return the calendar data in a map 086 */ 087 public Map<String, Object> getCalendarData(Calendar calendar, boolean recursive, boolean includeEvents, boolean useICSFormat) 088 { 089 Map<String, Object> result = new HashMap<>(); 090 091 result.put("id", calendar.getId()); 092 result.put("title", Text.unescapeIllegalJcrChars(calendar.getName())); 093 result.put("description", calendar.getDescription()); 094 result.put("templateDesc", calendar.getTemplateDescription()); 095 result.put("color", calendar.getColor()); 096 result.put("visibility", calendar.getVisibility().name().toLowerCase()); 097 result.put("public", calendar.getVisibility() == CalendarVisibility.PUBLIC); 098 099 if (calendar instanceof WorkflowAwareCalendar calendarWA) 100 { 101 result.put("workflowName", calendarWA.getWorkflowName()); 102 } 103 104 result.put("isTaskCalendar", calendar instanceof TaskCalendar); 105 result.put("isTaskCalendarDisabled", calendar instanceof TaskCalendar cal && cal.isDisabled()); 106 107 if (recursive) 108 { 109 List<Map<String, Object>> calendarList = new LinkedList<>(); 110 result.put("calendars", calendarList); 111 for (Calendar child : calendar.getChildCalendars()) 112 { 113 calendarList.add(getCalendarData(child, recursive, includeEvents, useICSFormat)); 114 } 115 } 116 117 if (includeEvents) 118 { 119 List<Map<String, Object>> eventList = new LinkedList<>(); 120 result.put("events", eventList); 121 122 for (CalendarEvent event : calendar.getAllEvents()) 123 { 124 eventList.add(_calendarEventJSONHelper.eventAsJson(event, false, useICSFormat)); 125 } 126 } 127 128 result.put("rights", _extractCalendarRightData(calendar)); 129 result.put("token", getCalendarIcsToken(calendar, true)); 130 131 132 return result; 133 } 134 135 /** 136 * Get calendar info 137 * @param calendar The calendar 138 * @return the calendar data in a map 139 */ 140 public Map<String, Object> getCalendarProperties(Calendar calendar) 141 { 142 return getCalendarData(calendar, false, false, false); 143 } 144 /** 145 * Add a calendar 146 * @param inputName The desired name for the calendar 147 * @param color The calendar color 148 * @param isPublic true if the calendar is public 149 * @return The result map with id, parentId and name keys 150 */ 151 @Callable (rights = Callable.CHECKED_BY_IMPLEMENTATION) 152 public Map<String, Object> addCalendar(String inputName, String color, boolean isPublic) 153 { 154 String rootId = _getCalendarRoot(true).getId(); 155 return addCalendar(rootId, inputName, StringUtils.EMPTY, StringUtils.EMPTY, color, isPublic ? CalendarVisibility.PUBLIC.name() : CalendarVisibility.PRIVATE.name(), "calendar-default", false); 156 } 157 158 /** 159 * Add a calendar 160 * @param id The identifier of the parent in which the calendar will be added 161 * @param inputName The desired name for the calendar 162 * @param description The calendar description 163 * @param templateDesc The calendar template description 164 * @param color The calendar color 165 * @param visibility The calendar visibility 166 * @param workflowName The calendar workflow name 167 * @param renameIfExists True to rename if existing 168 * @return The result map with id, parentId and name keys 169 */ 170 public Map<String, Object> addCalendar(String id, String inputName, String description, String templateDesc, String color, String visibility, String workflowName, Boolean renameIfExists) 171 { 172 return addCalendar(_resolver.resolveById(id), inputName, description, templateDesc, color, visibility, workflowName, renameIfExists, true, true); 173 } 174 175 /** 176 * Add a calendar 177 * @param parent The parent in which the calendar will be added 178 * @param inputName The desired name for the calendar 179 * @param description The calendar description 180 * @param templateDesc The calendar template description 181 * @param color The calendar color 182 * @param visibility The calendar visibility 183 * @param workflowName The calendar workflow name 184 * @param renameIfExists True to rename if existing 185 * @param checkRights true to check if the current user have enough rights to create the calendar 186 * @param notify True to notify the calendar creation 187 * @return The result map with id, parentId and name keys 188 */ 189 public Map<String, Object> addCalendar(ModifiableTraversableAmetysObject parent, String inputName, String description, String templateDesc, String color, String visibility, String workflowName, Boolean renameIfExists, Boolean checkRights, boolean notify) 190 { 191 String originalName = Text.escapeIllegalJcrChars(inputName); 192 193 // Check user right 194 if (checkRights) 195 { 196 _checkUserRights(parent, RIGHTS_CALENDAR_ADD); 197 } 198 199 if (BooleanUtils.isNotTrue(renameIfExists) && parent.hasChild(originalName)) 200 { 201 getLogger().warn("Cannot create the calendar with name '" + originalName + "', an object with same name already exists."); 202 return Map.of("message", "already-exist"); 203 } 204 205 if (!_explorerResourcesDAO.checkLock(parent)) 206 { 207 getLogger().warn("User '" + _currentUserProvider.getUser() + "' try to modify the object '" + parent.getName() + "' but it is locked by another user"); 208 return Map.of("message", "locked"); 209 } 210 211 int index = 2; 212 String name = originalName; 213 while (parent.hasChild(name)) 214 { 215 name = originalName + " (" + index + ")"; 216 index++; 217 } 218 219 JCRCalendar calendar = parent.createChild(name, JCRCalendarFactory.CALENDAR_NODETYPE); 220 calendar.setWorkflowName(workflowName); 221 calendar.setDescription(description); 222 calendar.setTemplateDescription(templateDesc); 223 calendar.setColor(color); 224 calendar.setVisibility(StringUtils.isNotEmpty(visibility) ? CalendarVisibility.valueOf(visibility.toUpperCase()) : CalendarVisibility.PRIVATE); 225 parent.saveChanges(); 226 227 // Notify listeners 228 Map<String, Object> eventParams = new HashMap<>(); 229 eventParams.put(ObservationConstants.ARGS_ID, calendar.getId()); 230 eventParams.put(ObservationConstants.ARGS_PARENT_ID, parent.getId()); 231 eventParams.put(ObservationConstants.ARGS_NAME, name); 232 eventParams.put(ObservationConstants.ARGS_PATH, calendar.getPath()); 233 234 if (notify) 235 { 236 _observationManager.notify(new Event(org.ametys.plugins.workspaces.calendars.ObservationConstants.EVENT_CALENDAR_CREATED, _currentUserProvider.getUser(), eventParams)); 237 } 238 239 return getCalendarProperties(calendar); 240 } 241 242 /** 243 * Edit a calendar 244 * @param id The identifier of the calendar 245 * @param inputName The new name 246 * @param templateDesc The new calendar template description 247 * @param color The calendar color 248 * @param isPublic true if the calendar is public 249 * @return The result map with id and name keys 250 */ 251 @Callable (rights = Callable.CHECKED_BY_IMPLEMENTATION) 252 public Map<String, Object> editCalendar(String id, String inputName, String templateDesc, String color, boolean isPublic) 253 { 254 CalendarVisibility visibility = isPublic ? CalendarVisibility.PUBLIC : CalendarVisibility.PRIVATE; 255 256 assert id != null; 257 String rename = Text.escapeIllegalJcrChars(inputName); 258 259 JCRCalendar calendar = _resolver.resolveById(id); 260 261 _checkUserRights(calendar, RIGHTS_CALENDAR_EDIT); 262 263 String name = calendar.getName(); 264 ModifiableTraversableAmetysObject parent = calendar.getParent(); 265 266 if (!StringUtils.equals(rename, name) && parent.hasChild(rename)) 267 { 268 getLogger().warn("Cannot edit the calendar with the new name '" + inputName + "', an object with same name already exists."); 269 return Map.of("message", "already-exist"); 270 } 271 272 if (!_explorerResourcesDAO.checkLock(calendar)) 273 { 274 getLogger().warn("User '" + _currentUserProvider.getUser() + "' try to modify calendar '" + calendar.getName() + "' but it is locked by another user"); 275 return Map.of("message", "locked"); 276 } 277 278 if (!StringUtils.equals(name, rename)) 279 { 280 int index = 2; 281 name = Text.escapeIllegalJcrChars(rename); 282 while (parent.hasChild(name)) 283 { 284 name = rename + " (" + index + ")"; 285 index++; 286 } 287 calendar.rename(name); 288 } 289 290 calendar.setTemplateDescription(templateDesc); 291 calendar.setColor(color); 292 calendar.setVisibility(visibility); 293 294 parent.saveChanges(); 295 296 // Notify listeners 297 Map<String, Object> eventParams = new HashMap<>(); 298 eventParams.put(ObservationConstants.ARGS_ID, calendar.getId()); 299 eventParams.put(ObservationConstants.ARGS_PARENT_ID, parent.getId()); 300 eventParams.put(ObservationConstants.ARGS_NAME, name); 301 eventParams.put(ObservationConstants.ARGS_PATH, calendar.getPath()); 302 303 _observationManager.notify(new Event(org.ametys.plugins.workspaces.calendars.ObservationConstants.EVENT_CALENDAR_UPDATED, _currentUserProvider.getUser(), eventParams)); 304 305 return getCalendarProperties(calendar); 306 } 307 308 /** 309 * Edit the task calendar 310 * @param inputName the input name 311 * @param color the color 312 * @param isPublic <code>true</code> if the calendar is public 313 * @param disabled <code>true</code> if the calendar is disabled 314 * @return the calendar properties 315 * @throws IllegalAccessException if a right error occurred 316 */ 317 @Callable (rights = Callable.CHECKED_BY_IMPLEMENTATION) 318 public Map<String, Object> editTaskCalendar(String inputName, String color, boolean isPublic, boolean disabled) throws IllegalAccessException 319 { 320 Project project = _workspaceHelper.getProjectFromRequest(); 321 TaskCalendar taskCalendar = getTaskCalendar(project, false); 322 323 // Check user right 324 _checkUserRights(_getCalendarRoot(project, false), RIGHTS_CALENDAR_EDIT); 325 326 if (taskCalendar != null) 327 { 328 taskCalendar.rename(inputName); 329 taskCalendar.setColor(color); 330 taskCalendar.setVisibility(isPublic ? CalendarVisibility.PUBLIC : CalendarVisibility.PRIVATE); 331 taskCalendar.disable(disabled); 332 } 333 return getCalendarProperties(taskCalendar); 334 } 335 336 /** 337 * Delete a calendar 338 * @param id The id of the calendar 339 * @return The result map with id, parent id and message keys 340 */ 341 @Callable (rights = Callable.CHECKED_BY_IMPLEMENTATION) 342 public Map<String, Object> deleteCalendar(String id) 343 { 344 Map<String, Object> result = new HashMap<>(); 345 346 assert id != null; 347 348 JCRCalendar calendar = _resolver.resolveById(id); 349 350 _checkUserRights(calendar, RIGHTS_CALENDAR_DELETE); 351 352 if (!_explorerResourcesDAO.checkLock(calendar)) 353 { 354 getLogger().warn("User '" + _currentUserProvider.getUser() + "' try to delete calendar'" + calendar.getName() + "' but it is locked by another user"); 355 result.put("message", "locked"); 356 return result; 357 } 358 359 ModifiableExplorerNode parent = calendar.getParent(); 360 String parentId = parent.getId(); 361 String name = calendar.getName(); 362 String path = calendar.getPath(); 363 364 calendar.remove(); 365 parent.saveChanges(); 366 367 // Notify listeners 368 Map<String, Object> eventParams = new HashMap<>(); 369 eventParams.put(ObservationConstants.ARGS_ID, id); 370 eventParams.put(ObservationConstants.ARGS_PARENT_ID, parentId); 371 eventParams.put(ObservationConstants.ARGS_NAME, name); 372 eventParams.put(ObservationConstants.ARGS_PATH, path); 373 374 _observationManager.notify(new Event(org.ametys.plugins.workspaces.calendars.ObservationConstants.EVENT_CALENDAR_DELETED, _currentUserProvider.getUser(), eventParams)); 375 376 result.put("id", id); 377 result.put("parentId", parentId); 378 379 return result; 380 } 381 382 /** 383 * Get or create the calendar ICS token 384 * @param calendar The calendar 385 * @param createIfNotExisting Create the token if none exists for the given calendar 386 * @return The token 387 */ 388 public String getCalendarIcsToken(Calendar calendar, boolean createIfNotExisting) 389 { 390 String token = calendar.getIcsUrlToken(); 391 392 if (createIfNotExisting && token == null && calendar instanceof JCRCalendar) 393 { 394 token = UUID.randomUUID().toString(); 395 ((JCRCalendar) calendar).setIcsUrlToken(token); 396 ((JCRCalendar) calendar).saveChanges(); 397 } 398 399 return token; 400 } 401 402 /** 403 * Retrieve the calendar for the matching ICS token 404 * @param token The ICS token 405 * @return The calendar, or null if not found 406 */ 407 public Calendar getCalendarFromIcsToken(String token) 408 { 409 if (StringUtils.isEmpty(token)) 410 { 411 return null; 412 } 413 414 Expression expr = new StringExpression(JCRCalendar.CALENDAR_ICS_TOKEN, Operator.EQ, token); 415 String calendarsQuery = QueryHelper.getXPathQuery(null, JCRCalendarFactory.CALENDAR_NODETYPE, expr); 416 AmetysObjectIterable<JCRCalendar> calendars = _resolver.query(calendarsQuery); 417 AmetysObjectIterator<JCRCalendar> calendarsIterator = calendars.iterator(); 418 419 if (calendarsIterator.getSize() > 0) 420 { 421 return calendarsIterator.next(); 422 } 423 424 // Don't find a token in default calendars, so check in the task calendars 425 return _projectManager.getProjects() 426 .stream() 427 .map(p -> this.getTaskCalendar(p, true)) 428 .filter(Objects::nonNull) 429 .filter(c -> c.getIcsUrlToken().equals(token)) 430 .findFirst() 431 .orElse(null); 432 } 433 434 /** 435 * Internal method to extract the data concerning the right of the current user for a calendar 436 * @param calendar The calendar 437 * @return The map of right data. Keys are the rights id, and values indicates whether the current user has the right or not. 438 */ 439 protected Map<String, Object> _extractCalendarRightData(Calendar calendar) 440 { 441 Map<String, Object> rightsData = new HashMap<>(); 442 443 UserIdentity user = _currentUserProvider.getUser(); 444 boolean isTaskCalendar = calendar instanceof TaskCalendar; 445 446 // Add 447 rightsData.put("add-event", !isTaskCalendar && _rightManager.hasRight(user, RIGHTS_EVENT_ADD, calendar) == RightResult.RIGHT_ALLOW); 448 449 // edit - delete 450 rightsData.put("edit", !isTaskCalendar && _rightManager.hasRight(user, RIGHTS_CALENDAR_EDIT, calendar) == RightResult.RIGHT_ALLOW); 451 rightsData.put("delete", !isTaskCalendar && _rightManager.hasRight(user, RIGHTS_CALENDAR_DELETE, calendar) == RightResult.RIGHT_ALLOW); 452 453 return rightsData; 454 } 455 456 /** 457 * Get the data of every available calendar for the current project 458 * @return the list of calendars 459 */ 460 @Callable (rights = Callable.CHECKED_BY_IMPLEMENTATION) 461 public List<Map<String, Object>> getCalendars() 462 { 463 Project project = _workspaceHelper.getProjectFromRequest(); 464 465 _checkReadAccess(project, CalendarWorkspaceModule.CALENDAR_MODULE_ID); 466 467 List<Map<String, Object>> calendarsData = new ArrayList<>(); 468 CalendarWorkspaceModule calendarModule = (CalendarWorkspaceModule) _workspaceModuleEP.getModule(CalendarWorkspaceModule.CALENDAR_MODULE_ID); 469 470 for (Calendar calendar : calendarModule.getCalendars(project, true)) 471 { 472 if (calendarModule.canView(calendar)) 473 { 474 calendarsData.add(this.getCalendarProperties(calendar)); 475 } 476 } 477 478 return calendarsData; 479 } 480 481 /** 482 * Get the colors of calendars 483 * @return colors 484 */ 485 @Callable (rights = Callable.NO_CHECK_REQUIRED) 486 public Map<String, CalendarColorsComponent.CalendarColor> getColors() 487 { 488 return _calendarColors.getColors(); 489 } 490 491 /** 492 * Get user rights on root calendar of current project 493 * @return the user rights 494 */ 495 @Callable (rights = Callable.NO_CHECK_REQUIRED) 496 public Map<String, Object> getUserRights() 497 { 498 Map<String, Object> results = new HashMap<>(); 499 ModifiableTraversableAmetysObject calendarRoot = _getCalendarRoot(false); 500 501 UserIdentity user = _currentUserProvider.getUser(); 502 results.put("canCreateCalendar", calendarRoot != null && _rightManager.hasRight(user, RIGHTS_CALENDAR_ADD, calendarRoot) == RightResult.RIGHT_ALLOW); 503 results.put("canEditCalendar", calendarRoot != null && _rightManager.hasRight(user, RIGHTS_CALENDAR_EDIT, calendarRoot) == RightResult.RIGHT_ALLOW); 504 results.put("canRemoveCalendar", calendarRoot != null && _rightManager.hasRight(user, RIGHTS_CALENDAR_DELETE, calendarRoot) == RightResult.RIGHT_ALLOW); 505 results.put("canCreateEvent", calendarRoot != null && _rightManager.hasRight(user, RIGHTS_EVENT_ADD, calendarRoot) == RightResult.RIGHT_ALLOW); 506 results.put("canEditEvent", calendarRoot != null && _rightManager.hasRight(user, RIGHTS_EVENT_EDIT, calendarRoot) == RightResult.RIGHT_ALLOW); 507 results.put("canRemoveAnyEvent", calendarRoot != null && _rightManager.hasRight(user, RIGHTS_EVENT_DELETE, calendarRoot) == RightResult.RIGHT_ALLOW); 508 results.put("canRemoveSelfEvent", calendarRoot != null && _rightManager.hasRight(user, RIGHTS_EVENT_DELETE_OWN, calendarRoot) == RightResult.RIGHT_ALLOW); 509 results.put("canHandleResource", calendarRoot != null && _rightManager.hasRight(user, RIGHTS_HANDLE_RESOURCE, calendarRoot) == RightResult.RIGHT_ALLOW); 510 results.put("canBookResource", calendarRoot != null && _rightManager.hasRight(user, RIGHTS_BOOK_RESOURCE, calendarRoot) == RightResult.RIGHT_ALLOW); 511 results.put("sharePrivateCalendar", calendarRoot != null && _rightManager.hasRight(user, RIGHTS_EVENT_EDIT, calendarRoot) == RightResult.RIGHT_ALLOW); 512 513 return results; 514 } 515 516 /** 517 * Get the calendar root 518 * @param createIfNotExist true to create root if not exist yet 519 * @return the calendar root 520 */ 521 protected ModifiableTraversableAmetysObject _getCalendarRoot(boolean createIfNotExist) 522 { 523 return _getCalendarRoot(_workspaceHelper.getProjectFromRequest(), createIfNotExist); 524 } 525 526 /** 527 * Get the calendar root form the project 528 * @param project the project 529 * @param createIfNotExist true to create root if not exist yet 530 * @return the calendar root 531 */ 532 protected ModifiableTraversableAmetysObject _getCalendarRoot(Project project, boolean createIfNotExist) 533 { 534 CalendarWorkspaceModule calendarModule = (CalendarWorkspaceModule) _workspaceModuleEP.getModule(CalendarWorkspaceModule.CALENDAR_MODULE_ID); 535 return calendarModule.getCalendarsRoot(project, createIfNotExist); 536 } 537 538 /** 539 * Get the data of calendar used to store resources 540 * @return the calendar used to store resources 541 */ 542 @Callable (rights = Callable.CHECKED_BY_IMPLEMENTATION) 543 public Map<String, Object> getResourceCalendar() 544 { 545 Project project = _workspaceHelper.getProjectFromRequest(); 546 _checkReadAccess(project, CalendarWorkspaceModule.CALENDAR_MODULE_ID); 547 548 CalendarWorkspaceModule calendarModule = (CalendarWorkspaceModule) _workspaceModuleEP.getModule(CalendarWorkspaceModule.CALENDAR_MODULE_ID); 549 Calendar calendar = calendarModule.getResourceCalendar(project); 550 551 return this.getCalendarProperties(calendar); 552 } 553 554 /** 555 * Get the task calendar 556 * @param project the project 557 * @param onlyIfEnabled <code>true</code> to return the task calendar only if it is enabled 558 * @return the task calendar 559 */ 560 public TaskCalendar getTaskCalendar(Project project, boolean onlyIfEnabled) 561 { 562 if (_projectManager.isModuleActivated(project, TasksWorkspaceModule.TASK_MODULE_ID)) 563 { 564 JCRResourcesCollection root = (JCRResourcesCollection) _getCalendarRoot(project, false); 565 TaskCalendar calendar = new TaskCalendar(project, root, _taskDAO); 566 return !onlyIfEnabled || !calendar.isDisabled() ? calendar : null; 567 } 568 569 return null; 570 } 571 572 /** 573 * <code>true</code> if the current user has read access on the task calendar 574 * @param project the project 575 * @return <code>true</code> if the current user has read access on the task calendar 576 */ 577 public boolean hasTaskCalendarReadAccess(Project project) 578 { 579 TasksWorkspaceModule taskModule = (TasksWorkspaceModule) _workspaceModuleEP.getModule(TasksWorkspaceModule.TASK_MODULE_ID); 580 DefaultTraversableAmetysObject tasksRoot = taskModule.getTasksRoot(project, true); 581 return _rightManager.currentUserHasReadAccess(tasksRoot); 582 } 583}