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.explorer.calendars.actions; 017 018import java.time.ZonedDateTime; 019import java.time.format.DateTimeFormatter; 020import java.util.ArrayList; 021import java.util.Date; 022import java.util.GregorianCalendar; 023import java.util.HashMap; 024import java.util.LinkedList; 025import java.util.List; 026import java.util.Map; 027import java.util.Set; 028 029import javax.jcr.RepositoryException; 030 031import org.apache.avalon.framework.component.Component; 032import org.apache.avalon.framework.logger.AbstractLogEnabled; 033import org.apache.avalon.framework.service.ServiceException; 034import org.apache.avalon.framework.service.ServiceManager; 035import org.apache.avalon.framework.service.Serviceable; 036import org.apache.commons.lang.BooleanUtils; 037import org.apache.commons.lang.IllegalClassException; 038import org.apache.commons.lang3.StringUtils; 039import org.apache.jackrabbit.util.Text; 040 041import org.ametys.core.observation.Event; 042import org.ametys.core.observation.ObservationManager; 043import org.ametys.core.right.RightManager; 044import org.ametys.core.right.RightManager.RightResult; 045import org.ametys.core.ui.Callable; 046import org.ametys.core.user.CurrentUserProvider; 047import org.ametys.core.user.User; 048import org.ametys.core.user.UserIdentity; 049import org.ametys.core.user.UserManager; 050import org.ametys.core.util.DateUtils; 051import org.ametys.plugins.explorer.ExplorerNode; 052import org.ametys.plugins.explorer.ModifiableExplorerNode; 053import org.ametys.plugins.explorer.ObservationConstants; 054import org.ametys.plugins.explorer.calendars.Calendar; 055import org.ametys.plugins.explorer.calendars.Calendar.CalendarVisibility; 056import org.ametys.plugins.explorer.calendars.CalendarEvent; 057import org.ametys.plugins.explorer.calendars.ModifiableCalendar; 058import org.ametys.plugins.explorer.calendars.ModifiableCalendarEvent; 059import org.ametys.plugins.explorer.calendars.jcr.JCRCalendar; 060import org.ametys.plugins.explorer.calendars.jcr.JCRCalendarEvent; 061import org.ametys.plugins.explorer.calendars.jcr.JCRCalendarFactory; 062import org.ametys.plugins.explorer.resources.ModifiableResourceCollection; 063import org.ametys.plugins.explorer.resources.actions.ExplorerResourcesDAO; 064import org.ametys.plugins.explorer.workflow.AbstractExplorerNodeWorkflowComponent; 065import org.ametys.plugins.repository.AmetysObject; 066import org.ametys.plugins.repository.AmetysObjectIterable; 067import org.ametys.plugins.repository.AmetysObjectResolver; 068import org.ametys.plugins.repository.AmetysRepositoryException; 069import org.ametys.plugins.repository.ModifiableTraversableAmetysObject; 070import org.ametys.plugins.workflow.support.WorkflowHelper; 071import org.ametys.plugins.workflow.support.WorkflowProvider; 072import org.ametys.runtime.i18n.I18nizableText; 073 074import com.opensymphony.workflow.Workflow; 075import com.opensymphony.workflow.WorkflowException; 076import com.opensymphony.workflow.spi.Step; 077 078/** 079 * Calendar DAO 080 */ 081public class CalendarDAO extends AbstractLogEnabled implements Serviceable, Component 082{ 083 /** Avalon Role */ 084 public static final String ROLE = CalendarDAO.class.getName(); 085 086 /** Right to add a calendar */ 087 public static final String RIGHTS_CALENDAR_ADD = "Plugin_Explorer_Calendar_Add"; 088 /** Right to edit a calendar */ 089 public static final String RIGHTS_CALENDAR_EDIT = "Plugin_Explorer_Calendar_Edit"; 090 /** Right to delete a calendar */ 091 public static final String RIGHTS_CALENDAR_DELETE = "Plugin_Explorer_Calendar_Delete"; 092 /** Right to add a event */ 093 public static final String RIGHTS_EVENT_ADD = "Plugin_Explorer_Event_Add"; 094 /** Right to edit a event */ 095 public static final String RIGHTS_EVENT_EDIT = "Plugin_Explorer_Event_Edit"; 096 /** Right to propose a event */ 097 public static final String RIGHTS_EVENT_PROPOSE = "Plugin_Explorer_Event_Propose"; 098 /** Right to validate a event */ 099 public static final String RIGHTS_EVENT_VALIDATE = "Plugin_Explorer_Event_Validate"; 100 /** Right to refuse a event */ 101 public static final String RIGHTS_EVENT_REFUSE = "Plugin_Explorer_Event_Refuse"; 102 /** Right to delete a event */ 103 public static final String RIGHTS_EVENT_DELETE = "Plugin_Explorer_Event_Delete"; 104 /** Right to delete_own a event */ 105 public static final String RIGHTS_EVENT_DELETE_OWN = "Plugin_Explorer_Owned_Event_Delete"; 106 107 /** Explorer resources DAO */ 108 protected ExplorerResourcesDAO _explorerResourcesDAO; 109 110 /** Ametys resolver */ 111 protected AmetysObjectResolver _resolver; 112 113 /** Observer manager. */ 114 protected ObservationManager _observationManager; 115 116 /** The current user provider. */ 117 protected CurrentUserProvider _currentUserProvider; 118 119 /** The rights manager */ 120 protected RightManager _rightManager; 121 122 /** User manager */ 123 protected UserManager _userManager; 124 125 /** The workflow provider */ 126 protected WorkflowProvider _workflowProvider; 127 128 /** The workflow helper */ 129 protected WorkflowHelper _workflowHelper; 130 131 132 public void service(ServiceManager manager) throws ServiceException 133 { 134 _explorerResourcesDAO = (ExplorerResourcesDAO) manager.lookup(ExplorerResourcesDAO.ROLE); 135 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 136 _observationManager = (ObservationManager) manager.lookup(ObservationManager.ROLE); 137 _currentUserProvider = (CurrentUserProvider) manager.lookup(CurrentUserProvider.ROLE); 138 _rightManager = (RightManager) manager.lookup(RightManager.ROLE); 139 _userManager = (UserManager) manager.lookup(UserManager.ROLE); 140 _workflowProvider = (WorkflowProvider) manager.lookup(WorkflowProvider.ROLE); 141 _workflowHelper = (WorkflowHelper) manager.lookup(WorkflowHelper.ROLE); 142 } 143 144 /** 145 * Get calendar info 146 * @param id The calendar id 147 * @param recursive True to get data for sub calendars 148 * @param includeEvents True to also include child events 149 * @return the calendar data in a map 150 */ 151 @Callable 152 public Map<String, Object> getCalendarData(String id, boolean recursive, boolean includeEvents) 153 { 154 Calendar calendar = (Calendar) _resolver.resolveById(id); 155 return getCalendarData(calendar, recursive, includeEvents); 156 } 157 158 /** 159 * Get calendar info 160 * @param calendar The calendar 161 * @param recursive True to get data for sub calendars 162 * @param includeEvents True to also include child events 163 * @return the calendar data in a map 164 */ 165 public Map<String, Object> getCalendarData(Calendar calendar, boolean recursive, boolean includeEvents) 166 { 167 Map<String, Object> result = new HashMap<>(); 168 169 result.put("id", calendar.getId()); 170 result.put("title", Text.unescapeIllegalJcrChars(calendar.getName())); 171 result.put("description", calendar.getDescription()); 172 result.put("templateDesc", calendar.getTemplateDescription()); 173 result.put("color", calendar.getColor()); 174 result.put("visibility", calendar.getVisibility().name().toLowerCase()); 175 result.put("workflowName", calendar.getWorkflowName()); 176 177 if (recursive) 178 { 179 List<Map<String, Object>> calendarList = new LinkedList<>(); 180 result.put("calendars", calendarList); 181 182 AmetysObjectIterable<AmetysObject> children = calendar.getChildren(); 183 for (AmetysObject child : children) 184 { 185 if (child instanceof Calendar) 186 { 187 calendarList.add(getCalendarData((Calendar) child, recursive, includeEvents)); 188 } 189 } 190 } 191 192 if (includeEvents) 193 { 194 List<Map<String, Object>> eventList = new LinkedList<>(); 195 result.put("events", eventList); 196 197 AmetysObjectIterable<AmetysObject> children = calendar.getChildren(); 198 for (AmetysObject child : children) 199 { 200 if (child instanceof CalendarEvent) 201 { 202 eventList.add(getEventData((CalendarEvent) child, false)); 203 } 204 } 205 } 206 207 return result; 208 } 209 210 /** 211 * Get the template description of a calendar 212 * @param calendarId The identifier of the calendar 213 * @return The template description 214 */ 215 @Callable 216 public String getTemplateDescription(String calendarId) 217 { 218 Calendar calendar = _resolver.resolveById(calendarId); 219 return StringUtils.defaultString(calendar.getTemplateDescription()); 220 } 221 222 /** 223 * Get event info 224 * @param ids The event ids 225 * @param fullInfo true to include full info (rights, parent id, etc...) 226 * @return the list of event data 227 */ 228 @Callable 229 public List<Map<String, Object>> getEventsDataByIds(List<String> ids, boolean fullInfo) 230 { 231 List<CalendarEvent> events = new LinkedList<>(); 232 for (String id : ids) 233 { 234 events.add((CalendarEvent) _resolver.resolveById(id)); 235 } 236 237 return getEventsData(events, fullInfo); 238 } 239 240 /** 241 * Get event info 242 * @param events The events 243 * @param fullInfo true to include full info (rights, parent id, etc...) 244 * @return the list of event data 245 */ 246 public List<Map<String, Object>> getEventsData(List<CalendarEvent> events, boolean fullInfo) 247 { 248 List<Map<String, Object>> result = new LinkedList<>(); 249 250 for (CalendarEvent event : events) 251 { 252 result.add(getEventData(event, fullInfo)); 253 } 254 255 return result; 256 } 257 258 /** 259 * Get event info 260 * @param id The event id 261 * @param fullInfo true to include full info (rights, parent id, etc...) 262 * @return the event data in a map 263 */ 264 @Callable 265 public Map<String, Object> getEventDataById(String id, boolean fullInfo) 266 { 267 CalendarEvent event = (CalendarEvent) _resolver.resolveById(id); 268 return getEventData(event, fullInfo); 269 } 270 271 /** 272 * Get event info for a specific occurrence 273 * @param id The event id 274 * @param occurrence a string representing the occurrence date (ISO format). 275 * @param fullInfo true to include full info (rights, parent id, etc...) 276 * @return the event data in a map 277 */ 278 @Callable 279 public Map<String, Object> getEventDataById(String id, String occurrence, boolean fullInfo) 280 { 281 CalendarEvent event = (CalendarEvent) _resolver.resolveById(id); 282 Date occurrenceDate = DateUtils.parse(occurrence); 283 284 return getEventData(event, occurrenceDate, fullInfo); 285 } 286 287 /** 288 * Get event info for a specific occurrence 289 * @param event The event 290 * @param occurrenceDate the occurrence 291 * @param fullInfo true to include full info (rights, parent id, etc...) 292 * @return the event data in a map 293 */ 294 public Map<String, Object> getEventData(CalendarEvent event, Date occurrenceDate, boolean fullInfo) 295 { 296 Map<String, Object> eventData = getEventData(event, fullInfo); 297 298 if (occurrenceDate != null) 299 { 300 // replace id, start and end date with specific occurrence data 301 eventData.putAll(getEventOccurrenceData(event, occurrenceDate)); 302 } 303 304 return eventData; 305 } 306 307 /** 308 * Get event info 309 * @param event The event 310 * @param fullInfo true to include full info (rights, parent id, etc...) 311 * @return the event data in a map 312 */ 313 public Map<String, Object> getEventData(CalendarEvent event, boolean fullInfo) 314 { 315 Calendar calendar = event.getParent(); 316 Map<String, Object> result = new HashMap<>(); 317 318 result.put("id", event.getId()); 319 result.put("calendarId", event.getParent().getId()); 320 result.put("color", calendar.getColor()); 321 322 result.put("title", event.getTitle()); 323 result.put("description", event.getDescription()); 324 result.put("fullDay", event.getFullDay()); 325 result.put("recurrenceType", event.getRecurrenceType().toString()); 326 327 result.put("location", event.getLocation()); 328 result.put("keywords", event.getKeywords()); 329 330 Date untilDate = event.getRepeatUntil(); 331 if (untilDate != null) 332 { 333 result.put("untilDate", DateUtils.dateToString(untilDate)); 334 } 335 336 Date startDateEvent = event.getStartDate(); 337 Date endDateEvent = event.getEndDate(); 338 339 if (event.getFullDay()) 340 { 341 GregorianCalendar gcStart = new GregorianCalendar(); 342 gcStart.setTime(startDateEvent); 343 gcStart.set(java.util.Calendar.HOUR, 0); 344 gcStart.set(java.util.Calendar.MINUTE, 0); 345 gcStart.set(java.util.Calendar.SECOND, 0); 346 gcStart.set(java.util.Calendar.MILLISECOND, 0); 347 startDateEvent = gcStart.getTime(); 348 349 GregorianCalendar gcEnd = new GregorianCalendar(); 350 gcEnd.setTime(endDateEvent); 351 gcEnd.set(java.util.Calendar.HOUR, 23); 352 gcEnd.set(java.util.Calendar.MINUTE, 59); 353 gcEnd.set(java.util.Calendar.SECOND, 59); 354 gcEnd.set(java.util.Calendar.MILLISECOND, 999); 355 endDateEvent = gcEnd.getTime(); 356 357 GregorianCalendar gcEndPlus1 = new GregorianCalendar(); 358 gcEndPlus1.setTime(endDateEvent); 359 gcEndPlus1.add(java.util.Calendar.DAY_OF_YEAR, 1); 360 gcEndPlus1.set(java.util.Calendar.HOUR, 0); 361 gcEndPlus1.set(java.util.Calendar.MINUTE, 0); 362 gcEndPlus1.set(java.util.Calendar.SECOND, 0); 363 gcEndPlus1.set(java.util.Calendar.MILLISECOND, 0); 364 365 long milis = gcEndPlus1.getTimeInMillis() - gcStart.getTimeInMillis(); 366 Integer nbDays = Math.round(milis / (1000 * 60 * 60 * 24)); 367 368 result.put("nbDays", nbDays.intValue()); 369 result.put("endDateNextDay", DateUtils.dateToString(gcEndPlus1.getTime())); 370 } 371 372 result.put("startDate", DateUtils.dateToString(startDateEvent)); 373 result.put("endDate", DateUtils.dateToString(endDateEvent)); 374 375 //excluded occurences 376 List<Date> excludedOccurences = event.getExcludedOccurences(); 377 if (excludedOccurences != null && !excludedOccurences.isEmpty()) 378 { 379 List<String> excludedOccurencesStrings = new ArrayList<>(); 380 for (Date excludedOccurence : excludedOccurences) 381 { 382 excludedOccurencesStrings.add(DateUtils.dateToString(excludedOccurence)); 383 } 384 result.put("excludedDates", excludedOccurencesStrings); 385 } 386 387 // creator 388 UserIdentity creatorIdentity = event.getCreator(); 389 User creator = _userManager.getUser(creatorIdentity); 390 391 result.put("creator", creatorIdentity); 392 result.put("creatorFullName", creator != null ? creator.getFullName() : creatorIdentity.getLogin()); 393 result.put("creationDate", DateUtils.dateToString(event.getCreationDate())); 394 395 // last modification 396 UserIdentity contributorIdentity = event.getLastContributor(); 397 User contributor = _userManager.getUser(contributorIdentity); 398 399 result.put("contributor", contributorIdentity); 400 result.put("contributorFullName", contributor != null ? contributor.getFullName() : contributorIdentity.getLogin()); 401 result.put("lastModified", DateUtils.dateToString(event.getLastModified())); 402 403 // last validation 404 UserIdentity validatorIdentity = event.getLastValidator(); 405 406 if (validatorIdentity != null) 407 { 408 result.put("validator", validatorIdentity); 409 User validator = _userManager.getUser(validatorIdentity); 410 result.put("validatorFullName", validator != null ? validator.getFullName() : validatorIdentity.getLogin()); 411 } 412 413 Date lastValidated = event.getLastValidated(); 414 if (lastValidated != null) 415 { 416 result.put("lastValidated", DateUtils.dateToString(lastValidated)); 417 } 418 419 Workflow workflow = _workflowProvider.getAmetysObjectWorkflow(event); 420 long workflowId = event.getWorkflowId(); 421 result.put("workflowId", workflowId); 422 result.put("workflowName", workflow.getWorkflowName(workflowId)); 423 424 List<Step> listSteps = workflow.getCurrentSteps(event.getWorkflowId()); 425 if (!listSteps.isEmpty()) 426 { 427 // We select the first current step 428 Step step = listSteps.get(0); 429 String stepName = _workflowHelper.getStepName(calendar.getWorkflowName(), step.getStepId()); 430 String[] nameParts = stepName.split(":"); 431 432 result.put("stepId", step.getStepId()); 433 result.put("stepName", nameParts[nameParts.length - 1]); // step name without the possible catalog name 434 435 I18nizableText workflowStepName = new I18nizableText("application", stepName); 436 437 if ("application".equals(workflowStepName.getCatalogue())) 438 { 439 result.put("icon-small-workflow", "/plugins/explorer/resources_workflow/" + workflowStepName.getKey() + "-small.png"); 440 } 441 else 442 { 443 String pluginName = workflowStepName.getCatalogue().substring("plugin.".length()); 444 result.put("icon-small-workflow", "/plugins/" + pluginName + "/resources/img/workflow/" + workflowStepName.getKey() + "-small.png"); 445 } 446 } 447 448 if (fullInfo) 449 { 450 result.putAll(_getEventDataFullInfo(event)); 451 } 452 453 return result; 454 } 455 456 /** 457 * Retrieves the event additional info (rights, parent id, etc...) 458 * @param event The event 459 * @return the event additional info (rights, parent id, etc...) in a map 460 */ 461 protected Map<String, Object> _getEventDataFullInfo(CalendarEvent event) 462 { 463 Map<String, Object> result = new HashMap<>(); 464 465 ExplorerNode explorerNode = event.getParent(); 466 ExplorerNode root = explorerNode; 467 while (true) 468 { 469 if (root.getParent() instanceof ExplorerNode) 470 { 471 root = root.getParent(); 472 } 473 else 474 { 475 break; 476 } 477 } 478 result.put("rootId", root.getId()); 479 result.put("parentId", explorerNode.getId()); 480 result.put("name", event.getName()); 481 result.put("path", explorerNode.getExplorerPath()); 482 result.put("isModifiable", true); 483 484 result.put("rights", _getUserRights(explorerNode)); 485 486 return result; 487 } 488 489 /** 490 * Get the user rights on the resource collection 491 * @param node The explorer node 492 * @return The user's rights 493 */ 494 protected Set<String> _getUserRights(ExplorerNode node) 495 { 496 return _rightManager.getUserRights(_currentUserProvider.getUser(), node); 497 } 498 499 /** 500 * Get info about the occurrence of an event 501 * @param event The event 502 * @param date The start date of the occurrence of the event 503 * @return the event occurrence data in a map 504 */ 505 public Map<String, Object> getEventOccurrenceData(CalendarEvent event, Date date) 506 { 507 Map<String, Object> result = new HashMap<>(); 508 509 String occurrenceDateIso = DateUtils.dateToString(date); 510 result.put("id", event.getId() + "$" + occurrenceDateIso); 511 result.put("occurrenceDate", occurrenceDateIso); 512 513 Date firstStartDateEvent = event.getStartDate(); 514 Date firstEndDateEvent = event.getEndDate(); 515 516 long diff = firstEndDateEvent.getTime() - firstStartDateEvent.getTime(); 517 Date startDateEvent = date; 518 Date endDateEvent = new Date(date.getTime() + diff); 519 520 if (event.getFullDay()) 521 { 522 GregorianCalendar gcStart = new GregorianCalendar(); 523 gcStart.setTime(startDateEvent); 524 gcStart.set(java.util.Calendar.HOUR_OF_DAY, 0); 525 gcStart.set(java.util.Calendar.MINUTE, 0); 526 gcStart.set(java.util.Calendar.SECOND, 0); 527 gcStart.set(java.util.Calendar.MILLISECOND, 0); 528 529 startDateEvent = gcStart.getTime(); 530 531 GregorianCalendar gcEnd = new GregorianCalendar(); 532 gcEnd.setTime(endDateEvent); 533 gcEnd.set(java.util.Calendar.HOUR_OF_DAY, 23); 534 gcEnd.set(java.util.Calendar.MINUTE, 59); 535 gcEnd.set(java.util.Calendar.SECOND, 59); 536 gcEnd.set(java.util.Calendar.MILLISECOND, 999); 537 538 endDateEvent = gcEnd.getTime(); 539 } 540 541 result.put("startDate", DateUtils.dateToString(startDateEvent)); 542 result.put("endDate", DateUtils.dateToString(endDateEvent)); 543 544 return result; 545 } 546 547 /** 548 * Add a calendar 549 * @param id The identifier of the parent in which the calendar will be added 550 * @param inputName The desired name for the calendar 551 * @param description The calendar description 552 * @param templateDesc The calendar template description 553 * @param color The calendar color 554 * @param visibility The calendar visibility 555 * @param workflowName The calendar workflow name 556 * @param renameIfExists True to rename if existing 557 * @return The result map with id, parentId and name keys 558 * @throws IllegalAccessException If the user has no sufficient rights 559 */ 560 @Callable 561 public Map<String, Object> addCalendar(String id, String inputName, String description, String templateDesc, String color, String visibility, String workflowName, Boolean renameIfExists) throws IllegalAccessException 562 { 563 return addCalendar(id, inputName, description, templateDesc, color, visibility, workflowName, renameIfExists, true); 564 } 565 566 /** 567 * Add a calendar 568 * @param id The identifier of the parent in which the calendar will be added 569 * @param inputName The desired name for the calendar 570 * @param description The calendar description 571 * @param templateDesc The calendar template description 572 * @param color The calendar color 573 * @param visibility The calendar visibility 574 * @param workflowName The calendar workflow name 575 * @param renameIfExists True to rename if existing 576 * @param checkRights true to check if the current user have enough rights to create the calendar 577 * @return The result map with id, parentId and name keys 578 * @throws IllegalAccessException If the user has no sufficient rights 579 */ 580 public Map<String, Object> addCalendar(String id, String inputName, String description, String templateDesc, String color, String visibility, String workflowName, Boolean renameIfExists, Boolean checkRights) throws IllegalAccessException 581 { 582 Map<String, Object> result = new HashMap<>(); 583 584 String originalName = Text.escapeIllegalJcrChars(inputName); 585 assert id != null; 586 587 AmetysObject object = _resolver.resolveById(id); 588 if (!(object instanceof ModifiableResourceCollection || object instanceof Calendar)) 589 { 590 throw new IllegalClassException(ModifiableResourceCollection.class, object.getClass()); 591 } 592 593 ModifiableTraversableAmetysObject parent = (ModifiableTraversableAmetysObject) object; 594 595 // Check user right 596 if (checkRights) 597 { 598 _explorerResourcesDAO.checkUserRight(object, RIGHTS_CALENDAR_ADD); 599 } 600 601 if (BooleanUtils.isNotTrue(renameIfExists) && parent.hasChild(originalName)) 602 { 603 getLogger().warn("Cannot create the calendar with name '" + originalName + "', an object with same name already exists."); 604 result.put("message", "already-exist"); 605 return result; 606 } 607 608 if (!_explorerResourcesDAO.checkLock(parent)) 609 { 610 getLogger().warn("User '" + _currentUserProvider.getUser() + "' try to modify the object '" + object.getName() + "' but it is locked by another user"); 611 result.put("message", "locked"); 612 return result; 613 } 614 615 int index = 2; 616 String name = originalName; 617 while (parent.hasChild(name)) 618 { 619 name = originalName + " (" + index + ")"; 620 index++; 621 } 622 623 JCRCalendar calendar = parent.createChild(name, JCRCalendarFactory.CALENDAR_NODETYPE); 624 calendar.setWorkflowName(workflowName); 625 calendar.setDescription(description); 626 calendar.setTemplateDescription(templateDesc); 627 calendar.setColor(color); 628 calendar.setVisibility(StringUtils.isNotEmpty(visibility) ? CalendarVisibility.valueOf(visibility.toUpperCase()) : CalendarVisibility.PRIVATE); 629 parent.saveChanges(); 630 631 // Notify listeners 632 Map<String, Object> eventParams = new HashMap<>(); 633 eventParams.put(ObservationConstants.ARGS_ID, calendar.getId()); 634 eventParams.put(ObservationConstants.ARGS_PARENT_ID, id); 635 eventParams.put(ObservationConstants.ARGS_NAME, name); 636 eventParams.put(ObservationConstants.ARGS_PATH, calendar.getPath()); 637 638 _observationManager.notify(new Event(ObservationConstants.EVENT_CALENDAR_CREATED, _currentUserProvider.getUser(), eventParams)); 639 640 result.put("id", calendar.getId()); 641 result.put("parentId", id); 642 result.put("name", Text.unescapeIllegalJcrChars(name)); 643 644 return result; 645 } 646 647 /** 648 * Edit a calendar 649 * @param id The identifier of the calendar 650 * @param inputName The new name 651 * @param description The new description 652 * @param templateDesc The new calendar template description 653 * @param color The calendar color 654 * @param visibility The calendar visibility 655 * @param workflowName The calendar workflow name 656 * @param renameIfExists True to rename if existing 657 * @param fullEdit true to allow full edition, otherwise only the name will be changed 658 * @return The result map with id and name keys 659 * @throws IllegalAccessException If the user has no sufficient rights 660 */ 661 @Callable 662 public Map<String, Object> editCalendar(String id, String inputName, String description, String templateDesc, String color, String visibility, String workflowName, Boolean renameIfExists, Boolean fullEdit) throws IllegalAccessException 663 { 664 // TODO handle template desc 665 Map<String, Object> result = new HashMap<>(); 666 667 assert id != null; 668 String rename = Text.escapeIllegalJcrChars(inputName); 669 670 AmetysObject object = _resolver.resolveById(id); 671 if (!(object instanceof ModifiableCalendar)) 672 { 673 throw new IllegalClassException(ModifiableCalendar.class, object.getClass()); 674 } 675 676 ModifiableCalendar calendar = (ModifiableCalendar) object; 677 678 // Check user right 679 _explorerResourcesDAO.checkUserRight(object, RIGHTS_CALENDAR_EDIT); 680 681 String name = calendar.getName(); 682 ModifiableTraversableAmetysObject parent = calendar.getParent(); 683 684 if (BooleanUtils.isNotTrue(renameIfExists) && !StringUtils.equals(rename, name) && parent.hasChild(rename)) 685 { 686 getLogger().warn("Cannot edit the calendar with the new name '" + inputName + "', an object with same name already exists."); 687 result.put("message", "already-exist"); 688 return result; 689 } 690 691 if (!_explorerResourcesDAO.checkLock(calendar)) 692 { 693 getLogger().warn("User '" + _currentUserProvider.getUser() + "' try to modify calendar '" + object.getName() + "' but it is locked by another user"); 694 result.put("message", "locked"); 695 return result; 696 } 697 698 if (!StringUtils.equals(name, rename)) 699 { 700 int index = 2; 701 name = Text.escapeIllegalJcrChars(rename); 702 while (parent.hasChild(name)) 703 { 704 name = rename + " (" + index + ")"; 705 index++; 706 } 707 calendar.rename(name); 708 } 709 710 if (BooleanUtils.isTrue(fullEdit)) 711 { 712 calendar.setDescription(description); 713 calendar.setTemplateDescription(templateDesc); 714 calendar.setColor(color); 715 calendar.setVisibility(StringUtils.isNotEmpty(visibility) ? CalendarVisibility.valueOf(visibility.toUpperCase()) : CalendarVisibility.PRIVATE); 716 } 717 718 parent.saveChanges(); 719 720 // Notify listeners 721 Map<String, Object> eventParams = new HashMap<>(); 722 eventParams.put(ObservationConstants.ARGS_ID, calendar.getId()); 723 eventParams.put(ObservationConstants.ARGS_PARENT_ID, parent.getId()); 724 eventParams.put(ObservationConstants.ARGS_NAME, name); 725 eventParams.put(ObservationConstants.ARGS_PATH, calendar.getPath()); 726 727 _observationManager.notify(new Event(ObservationConstants.EVENT_CALENDAR_UPDATED, _currentUserProvider.getUser(), eventParams)); 728 729 result.put("id", calendar.getId()); 730 result.put("title", Text.unescapeIllegalJcrChars(name)); 731 732 return result; 733 } 734 735 /** 736 * Move a event to another calendar 737 * @param event The event to move 738 * @param parent The new parent calendar 739 * @throws AmetysRepositoryException if an error occurred while moving 740 */ 741 public void move(JCRCalendarEvent event, JCRCalendar parent) throws AmetysRepositoryException 742 { 743 try 744 { 745 event.getNode().getSession().move(event.getNode().getPath(), parent.getNode().getPath() + "/ametys:calendar-event"); 746 747 Workflow workflow = _workflowProvider.getAmetysObjectWorkflow(event); 748 749 String previousWorkflowName = workflow.getWorkflowName(event.getWorkflowId()); 750 String workflowName = parent.getWorkflowName(); 751 752 if (!StringUtils.equals(previousWorkflowName, workflowName)) 753 { 754 // If both calendar have a different workflow, initialize a new workflow instance for the event 755 HashMap<String, Object> inputs = new HashMap<>(); 756 inputs.put(AbstractExplorerNodeWorkflowComponent.EXPLORERNODE_KEY, parent); 757 workflow = _workflowProvider.getAmetysObjectWorkflow(event); 758 759 long workflowId = workflow.initialize(workflowName, 0, inputs); 760 event.setWorkflowId(workflowId); 761 } 762 } 763 catch (WorkflowException | RepositoryException e) 764 { 765 String errorMsg = String.format("Fail to move the event '%s' to the calendar '%s'.", event.getId(), parent.getId()); 766 throw new AmetysRepositoryException(errorMsg, e); 767 } 768 } 769 770 /** 771 * Delete a calendar 772 * @param id The id of the calendar 773 * @return The result map with id, parent id and message keys 774 * @throws IllegalAccessException If the user has no sufficient rights 775 */ 776 @Callable 777 public Map<String, Object> deleteCalendar(String id) throws IllegalAccessException 778 { 779 Map<String, Object> result = new HashMap<>(); 780 781 assert id != null; 782 783 AmetysObject object = _resolver.resolveById(id); 784 if (!(object instanceof ModifiableCalendar)) 785 { 786 throw new IllegalClassException(ModifiableCalendar.class, object.getClass()); 787 } 788 789 ModifiableCalendar calendar = (ModifiableCalendar) object; 790 791 // Check user right 792 _explorerResourcesDAO.checkUserRight(object, RIGHTS_CALENDAR_DELETE); 793 794 if (!_explorerResourcesDAO.checkLock(calendar)) 795 { 796 getLogger().warn("User '" + _currentUserProvider.getUser() + "' try to delete calendar'" + object.getName() + "' but it is locked by another user"); 797 result.put("message", "locked"); 798 return result; 799 } 800 801 ModifiableExplorerNode parent = calendar.getParent(); 802 String parentId = parent.getId(); 803 String name = calendar.getName(); 804 String path = calendar.getPath(); 805 806 calendar.remove(); 807 parent.saveChanges(); 808 809 // Notify listeners 810 Map<String, Object> eventParams = new HashMap<>(); 811 eventParams.put(ObservationConstants.ARGS_ID, id); 812 eventParams.put(ObservationConstants.ARGS_PARENT_ID, parentId); 813 eventParams.put(ObservationConstants.ARGS_NAME, name); 814 eventParams.put(ObservationConstants.ARGS_PATH, path); 815 816 _observationManager.notify(new Event(ObservationConstants.EVENT_CALENDAR_DELETED, _currentUserProvider.getUser(), eventParams)); 817 818 result.put("id", id); 819 result.put("parentId", parentId); 820 821 return result; 822 } 823 824 /** 825 * Do an event workflow action 826 * @param parameters The map of action parameters 827 * @return The map of results populated by the workflow action 828 * @throws WorkflowException if an error occurred 829 */ 830 @Callable 831 public Map<String, Object> doWorkflowEventAction(Map<String, Object> parameters) throws WorkflowException 832 { 833 Map<String, Object> result = new HashMap<>(); 834 HashMap<String, Object> inputs = new HashMap<>(); 835 836 inputs.put("parameters", parameters); 837 inputs.put("result", result); 838 839 String eventId = (String) parameters.get("id"); 840 Long workflowInstanceId = null; 841 CalendarEvent event = null; 842 if (StringUtils.isNotEmpty(eventId)) 843 { 844 event = _resolver.resolveById(eventId); 845 workflowInstanceId = event.getWorkflowId(); 846 } 847 848 inputs.put("eventId", eventId); 849 850 Calendar calendar = null; 851 String calendarId = (String) parameters.get("parentId"); 852 853 if (StringUtils.isNotEmpty(calendarId)) 854 { 855 calendar = _resolver.resolveById(calendarId); 856 } 857 // parentId can be not provided for some basic actions where the event already exists 858 else if (event != null) 859 { 860 calendar = event.getParent(); 861 } 862 else 863 { 864 throw new WorkflowException("Unable to retrieve the current calendar"); 865 } 866 867 inputs.put(AbstractExplorerNodeWorkflowComponent.EXPLORERNODE_KEY, calendar); 868 869 String workflowName = calendar.getWorkflowName(); 870 if (workflowName == null) 871 { 872 throw new IllegalArgumentException("The workflow name is not specified"); 873 } 874 875 int actionId = (int) parameters.get("actionId"); 876 877 boolean sendMail = true; 878 String choice = (String) parameters.get("choice"); 879 if (actionId == 2 && "unit".equals(choice)) 880 { 881 sendMail = false; 882 } 883 inputs.put("sendMail", sendMail); 884 885 Workflow workflow = _workflowProvider.getAmetysObjectWorkflow(event != null ? event : null); 886 887 if (workflowInstanceId == null) 888 { 889 try 890 { 891 workflow.initialize(workflowName, actionId, inputs); 892 } 893 catch (WorkflowException e) 894 { 895 getLogger().error("An error occured while creating workflow '" + workflowName + "' with action '" + actionId, e); 896 throw e; 897 } 898 } 899 else 900 { 901 try 902 { 903 workflow.doAction(workflowInstanceId, actionId, inputs); 904 } 905 catch (WorkflowException e) 906 { 907 getLogger().error("An error occured while doing action '" + actionId + "'with the workflow '" + workflowName, e); 908 throw e; 909 } 910 } 911 912 return result; 913 } 914 915 /** 916 * Delete an event 917 * @param id The id of the event 918 * @param occurrence a string representing the occurrence date (ISO format). 919 * @param choice The type of modification 920 * @return The result map with id, parent id and message keys 921 * @throws IllegalAccessException If the user has no sufficient rights 922 */ 923 @Callable 924 public Map<String, Object> deleteEvent(String id, String occurrence, String choice) throws IllegalAccessException 925 { 926 Map<String, Object> result = new HashMap<>(); 927 928 assert id != null; 929 930 AmetysObject object = _resolver.resolveById(id); 931 if (!(object instanceof ModifiableCalendarEvent)) 932 { 933 throw new IllegalClassException(ModifiableCalendarEvent.class, object.getClass()); 934 } 935 936 ModifiableCalendarEvent event = (ModifiableCalendarEvent) object; 937 ModifiableCalendar calendar = event.getParent(); 938 939 // Check user right 940 try 941 { 942 _explorerResourcesDAO.checkUserRight(calendar, RIGHTS_EVENT_DELETE); 943 } 944 catch (IllegalAccessException e) 945 { 946 UserIdentity user = _currentUserProvider.getUser(); 947 UserIdentity creator = event.getCreator(); 948 RightResult rightCreator = _rightManager.hasRight(user, RIGHTS_EVENT_DELETE_OWN, calendar); 949 boolean hasOwnDeleteRight = rightCreator == RightResult.RIGHT_ALLOW && creator.equals(user); 950 if (!hasOwnDeleteRight) 951 { 952 // rethrow exception 953 throw e; 954 } 955 } 956 957 if (!_explorerResourcesDAO.checkLock(event)) 958 { 959 getLogger().warn("User '" + _currentUserProvider.getUser() + "' try to delete event'" + object.getName() + "' but it is locked by another user"); 960 result.put("message", "locked"); 961 return result; 962 } 963 964 String parentId = calendar.getId(); 965 String name = event.getName(); 966 String path = event.getPath(); 967 968 if (StringUtils.isNotBlank(choice) && choice.equals("unit")) 969 { 970 ArrayList<Date> excludedOccurrences = new ArrayList<>(); 971 excludedOccurrences.addAll(event.getExcludedOccurences()); 972 long date = ZonedDateTime.parse(occurrence, DateTimeFormatter.ISO_DATE_TIME).toInstant().toEpochMilli(); 973 GregorianCalendar gCalendar = new GregorianCalendar(); 974 gCalendar.setTimeInMillis(date); 975 gCalendar.set(java.util.Calendar.HOUR_OF_DAY, 0); 976 gCalendar.set(java.util.Calendar.MINUTE, 0); 977 gCalendar.set(java.util.Calendar.SECOND, 0); 978 gCalendar.set(java.util.Calendar.MILLISECOND, 0); 979 980 Date dateExcluded = gCalendar.getTime(); 981 excludedOccurrences.add(dateExcluded); 982 983 event.setExcludedOccurrences(excludedOccurrences); 984 } 985 else 986 { 987 event.remove(); 988 } 989 990 calendar.saveChanges(); 991 992 // Notify listeners 993 Map<String, Object> eventParams = new HashMap<>(); 994 eventParams.put(ObservationConstants.ARGS_CALENDAR, calendar); 995 eventParams.put(ObservationConstants.ARGS_ID, id); 996 eventParams.put(ObservationConstants.ARGS_NAME, name); 997 eventParams.put(ObservationConstants.ARGS_PATH, path); 998 _observationManager.notify(new Event(ObservationConstants.EVENT_CALENDAR_EVENT_DELETED, _currentUserProvider.getUser(), eventParams)); 999 1000 result.put("id", id); 1001 result.put("parentId", parentId); 1002 1003 return result; 1004 } 1005} 1006