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
048    /** Avalon Role */
049    public static final String ROLE = CalendarEventJSONHelper.class.getName();
050
051    /** Calendar DAO */
052    protected CalendarDAO _calendarDAO;
053    
054    @Override
055    public void service(ServiceManager manager) throws ServiceException
056    {
057        super.service(manager);
058        _calendarDAO = (CalendarDAO) manager.lookup(CalendarDAO.ROLE);
059    }
060        
061    /**
062     * Get event info for a specific occurrence
063     * @param event The event
064     * @param occurrenceDate the occurrence
065     * @param fullInfo true to include full info (rights, parent id, etc...)
066     * @return the event data in a map
067     */
068    public Map<String, Object> eventAsJsonWithOccurrence(CalendarEvent event, ZonedDateTime occurrenceDate, boolean fullInfo)
069    {
070        Map<String, Object> eventData = eventAsJson(event, fullInfo);
071        
072        Optional<CalendarEventOccurrence> optionalEvent = event.getFirstOccurrence(occurrenceDate);
073        if (optionalEvent.isPresent())
074        {
075            eventData.putAll(optionalEvent.get().toJSON());
076        }
077        
078        return eventData;
079    }
080    
081    /**
082     * Get event info
083     * @param event The event
084     * @param fullInfo true to include full info (rights, parent id, etc...)
085     * @param startDate The start date.
086     * @param endDate The end date.
087     * @return the event data in a map
088     */
089    public Map<String, Object> eventAsJsonWithOccurrences(CalendarEvent event, boolean fullInfo, ZonedDateTime startDate, ZonedDateTime endDate)
090    {
091        Map<String, Object> eventData = eventAsJson(event, false);
092
093        List<CalendarEventOccurrence> occurences = event.getOccurrences(startDate, endDate);
094        
095        List<Object> occurrencesDataList = new ArrayList<>();
096        eventData.put("occurrences", occurrencesDataList);
097        
098        for (CalendarEventOccurrence occurence : occurences)
099        {
100            occurrencesDataList.add(occurence.toJSON());
101        }
102        return eventData;
103    }
104    
105    /**
106     * Get event info
107     * @param event The event
108     * @param fullInfo true to include full info (rights, parent id, etc...)
109     * @return the event data in a map
110     */
111    public Map<String, Object> eventAsJson(CalendarEvent event, boolean fullInfo)
112    {
113        Calendar calendar = event.getParent();
114        Map<String, Object> result = new HashMap<>();
115        
116        result.put("id", event.getId());
117        result.put("calendarId", event.getParent().getId());
118        result.put("color", calendar.getColor());
119        
120        result.put("title", event.getTitle());
121        result.put("description", event.getDescription());
122        result.put("fullDay", event.getFullDay());
123        result.put("recurrenceType", event.getRecurrenceType().toString());
124        
125        result.put("location", event.getLocation());
126        result.put("keywords", event.getTags());
127        
128        ZonedDateTime untilDate = event.getRepeatUntil();
129        if (untilDate != null)
130        {
131            result.put("untilDate", DateUtils.zonedDateTimeToString(untilDate));
132        }
133        
134        ZonedDateTime startDateEvent = event.getStartDate();
135        ZonedDateTime endDateEvent = event.getEndDate();
136        
137        if (event.getFullDay())
138        {
139            result.put("endDateNextDay", DateUtils.zonedDateTimeToString(endDateEvent.plusDays(1)));
140        }
141
142        result.put("startDate", DateUtils.zonedDateTimeToString(startDateEvent));
143        result.put("endDate", DateUtils.zonedDateTimeToString(endDateEvent));
144        
145        //excluded occurences
146        List<ZonedDateTime> excludedOccurences = event.getExcludedOccurences();
147        if (excludedOccurences != null && !excludedOccurences.isEmpty())
148        {
149            List<String> excludedOccurencesStrings = new ArrayList<>();
150            for (ZonedDateTime excludedOccurence : excludedOccurences)
151            {
152                excludedOccurencesStrings.add(DateUtils.zonedDateTimeToString(excludedOccurence));
153            }
154            result.put("excludedDates", excludedOccurencesStrings);
155        }
156
157        // creator
158        UserIdentity creatorIdentity = event.getCreator();
159        User creator = _userManager.getUser(creatorIdentity);
160        
161        result.put("creator", creatorIdentity);
162        result.put("creatorFullName", creator != null ? creator.getFullName() : creatorIdentity.getLogin());
163
164        UserIdentity user = _currentUserProvider.getUser();
165        result.put("isCreator", creatorIdentity.equals(user));
166        result.put("creationDate", DateUtils.zonedDateTimeToString(event.getCreationDate()));
167        
168        // last modification
169        UserIdentity contributorIdentity = event.getLastContributor();
170        User contributor = _userManager.getUser(contributorIdentity);
171        
172        result.put("contributor", contributorIdentity);
173        result.put("contributorFullName", contributor != null ? contributor.getFullName() : contributorIdentity.getLogin());
174        result.put("lastModified", DateUtils.zonedDateTimeToString(event.getLastModified()));
175        
176        if (fullInfo)
177        {
178            result.putAll(_eventAsJsonFullInfo(event));
179        }
180
181        result.put("calendar", _calendarDAO.getCalendarData(calendar, false, false)); 
182        
183        result.put("isStillSynchronized", _messagingConnectorCalendarManager.isEventStillSynchronized(event.getId()));
184        
185        // tags and places are expected by the client (respectively keywords and location on the server side)
186        result.put("tags", event.getTags());
187        
188        String location = StringUtils.defaultString(event.getLocation());
189        result.put("location", location);
190        result.put("places", Stream.of(location.split(",")).filter(StringUtils::isNotEmpty).collect(Collectors.toList()));
191        
192        // add event rights
193        result.put("rights", _extractEventRightData(event));
194        
195        result.put("resourceIds", event.getResources());
196        
197        return result;
198    }
199    
200    /**
201     * Retrieves the event additional info (rights, parent id, etc...)
202     * @param event The event
203     * @return the event additional info (rights, parent id, etc...) in a map
204     */
205    protected Map<String, Object> _eventAsJsonFullInfo(CalendarEvent event)
206    {
207        Map<String, Object> result = new HashMap<>();
208        
209        ExplorerNode explorerNode = event.getParent();
210        ExplorerNode root = explorerNode;
211        while (true)
212        {
213            if (root.getParent() instanceof ExplorerNode)
214            {
215                root = root.getParent();
216            }
217            else
218            {
219                break;
220            }
221        }
222        result.put("rootId", root.getId());
223        result.put("parentId", explorerNode.getId());
224        result.put("name", event.getName());
225        result.put("path", explorerNode.getExplorerPath());
226        result.put("isModifiable", true);
227        
228        result.put("rights", _getUserRights(explorerNode));
229        
230        return result;
231    }
232    
233    /**
234     * Internal method to extract the data concerning the right of the current user for an event
235     * @param event The event
236     * @return The map of right data. Keys are the rights id, and values indicates whether the current user has the right or not.
237     */
238    protected  Map<String, Object> _extractEventRightData(CalendarEvent event)
239    {
240        Map<String, Object> rightsData = new HashMap<>();
241        UserIdentity user = _currentUserProvider.getUser();
242        Calendar calendar = event.getParent();
243        
244        rightsData.put("edit", _rightManager.hasRight(user, AbstractCalendarDAO.RIGHTS_EVENT_EDIT, calendar) == RightResult.RIGHT_ALLOW);
245        rightsData.put("delete", _rightManager.hasRight(user, AbstractCalendarDAO.RIGHTS_EVENT_DELETE, calendar) == RightResult.RIGHT_ALLOW);
246        rightsData.put("delete-own", _rightManager.hasRight(user, AbstractCalendarDAO.RIGHTS_EVENT_DELETE_OWN, calendar) == RightResult.RIGHT_ALLOW);
247        
248        return rightsData;
249    }
250    
251    /**
252     * Get the user rights on the resource collection
253     * @param node The explorer node
254     * @return The user's rights
255     */
256    protected Set<String> _getUserRights(ExplorerNode node)
257    {
258        return _rightManager.getUserRights(_currentUserProvider.getUser(), node);
259    }
260    
261}