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.Date; 020import java.util.HashMap; 021import java.util.List; 022import java.util.Map; 023 024import javax.jcr.Node; 025import javax.jcr.RepositoryException; 026 027import org.apache.avalon.framework.component.Component; 028import org.apache.avalon.framework.context.Context; 029import org.apache.avalon.framework.context.ContextException; 030import org.apache.avalon.framework.context.Contextualizable; 031import org.apache.avalon.framework.logger.AbstractLogEnabled; 032import org.apache.avalon.framework.service.ServiceException; 033import org.apache.avalon.framework.service.ServiceManager; 034import org.apache.avalon.framework.service.Serviceable; 035import org.apache.cocoon.components.ContextHelper; 036import org.apache.cocoon.environment.Request; 037import org.apache.commons.lang3.StringUtils; 038 039import org.ametys.core.observation.Event; 040import org.ametys.core.observation.ObservationManager; 041import org.ametys.core.ui.Callable; 042import org.ametys.core.user.CurrentUserProvider; 043import org.ametys.core.user.User; 044import org.ametys.core.user.UserIdentity; 045import org.ametys.core.user.UserManager; 046import org.ametys.core.util.DateUtils; 047import org.ametys.core.util.I18nUtils; 048import org.ametys.plugins.messagingconnector.MessagingConnector; 049import org.ametys.plugins.messagingconnector.MessagingConnector.AttendeeInformation; 050import org.ametys.plugins.messagingconnector.MessagingConnector.FreeBusyStatus; 051import org.ametys.plugins.messagingconnector.MessagingConnector.ResponseType; 052import org.ametys.plugins.repository.AmetysObjectResolver; 053import org.ametys.plugins.repository.RepositoryConstants; 054import org.ametys.plugins.workspaces.calendars.events.CalendarEventAttendee; 055import org.ametys.plugins.workspaces.calendars.jcr.JCRCalendarEvent; 056import org.ametys.plugins.workspaces.project.objects.Project; 057import org.ametys.runtime.i18n.I18nizableText; 058 059/** 060 * Manager for manipulating messaging connector linked to calendars event of a project 061 * 062 */ 063public class MessagingConnectorCalendarManager extends AbstractLogEnabled implements Serviceable, Component, Contextualizable 064{ 065 /** Avalon Role */ 066 public static final String ROLE = MessagingConnectorCalendarManager.class.getName(); 067 068 /** Property's name for synchronized id id */ 069 public static final String PROPERTY_SYNCHRONIZED_ID = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":synchronizedId"; 070 071 private MessagingConnector _messagingConnector; 072 private UserManager _userManager; 073 private AmetysObjectResolver _resolver; 074 private ObservationManager _observationManager; 075 private CurrentUserProvider _currentUserProvider; 076 private I18nUtils _i18nUtils; 077 private Context _context; 078 079 @Override 080 public void contextualize(Context context) throws ContextException 081 { 082 _context = context; 083 } 084 085 @Override 086 public void service(ServiceManager manager) throws ServiceException 087 { 088 boolean hasService = manager.hasService(MessagingConnector.ROLE); 089 _messagingConnector = hasService ? (MessagingConnector) manager.lookup(MessagingConnector.ROLE) : null; 090 091 _userManager = (UserManager) manager.lookup(UserManager.ROLE); 092 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 093 _observationManager = (ObservationManager) manager.lookup(ObservationManager.ROLE); 094 _currentUserProvider = (CurrentUserProvider) manager.lookup(CurrentUserProvider.ROLE); 095 _i18nUtils = (I18nUtils) manager.lookup(I18nUtils.ROLE); 096 } 097 098 /** 099 * Set the synchronized id (for messaging connector) to the event 100 * @param event the event 101 * @param synchronizedId the synchronized id 102 */ 103 public void setSynchronizedId(JCRCalendarEvent event, String synchronizedId) 104 { 105 try 106 { 107 event.getNode().setProperty(PROPERTY_SYNCHRONIZED_ID, synchronizedId); 108 event.saveChanges(); 109 } 110 catch (RepositoryException e) 111 { 112 getLogger().error("An error occurred setting synchronized id for event " + event.getId()); 113 } 114 } 115 116 /** 117 * Get the synchronized id (for messaging connector) of the event 118 * @param event the event 119 * @return the synchronized id 120 */ 121 public String getSynchronizedId(JCRCalendarEvent event) 122 { 123 try 124 { 125 Node eventNode = event.getNode(); 126 if (eventNode.hasProperty(PROPERTY_SYNCHRONIZED_ID)) 127 { 128 return eventNode.getProperty(PROPERTY_SYNCHRONIZED_ID).getString(); 129 } 130 } 131 catch (RepositoryException e) 132 { 133 getLogger().error("An error occurred getting synchronized id for event " + event.getId()); 134 } 135 136 return null; 137 } 138 139 /** 140 * Set attendees to the event 141 * @param eventId the event id 142 * @param attendees the list of attendees 143 */ 144 public void setAttendees(String eventId, List<CalendarEventAttendee> attendees) 145 { 146 if (attendees != null) 147 { 148 JCRCalendarEvent event = (JCRCalendarEvent) _resolver.resolveById(eventId); 149 try 150 { 151 event.setAttendees(attendees); 152 } 153 catch (RepositoryException e) 154 { 155 getLogger().error("An error occurred setting attendees for event " + eventId, e); 156 } 157 } 158 } 159 160 /** 161 * Set organiser to the event 162 * @param eventId the event id 163 * @param organiser the organiser 164 */ 165 public void setOrganiser(String eventId, UserIdentity organiser) 166 { 167 JCRCalendarEvent event = (JCRCalendarEvent) _resolver.resolveById(eventId); 168 if (organiser != null) 169 { 170 event.setOrganiser(organiser); 171 } 172 } 173 174 /** 175 * Add event invitation parameters (attendees and organiser) 176 * @param parameters the map of parameters 177 * @param eventId the event id 178 */ 179 public void addEventInvitation(Map<String, Object> parameters, String eventId) 180 { 181 @SuppressWarnings("unchecked") 182 List<Map<String, Object>> attendeesList = (List<Map<String, Object>>) parameters.get("attendees"); 183 List<CalendarEventAttendee> attendees = _getAttendeeListFromParameter(attendeesList); 184 setAttendees(eventId, attendees); 185 186 @SuppressWarnings("unchecked") 187 Map<String, Object> organiserMap = (Map<String, Object>) parameters.get("organiser"); 188 UserIdentity organiser = _getUserFromParameter(organiserMap); 189 setOrganiser(eventId, organiser); 190 191 if (organiser != null) 192 { 193 JCRCalendarEvent event = _resolver.resolveById(eventId); 194 _createEvent(event, organiser, attendees); 195 } 196 } 197 198 /** 199 * Edit event invitation parameters (attendees and organiser) 200 * @param parameters the map of parameters 201 * @param eventId the event id 202 */ 203 public void editEventInvitation(Map<String, Object> parameters, String eventId) 204 { 205 JCRCalendarEvent event = _resolver.resolveById(eventId); 206 207 @SuppressWarnings("unchecked") 208 List<Map<String, Object>> attendeesList = (List<Map<String, Object>>) parameters.get("attendees"); 209 List<CalendarEventAttendee> attendees = _getAttendeeListFromParameter(attendeesList); 210 try 211 { 212 if (attendees == null) 213 { 214 attendees = event.getAttendees(); 215 } 216 } 217 catch (RepositoryException e) 218 { 219 getLogger().error("An error occurred getting attendees from repository for event " + event.getId()); 220 } 221 setAttendees(eventId, attendees); 222 223 @SuppressWarnings("unchecked") 224 Map<String, Object> organiserMap = (Map<String, Object>) parameters.get("organiser"); 225 UserIdentity organiserFromMap = _getUserFromParameter(organiserMap); 226 UserIdentity organiser = organiserFromMap != null ? organiserFromMap : event.getOrganiser(); 227 setOrganiser(eventId, organiser); 228 229 if (_messagingConnector != null) 230 { 231 String synchronizedId = getSynchronizedId(event); 232 if (StringUtils.isNotBlank(synchronizedId) && _messagingConnector.isEventExist(synchronizedId, organiser)) 233 { 234 _editEvent(event, organiser, attendees); 235 } 236 else if (organiser != null) 237 { 238 _createEvent(event, organiser, attendees); 239 } 240 } 241 } 242 243 /** 244 * Create event in the messaging connector 245 * @param event the event 246 * @param organiser the organiser 247 * @param attendees the list of attendee 248 */ 249 protected void _createEvent(JCRCalendarEvent event, UserIdentity organiser, List<CalendarEventAttendee> attendees) 250 { 251 Map<String, Boolean> attendeesForMessagingConnector = new HashMap<>(); 252 for (CalendarEventAttendee attendee : attendees) 253 { 254 if (attendee.isExternal()) 255 { 256 attendeesForMessagingConnector.put(attendee.getEmail(), attendee.isMandatory()); 257 } 258 else 259 { 260 User user = _userManager.getUser(new UserIdentity(attendee.getLogin(), attendee.getPopulationId())); 261 attendeesForMessagingConnector.put(user.getEmail(), attendee.isMandatory()); 262 } 263 } 264 265 String synchronizedId = _messagingConnector.createEvent(_getEventTitle(event), event.getDescription(), event.getLocation(), event.getFullDay(), event.getStartDate(), event.getEndDate(), event.getRecurrenceType(), event.getRepeatUntil(), attendeesForMessagingConnector, event.getOrganiser()); 266 setSynchronizedId(event, synchronizedId); 267 } 268 269 /** 270 * Delete event in the messaging connector 271 * @param event the event 272 */ 273 public void deleteEvent(JCRCalendarEvent event) 274 { 275 if (isEventSynchronized(event.getId())) 276 { 277 String synchronizedId = getSynchronizedId(event); 278 _messagingConnector.deleteEvent(synchronizedId, event.getOrganiser()); 279 } 280 } 281 282 /** 283 * Edit event in the messaging connector 284 * @param event the event 285 * @param organiser the organiser 286 * @param attendees the list of attendee 287 */ 288 protected void _editEvent(JCRCalendarEvent event, UserIdentity organiser, List<CalendarEventAttendee> attendees) 289 { 290 String synchronizedId = getSynchronizedId(event); 291 Map<String, Boolean> attendeesForMessagingConnector = null; 292 if (attendees != null) 293 { 294 attendeesForMessagingConnector = new HashMap<>(); 295 for (CalendarEventAttendee attendee : attendees) 296 { 297 if (attendee.isExternal()) 298 { 299 attendeesForMessagingConnector.put(attendee.getEmail(), attendee.isMandatory()); 300 } 301 else 302 { 303 User user = _userManager.getUser(new UserIdentity(attendee.getLogin(), attendee.getPopulationId())); 304 attendeesForMessagingConnector.put(user.getEmail(), attendee.isMandatory()); 305 } 306 } 307 } 308 309 _messagingConnector.updateEvent(synchronizedId, _getEventTitle(event), event.getDescription(), event.getLocation(), event.getFullDay(), event.getStartDate(), event.getEndDate(), event.getRecurrenceType(), event.getRepeatUntil(), attendeesForMessagingConnector, event.getOrganiser()); 310 } 311 312 /** 313 * Get the computed title of the event 314 * @param event the event 315 * @return the computed title of the event 316 */ 317 protected String _getEventTitle(JCRCalendarEvent event) 318 { 319 Calendar calendar = event.getParent(); 320 Project project = calendar.getProject(); 321 322 List<String> parameters = new ArrayList<>(); 323 parameters.add(project.getTitle()); 324 parameters.add(event.getTitle()); 325 I18nizableText titleI18n = new I18nizableText("plugin.workspaces", "PLUGINS_WORKSPACES_PROJECT_SERVICE_MODULE_CALENDAR_ADD_EVENT_MESSAGING_CONNECTOR_TITLE", parameters); 326 327 Request request = ContextHelper.getRequest(_context); 328 String lang = (String) request.getAttribute("sitemapLanguage"); 329 330 return _i18nUtils.translate(titleI18n, lang); 331 } 332 333 /** 334 * True if messaging connector support invitation 335 * @return true if messaging connector support invitation 336 */ 337 @Callable 338 public boolean supportInvitation() 339 { 340 return _messagingConnector != null && _messagingConnector.supportInvitation(); 341 } 342 343 /** 344 * True if user exists in the messaging connector 345 * @param userAsMap the user 346 * @return true if user exists in the messaging connector 347 */ 348 @Callable 349 public boolean isValidUser(Map<String, Object> userAsMap) 350 { 351 UserIdentity user = _getUserFromParameter(userAsMap); 352 return _messagingConnector != null && _messagingConnector.isUserExist(user); 353 } 354 355 /** 356 * True if the event is synchronized in the messaging connector 357 * @param eventId the event id 358 * @return true if the event exist 359 */ 360 @Callable 361 public boolean isEventSynchronized(String eventId) 362 { 363 if (_messagingConnector == null) 364 { 365 return false; 366 } 367 JCRCalendarEvent event = _resolver.resolveById(eventId); 368 String synchronizedId = getSynchronizedId(event); 369 370 if (StringUtils.isBlank(synchronizedId)) 371 { 372 return false; 373 } 374 375 try 376 { 377 return _messagingConnector.isEventExist(synchronizedId, event.getOrganiser()); 378 } 379 catch (UnsupportedOperationException e) 380 { 381 getLogger().error("An error occurred while checking if event " + eventId + " exists", e); 382 return false; 383 } 384 385 386 } 387 388 /** 389 * Get the list of attendees email with their free/busy status 390 * @param startDate the start date 391 * @param endDate the end date 392 * @param isAllDay true if is all day 393 * @param organiserMap the organiser map 394 * @param attendeesList the attendee list 395 * @return the list of attendees email with their free/busy status 396 */ 397 @Callable 398 public Map<String, FreeBusyStatus> getFreeBusy(String startDate, String endDate, boolean isAllDay, Map<String, Object> organiserMap, Map<String, String> attendeesList) 399 { 400 Map<String, FreeBusyStatus> userIdFreeBusy = new HashMap<>(); 401 402 if (_messagingConnector != null) 403 { 404 Date start = DateUtils.parse(startDate); 405 Date end = DateUtils.parse(endDate); 406 407 UserIdentity organiser = _getUserFromParameter(organiserMap); 408 409 Map<String, FreeBusyStatus> freeBusy = _messagingConnector.getFreeBusy(start, end, isAllDay, attendeesList.keySet(), organiser); 410 for (String email : freeBusy.keySet()) 411 { 412 userIdFreeBusy.put(attendeesList.get(email), freeBusy.get(email)); 413 } 414 } 415 416 return userIdFreeBusy; 417 } 418 419 /** 420 * Set invitation to the event 421 * @param eventId the event id 422 * @param organiserMap the organiser map 423 * @param attendeesList the attendees list 424 */ 425 @Callable 426 public void setInvitations(String eventId, Map<String, Object> organiserMap, List<Map<String, Object>> attendeesList) 427 { 428 UserIdentity organiser = _getUserFromParameter(organiserMap); 429 setOrganiser(eventId, organiser); 430 431 List<CalendarEventAttendee> attendees = _getAttendeeListFromParameter(attendeesList); 432 setAttendees(eventId, attendees); 433 434 JCRCalendarEvent event = _resolver.resolveById(eventId); 435 event.saveChanges(); 436 437 if (_messagingConnector != null) 438 { 439 String synchronizedId = getSynchronizedId(event); 440 if (StringUtils.isNotBlank(synchronizedId) && _messagingConnector.isEventExist(synchronizedId, organiser)) 441 { 442 443 Map<String, Boolean> attendeesForMessagingConnector = new HashMap<>(); 444 for (CalendarEventAttendee attendee : attendees) 445 { 446 if (attendee.isExternal()) 447 { 448 attendeesForMessagingConnector.put(attendee.getEmail(), attendee.isMandatory()); 449 } 450 else 451 { 452 User user = _userManager.getUser(new UserIdentity(attendee.getLogin(), attendee.getPopulationId())); 453 attendeesForMessagingConnector.put(user.getEmail(), attendee.isMandatory()); 454 } 455 } 456 _messagingConnector.setAttendees(synchronizedId, attendeesForMessagingConnector, organiser); 457 } 458 else 459 { 460 _createEvent(event, organiser, attendees); 461 } 462 } 463 464 Map<String, Object> eventParams = new HashMap<>(); 465 eventParams.put(ObservationConstants.ARGS_CALENDAR, event.getParent()); 466 eventParams.put(ObservationConstants.ARGS_CALENDAR_EVENT, event); 467 eventParams.put(org.ametys.plugins.explorer.ObservationConstants.ARGS_ID, event.getId()); 468 eventParams.put(org.ametys.plugins.explorer.ObservationConstants.ARGS_PARENT_ID, event.getParent().getId()); 469 470 _observationManager.notify(new Event(ObservationConstants.EVENT_CALENDAR_EVENT_UPDATED, _currentUserProvider.getUser(), eventParams)); 471 } 472 473 /** 474 * Retrieves attendees to the event 475 * @param eventId the event id 476 * @return The map of attendees 477 */ 478 @Callable 479 public Map<String, Object> getAttendees(String eventId) 480 { 481 JCRCalendarEvent event = _resolver.resolveById(eventId); 482 Map<String, AttendeeInformation> attendees = new HashMap<>(); 483 484 String synchronizedId = getSynchronizedId(event); 485 if (_messagingConnector != null && StringUtils.isNotBlank(synchronizedId)) 486 { 487 attendees = _messagingConnector.getAttendees(synchronizedId, event.getOrganiser()); 488 } 489 490 Map<String, Object> result = new HashMap<>(); 491 List<Map<String, Object>> attendeesMap = new ArrayList<>(); 492 493 try 494 { 495 for (CalendarEventAttendee attendee : event.getAttendees()) 496 { 497 Map<String, Object> attendee2Json = new HashMap<>(); 498 if (!attendee.isExternal()) 499 { 500 UserIdentity userIdentity = new UserIdentity(attendee.getLogin(), attendee.getPopulationId()); 501 User user = _userManager.getUser(userIdentity); 502 503 attendee2Json.put("login", attendee.getLogin()); 504 attendee2Json.put("populationId", attendee.getPopulationId()); 505 506 attendee2Json.put("sortablename", user.getSortableName()); 507 attendee2Json.put("fullname", user.getFullName()); 508 509 String email = user.getEmail(); 510 attendee2Json.put("email", email); 511 AttendeeInformation attendeeInformation = attendees.get(email); 512 if (attendeeInformation != null) 513 { 514 attendee2Json.put("status", attendeeInformation.getResponseType()); 515 } 516 else 517 { 518 attendee2Json.put("status", ResponseType.Unknown); 519 } 520 } 521 else 522 { 523 String email = attendee.getEmail(); 524 attendee2Json.put("email", email); 525 attendee2Json.put("sortablename", email); 526 attendee2Json.put("fullname", email); 527 AttendeeInformation attendeeInformation = attendees.get(email); 528 if (attendeeInformation != null) 529 { 530 attendee2Json.put("status", attendeeInformation.getResponseType()); 531 } 532 else 533 { 534 attendee2Json.put("status", ResponseType.Unknown); 535 } 536 } 537 538 attendee2Json.put("external", attendee.isExternal()); 539 attendee2Json.put("mandatory", attendee.isMandatory()); 540 541 attendeesMap.add(attendee2Json); 542 } 543 } 544 catch (Exception e) 545 { 546 getLogger().error("An error occurred getting attendees for event " + eventId, e); 547 } 548 549 result.put("attendees", attendeesMap); 550 551 return result; 552 } 553 554 /** 555 * Retrieves organiser to the event 556 * @param eventId the event id 557 * @return The organiser 558 */ 559 @Callable 560 public Map<String, Object> getOrganiser(String eventId) 561 { 562 Map<String, Object> result = new HashMap<>(); 563 Map<String, Object> organiserMap = new HashMap<>(); 564 565 try 566 { 567 JCRCalendarEvent event = (JCRCalendarEvent) _resolver.resolveById(eventId); 568 UserIdentity organiserIdentity = event.getOrganiser(); 569 570 organiserMap.put("login", organiserIdentity.getLogin()); 571 organiserMap.put("populationId", organiserIdentity.getPopulationId()); 572 573 User organiser = _userManager.getUser(organiserIdentity); 574 organiserMap.put("sortablename", organiser.getSortableName()); 575 organiserMap.put("fullname", organiser.getFullName()); 576 } 577 catch (Exception e) 578 { 579 getLogger().error("An error occurred getting organiser for event " + eventId, e); 580 } 581 582 result.put("user", organiserMap); 583 584 return result; 585 } 586 587 /** 588 * Get user from the user parameter 589 * @param userMap the user map from parameters 590 * @return the user 591 */ 592 protected UserIdentity _getUserFromParameter(Map<String, Object> userMap) 593 { 594 if (userMap != null) 595 { 596 String login = (String) userMap.get("login"); 597 String populationId = (String) userMap.get("populationId"); 598 599 return new UserIdentity(login, populationId); 600 } 601 602 return null; 603 } 604 605 /** 606 * Get attendees list from the attendees parameter 607 * @param attendeesList the attendees list from parameters 608 * @return the attendees list 609 */ 610 protected List<CalendarEventAttendee> _getAttendeeListFromParameter(List<Map<String, Object>> attendeesList) 611 { 612 if (attendeesList != null) 613 { 614 List<CalendarEventAttendee> attendees = new ArrayList<>(); 615 for (Map<String, Object> attendee : attendeesList) 616 { 617 CalendarEventAttendee calendarEventAttendee = new CalendarEventAttendee(); 618 boolean isExternal = (boolean) attendee.get("external"); 619 if (isExternal) 620 { 621 calendarEventAttendee.setEmail((String) attendee.get("email")); 622 } 623 else 624 { 625 calendarEventAttendee.setPopulationId((String) attendee.get("populationId")); 626 calendarEventAttendee.setLogin((String) attendee.get("login")); 627 } 628 629 calendarEventAttendee.setIsExternal(isExternal); 630 calendarEventAttendee.setIsMandatory((boolean) attendee.get("mandatory")); 631 632 attendees.add(calendarEventAttendee); 633 } 634 return attendees; 635 } 636 637 return null; 638 } 639 640}