001/* 002 * Copyright 2022 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 */ 016 017package org.ametys.plugins.workspaces.calendars.events; 018 019import java.time.ZonedDateTime; 020import java.util.ArrayList; 021import java.util.HashMap; 022import java.util.List; 023import java.util.Map; 024import java.util.Optional; 025import java.util.Set; 026import java.util.stream.Collectors; 027import java.util.stream.Stream; 028 029import org.apache.avalon.framework.service.ServiceException; 030import org.apache.avalon.framework.service.ServiceManager; 031import org.apache.commons.lang3.StringUtils; 032 033import org.ametys.core.right.RightManager.RightResult; 034import org.ametys.core.user.User; 035import org.ametys.core.user.UserIdentity; 036import org.ametys.core.util.DateUtils; 037import org.ametys.plugins.explorer.ExplorerNode; 038import org.ametys.plugins.workspaces.calendars.AbstractCalendarDAO; 039import org.ametys.plugins.workspaces.calendars.Calendar; 040import org.ametys.plugins.workspaces.calendars.CalendarDAO; 041 042/** 043 * Helper to convert events to JSON 044 */ 045public class CalendarEventJSONHelper extends AbstractCalendarDAO 046{ 047 /** Avalon Role */ 048 public static final String ROLE = CalendarEventJSONHelper.class.getName(); 049 050 private static final String __HOUR_PATTERN = "yyyyMMdd'T'HHmmssXXX"; 051 052// private static final String __FULL_DAY_PATTERN = "uuuuMMdd"; 053 054 055 /** Calendar DAO */ 056 protected CalendarDAO _calendarDAO; 057 058 @Override 059 public void service(ServiceManager manager) throws ServiceException 060 { 061 super.service(manager); 062 _calendarDAO = (CalendarDAO) manager.lookup(CalendarDAO.ROLE); 063 } 064 065 /** 066 * Get event info for a specific occurrence 067 * @param event The event 068 * @param occurrenceDate the occurrence 069 * @param fullInfo true to include full info (rights, parent id, etc...) 070 * @return the event data in a map 071 */ 072 public Map<String, Object> eventAsJsonWithOccurrence(CalendarEvent event, ZonedDateTime occurrenceDate, boolean fullInfo) 073 { 074 Map<String, Object> eventData = eventAsJson(event, fullInfo, false); 075 076 Optional<CalendarEventOccurrence> optionalEvent = event.getFirstOccurrence(occurrenceDate); 077 if (optionalEvent.isPresent()) 078 { 079 eventData.putAll(optionalEvent.get().toJSON()); 080 } 081 082 return eventData; 083 } 084 085 /** 086 * Get event info 087 * @param event The event 088 * @param fullInfo true to include full info (rights, parent id, etc...) 089 * @param startDate The start date. 090 * @param endDate The end date. 091 * @return the event data in a map 092 */ 093 public Map<String, Object> eventAsJsonWithOccurrences(CalendarEvent event, boolean fullInfo, ZonedDateTime startDate, ZonedDateTime endDate) 094 { 095 Map<String, Object> eventData = eventAsJson(event, false, false); 096 097 List<CalendarEventOccurrence> occurences = event.getOccurrences(startDate, endDate); 098 099 List<Object> occurrencesDataList = new ArrayList<>(); 100 eventData.put("occurrences", occurrencesDataList); 101 102 for (CalendarEventOccurrence occurence : occurences) 103 { 104 occurrencesDataList.add(occurence.toJSON()); 105 } 106 return eventData; 107 } 108 109 /** 110 * Get event info 111 * @param event The event 112 * @param fullInfo true to include full info (rights, parent id, etc...) 113 * @param useICSFormat true to use ICS Format for dates 114 * @return the event data in a map 115 */ 116 public Map<String, Object> eventAsJson(CalendarEvent event, boolean fullInfo, boolean useICSFormat) 117 { 118 119 Calendar calendar = event.getParent(); 120 Map<String, Object> result = new HashMap<>(); 121 122 result.put("id", event.getId()); 123 result.put("calendarId", event.getParent().getId()); 124 result.put("color", calendar.getColor()); 125 126 result.put("title", event.getTitle()); 127 result.put("description", event.getDescription()); 128 129 boolean fullDay = event.getFullDay(); 130 131 result.put("fullDay", fullDay); 132 result.put("recurrenceType", event.getRecurrenceType().toString()); 133 134 result.put("location", event.getLocation()); 135 result.put("keywords", event.getTags()); 136 137 ZonedDateTime untilDate = event.getRepeatUntil(); 138 if (untilDate != null) 139 { 140 if (useICSFormat) 141 { 142 // iCalendar/ICS handle until date slightly differently, so we have to add one day to include the last occurrence 143 untilDate = untilDate.plusDays(1).minusSeconds(1); 144 } 145 146 result.put("untilDate", formatDate(untilDate, useICSFormat, fullDay)); 147 } 148 149 ZonedDateTime startDateEvent = event.getStartDate(); 150 ZonedDateTime endDateEvent = event.getEndDate(); 151 152 if (event.getFullDay()) 153 { 154 result.put("endDateNextDay", formatDate(endDateEvent.plusDays(1), useICSFormat, fullDay)); 155 } 156 157 result.put("startDate", formatDate(startDateEvent, useICSFormat, fullDay)); 158 159 if (event.getFullDay() && useICSFormat) 160 { 161 result.put("endDate", formatDate(endDateEvent.plusDays(1), useICSFormat, fullDay)); 162 } 163 else 164 { 165 result.put("endDate", formatDate(endDateEvent, useICSFormat, fullDay)); 166 } 167 168 //excluded occurences 169 List<ZonedDateTime> excludedOccurences = event.getExcludedOccurences(); 170 if (excludedOccurences != null && !excludedOccurences.isEmpty()) 171 { 172 List<String> excludedOccurencesStrings = new ArrayList<>(); 173 for (ZonedDateTime excludedOccurence : excludedOccurences) 174 { 175 excludedOccurencesStrings.add(formatDate(excludedOccurence, useICSFormat, true)); 176 } 177 result.put("excludedDates", excludedOccurencesStrings); 178 } 179 180 // creator 181 UserIdentity creatorIdentity = event.getCreator(); 182 User creator = _userManager.getUser(creatorIdentity); 183 184 result.put("creator", creatorIdentity); 185 result.put("creatorFullName", creator != null ? creator.getFullName() : creatorIdentity.getLogin()); 186 187 UserIdentity user = _currentUserProvider.getUser(); 188 result.put("isCreator", creatorIdentity.equals(user)); 189 result.put("creationDate", formatDate(event.getCreationDate(), useICSFormat, false)); 190 191 // last modification 192 UserIdentity contributorIdentity = event.getLastContributor(); 193 User contributor = _userManager.getUser(contributorIdentity); 194 195 result.put("contributor", contributorIdentity); 196 result.put("contributorFullName", contributor != null ? contributor.getFullName() : contributorIdentity.getLogin()); 197 result.put("lastModified", formatDate(event.getLastModified(), useICSFormat, false)); 198 199 if (fullInfo) 200 { 201 result.putAll(_eventAsJsonFullInfo(event)); 202 } 203 204 result.put("calendar", _calendarDAO.getCalendarProperties(calendar)); 205 206 result.put("isStillSynchronized", _messagingConnectorCalendarManager.isEventStillSynchronized(event.getId())); 207 208 // tags and places are expected by the client (respectively keywords and location on the server side) 209 result.put("tags", event.getTags()); 210 211 String location = StringUtils.defaultString(event.getLocation()); 212 result.put("location", location); 213 result.put("places", Stream.of(location.split(",")).filter(StringUtils::isNotEmpty).collect(Collectors.toList())); 214 215 // add event rights 216 result.put("rights", _extractEventRightData(event)); 217 218 result.put("resourceIds", event.getResources()); 219 220 return result; 221 } 222 223 private String formatDate(ZonedDateTime date, boolean useICSFormat, boolean fullDay) 224 { 225 if (useICSFormat) 226 { 227 if (fullDay) 228 { 229 // FIXME : commented because currently, dates are badly stored, waiting for a data migration to use it back 230 // return DateUtils.zonedDateTimeToString(date, date.getZone(), __FULL_DAY_PATTERN); 231 return DateUtils.zonedDateTimeToString(date); 232 } 233 else 234 { 235 return DateUtils.zonedDateTimeToString(date, date.getZone(), __HOUR_PATTERN); 236 } 237 } 238 else 239 { 240 return DateUtils.zonedDateTimeToString(date); 241 } 242 } 243 244 /** 245 * Retrieves the event additional info (rights, parent id, etc...) 246 * @param event The event 247 * @return the event additional info (rights, parent id, etc...) in a map 248 */ 249 protected Map<String, Object> _eventAsJsonFullInfo(CalendarEvent event) 250 { 251 Map<String, Object> result = new HashMap<>(); 252 253 ExplorerNode explorerNode = event.getParent(); 254 ExplorerNode root = explorerNode; 255 while (true) 256 { 257 if (root.getParent() instanceof ExplorerNode) 258 { 259 root = root.getParent(); 260 } 261 else 262 { 263 break; 264 } 265 } 266 result.put("rootId", root.getId()); 267 result.put("parentId", explorerNode.getId()); 268 result.put("name", event.getName()); 269 result.put("path", explorerNode.getExplorerPath()); 270 result.put("isModifiable", true); 271 272 result.put("rights", _getUserRights(explorerNode)); 273 274 return result; 275 } 276 277 /** 278 * Internal method to extract the data concerning the right of the current user for an event 279 * @param event The event 280 * @return The map of right data. Keys are the rights id, and values indicates whether the current user has the right or not. 281 */ 282 protected Map<String, Object> _extractEventRightData(CalendarEvent event) 283 { 284 Map<String, Object> rightsData = new HashMap<>(); 285 UserIdentity user = _currentUserProvider.getUser(); 286 Calendar calendar = event.getParent(); 287 288 rightsData.put("edit", _rightManager.hasRight(user, AbstractCalendarDAO.RIGHTS_EVENT_EDIT, calendar) == RightResult.RIGHT_ALLOW); 289 rightsData.put("delete", _rightManager.hasRight(user, AbstractCalendarDAO.RIGHTS_EVENT_DELETE, calendar) == RightResult.RIGHT_ALLOW); 290 rightsData.put("delete-own", _rightManager.hasRight(user, AbstractCalendarDAO.RIGHTS_EVENT_DELETE_OWN, calendar) == RightResult.RIGHT_ALLOW); 291 292 return rightsData; 293 } 294 295 /** 296 * Get the user rights on the resource collection 297 * @param node The explorer node 298 * @return The user's rights 299 */ 300 protected Set<String> _getUserRights(ExplorerNode node) 301 { 302 return _rightManager.getUserRights(_currentUserProvider.getUser(), node); 303 } 304 305}