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 // tags and places are expected by the client (respectively keywords and location on the server side) 207 result.put("tags", event.getTags()); 208 209 String location = StringUtils.defaultString(event.getLocation()); 210 result.put("location", location); 211 result.put("places", Stream.of(location.split(",")).filter(StringUtils::isNotEmpty).collect(Collectors.toList())); 212 213 // add event rights 214 result.put("rights", _extractEventRightData(event)); 215 216 result.put("resourceIds", event.getResources()); 217 218 return result; 219 } 220 221 private String formatDate(ZonedDateTime date, boolean useICSFormat, boolean fullDay) 222 { 223 if (useICSFormat) 224 { 225 if (fullDay) 226 { 227 // FIXME : commented because currently, dates are badly stored, waiting for a data migration to use it back 228 // return DateUtils.zonedDateTimeToString(date, date.getZone(), __FULL_DAY_PATTERN); 229 return DateUtils.zonedDateTimeToString(date); 230 } 231 else 232 { 233 return DateUtils.zonedDateTimeToString(date, date.getZone(), __HOUR_PATTERN); 234 } 235 } 236 else 237 { 238 return DateUtils.zonedDateTimeToString(date); 239 } 240 } 241 242 /** 243 * Retrieves the event additional info (rights, parent id, etc...) 244 * @param event The event 245 * @return the event additional info (rights, parent id, etc...) in a map 246 */ 247 protected Map<String, Object> _eventAsJsonFullInfo(CalendarEvent event) 248 { 249 Map<String, Object> result = new HashMap<>(); 250 251 ExplorerNode explorerNode = event.getParent(); 252 ExplorerNode root = explorerNode; 253 while (true) 254 { 255 if (root.getParent() instanceof ExplorerNode) 256 { 257 root = root.getParent(); 258 } 259 else 260 { 261 break; 262 } 263 } 264 result.put("rootId", root.getId()); 265 result.put("parentId", explorerNode.getId()); 266 result.put("name", event.getName()); 267 result.put("path", explorerNode.getExplorerPath()); 268 result.put("isModifiable", true); 269 270 result.put("rights", _getUserRights(explorerNode)); 271 272 return result; 273 } 274 275 /** 276 * Internal method to extract the data concerning the right of the current user for an event 277 * @param event The event 278 * @return The map of right data. Keys are the rights id, and values indicates whether the current user has the right or not. 279 */ 280 protected Map<String, Object> _extractEventRightData(CalendarEvent event) 281 { 282 Map<String, Object> rightsData = new HashMap<>(); 283 UserIdentity user = _currentUserProvider.getUser(); 284 Calendar calendar = event.getParent(); 285 286 rightsData.put("edit", _rightManager.hasRight(user, AbstractCalendarDAO.RIGHTS_EVENT_EDIT, calendar) == RightResult.RIGHT_ALLOW); 287 rightsData.put("delete", _rightManager.hasRight(user, AbstractCalendarDAO.RIGHTS_EVENT_DELETE, calendar) == RightResult.RIGHT_ALLOW); 288 rightsData.put("delete-own", _rightManager.hasRight(user, AbstractCalendarDAO.RIGHTS_EVENT_DELETE_OWN, calendar) == RightResult.RIGHT_ALLOW); 289 290 return rightsData; 291 } 292 293 /** 294 * Get the user rights on the resource collection 295 * @param node The explorer node 296 * @return The user's rights 297 */ 298 protected Set<String> _getUserRights(ExplorerNode node) 299 { 300 return _rightManager.getUserRights(_currentUserProvider.getUser(), node); 301 } 302 303}