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.HashMap;
020import java.util.List;
021import java.util.Map;
022
023import javax.jcr.Node;
024import javax.jcr.RepositoryException;
025
026import org.apache.avalon.framework.component.Component;
027import org.apache.avalon.framework.context.Context;
028import org.apache.avalon.framework.context.ContextException;
029import org.apache.avalon.framework.context.Contextualizable;
030import org.apache.avalon.framework.logger.AbstractLogEnabled;
031import org.apache.avalon.framework.service.ServiceException;
032import org.apache.avalon.framework.service.ServiceManager;
033import org.apache.avalon.framework.service.Serviceable;
034import org.apache.cocoon.components.ContextHelper;
035import org.apache.cocoon.environment.Request;
036import org.apache.commons.lang3.StringUtils;
037
038import org.ametys.core.user.User;
039import org.ametys.core.user.UserIdentity;
040import org.ametys.core.user.UserManager;
041import org.ametys.core.util.I18nUtils;
042import org.ametys.plugins.messagingconnector.MessagingConnector;
043import org.ametys.plugins.repository.AmetysObjectResolver;
044import org.ametys.plugins.repository.RepositoryConstants;
045import org.ametys.plugins.workspaces.calendars.events.CalendarEventAttendee;
046import org.ametys.plugins.workspaces.calendars.jcr.JCRCalendarEvent;
047import org.ametys.plugins.workspaces.project.objects.Project;
048import org.ametys.runtime.i18n.I18nizableText;
049
050/**
051 * Manager for manipulating messaging connector linked to calendars event of a project
052 *
053 */
054public class MessagingConnectorCalendarManager extends AbstractLogEnabled implements Serviceable, Component, Contextualizable
055{
056    /** Avalon Role */
057    public static final String ROLE = MessagingConnectorCalendarManager.class.getName();
058    
059    /** Property's name for synchronized id id */
060    public static final String PROPERTY_SYNCHRONIZED_ID = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":synchronizedId";
061    
062    private MessagingConnector _messagingConnector;
063    private UserManager _userManager;
064    private AmetysObjectResolver _resolver;
065    private I18nUtils _i18nUtils;
066    private Context _context;
067    
068    @Override
069    public void contextualize(Context context) throws ContextException
070    {
071        _context = context;
072    }
073    
074    @Override
075    public void service(ServiceManager manager) throws ServiceException
076    {
077        boolean hasService = manager.hasService(MessagingConnector.ROLE);
078        _messagingConnector = hasService ? (MessagingConnector) manager.lookup(MessagingConnector.ROLE) : null;
079        _userManager = (UserManager) manager.lookup(UserManager.ROLE);
080        _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE);
081        _i18nUtils = (I18nUtils) manager.lookup(I18nUtils.ROLE);
082    }
083    
084    /**
085     * Set the synchronized id (for messaging connector) to the event
086     * @param event the event
087     * @param synchronizedId the synchronized id
088     */
089    public void setSynchronizedId(JCRCalendarEvent event, String synchronizedId)
090    {
091        try
092        {
093            event.getNode().setProperty(PROPERTY_SYNCHRONIZED_ID, synchronizedId);
094            event.saveChanges();
095        }
096        catch (RepositoryException e)
097        {
098            getLogger().error("An error occurred setting synchronized id for event " + event.getId());
099        }
100    }
101    
102    /**
103     * Get the synchronized id (for messaging connector) of the event
104     * @param event the event
105     * @return the synchronized id
106     */
107    public String getSynchronizedId(JCRCalendarEvent event)
108    {
109        try
110        {
111            Node eventNode = event.getNode();
112            if (eventNode.hasProperty(PROPERTY_SYNCHRONIZED_ID))
113            {
114                return eventNode.getProperty(PROPERTY_SYNCHRONIZED_ID).getString();
115            }
116        }
117        catch (RepositoryException e)
118        {
119            getLogger().error("An error occurred getting synchronized id for event " + event.getId());
120        }
121        
122        return null;
123    }
124    
125    /**
126     * Set attendees to the event
127     * @param eventId the event id
128     * @param attendees the list of attendees
129     */
130    public void setAttendees(String eventId, List<CalendarEventAttendee> attendees)
131    {
132        if (attendees != null)
133        {
134            JCRCalendarEvent event = (JCRCalendarEvent) _resolver.resolveById(eventId);
135            try
136            {
137                event.setAttendees(attendees);
138            }
139            catch (RepositoryException e)
140            {
141                getLogger().error("An error occurred setting attendees for event " + eventId, e);
142            }
143        }
144    }
145    
146    /**
147     * Set organiser to the event
148     * @param eventId the event id
149     * @param organiser the organiser
150     */
151    public void setOrganiser(String eventId, UserIdentity organiser)
152    {
153        JCRCalendarEvent event = (JCRCalendarEvent) _resolver.resolveById(eventId);
154        if (organiser != null)
155        {
156            event.setOrganiser(organiser);
157        }
158    }
159    
160    /**
161     * Add event invitation parameters (attendees and organiser)
162     * @param parameters the map of parameters
163     * @param eventId the event id
164     */
165    public void addEventInvitation(Map<String, Object> parameters, String eventId)
166    {
167        @SuppressWarnings("unchecked")
168        List<Map<String, Object>> attendeesList = (List<Map<String, Object>>) parameters.get("attendees");
169        List<CalendarEventAttendee> attendees = _getAttendeeListFromParameter(attendeesList);
170        setAttendees(eventId, attendees);
171        
172        @SuppressWarnings("unchecked")
173        Map<String, Object> organiserMap = (Map<String, Object>) parameters.get("organiser");
174        UserIdentity organiser = _getUserFromParameter(organiserMap);
175        setOrganiser(eventId, organiser);
176        
177        if (organiser != null)
178        {
179            JCRCalendarEvent event = _resolver.resolveById(eventId);
180            _createEvent(event, organiser, attendees);
181        }
182    }
183    
184    /**
185     * Edit event invitation parameters (attendees and organiser)
186     * @param parameters the map of parameters
187     * @param eventId the event id
188     */
189    public void editEventInvitation(Map<String, Object> parameters, String eventId)
190    {
191        JCRCalendarEvent event = _resolver.resolveById(eventId);
192
193        @SuppressWarnings("unchecked")
194        List<Map<String, Object>> attendeesList = (List<Map<String, Object>>) parameters.get("attendees");
195        List<CalendarEventAttendee> attendees = _getAttendeeListFromParameter(attendeesList);
196        try
197        {
198            if (attendees == null)
199            {
200                attendees = event.getAttendees();
201            }
202        }
203        catch (RepositoryException e)
204        {
205            getLogger().error("An error occurred getting attendees from repository for event " + event.getId());
206        }
207        setAttendees(eventId, attendees);
208
209        @SuppressWarnings("unchecked")
210        Map<String, Object> organiserMap = (Map<String, Object>) parameters.get("organiser");
211        UserIdentity organiserFromMap = _getUserFromParameter(organiserMap);
212        UserIdentity organiser = organiserFromMap != null ? organiserFromMap : event.getOrganiser();
213        setOrganiser(eventId, organiser);
214        
215        if (_messagingConnector != null)
216        {
217            String synchronizedId = getSynchronizedId(event);
218            if (StringUtils.isNotBlank(synchronizedId) && _messagingConnector.isEventExist(synchronizedId, organiser))
219            {
220                _editEvent(event, organiser, attendees);
221            }
222            else if (organiser != null)
223            {
224                _createEvent(event, organiser, attendees);
225            }
226        }
227    }
228    
229    /**
230     * Create event in the messaging connector
231     * @param event the event
232     * @param organiser the organiser
233     * @param attendees the list of attendee
234     */
235    protected void _createEvent(JCRCalendarEvent event, UserIdentity organiser, List<CalendarEventAttendee> attendees)
236    {
237        Map<String, Boolean> attendeesForMessagingConnector = new HashMap<>();
238        for (CalendarEventAttendee attendee : attendees)
239        {
240            if (attendee.isExternal())
241            {
242                attendeesForMessagingConnector.put(attendee.getEmail(), attendee.isMandatory());
243            }
244            else
245            {
246                User user = _userManager.getUser(new UserIdentity(attendee.getLogin(), attendee.getPopulationId()));
247                attendeesForMessagingConnector.put(user.getEmail(), attendee.isMandatory());
248            }
249        }
250        
251        String synchronizedId = _messagingConnector.createEvent(_getEventTitle(event), event.getDescription(), event.getLocation(), event.getFullDay(), event.getStartDate(), event.getEndDate(), event.getRecurrenceType(), event.getRepeatUntil(), attendeesForMessagingConnector, event.getOrganiser());
252        setSynchronizedId(event, synchronizedId);
253    }
254    
255    /**
256     * Delete event in the messaging connector
257     * @param event the event
258     */
259    public void deleteEvent(JCRCalendarEvent event)
260    {
261        if (isEventSynchronized(event.getId()))
262        {
263            String synchronizedId = getSynchronizedId(event);
264            _messagingConnector.deleteEvent(synchronizedId, event.getOrganiser());
265        }
266    }
267
268    /**
269     * True if the event is synchronized in the messaging connector
270     * @param eventId the event id
271     * @return true if the event exist
272     */
273    public boolean isEventSynchronized(String eventId)
274    {
275        if (_messagingConnector == null)
276        {
277            return false;
278        }
279        JCRCalendarEvent event = _resolver.resolveById(eventId);
280        String synchronizedId = getSynchronizedId(event);
281        
282        if (StringUtils.isBlank(synchronizedId))
283        {
284            return false;
285        }
286        
287        try
288        {
289            return _messagingConnector.isEventExist(synchronizedId, event.getOrganiser());
290        }
291        catch (UnsupportedOperationException e)
292        {
293            getLogger().error("An error occurred while checking if event " + eventId + " exists", e);
294            return false;
295        }
296    }
297    
298    /**
299     * Edit event in the messaging connector
300     * @param event the event
301     * @param organiser the organiser
302     * @param attendees the list of attendee
303     */
304    protected void _editEvent(JCRCalendarEvent event, UserIdentity organiser, List<CalendarEventAttendee> attendees)
305    {
306        String synchronizedId = getSynchronizedId(event);
307        Map<String, Boolean> attendeesForMessagingConnector = null;
308        if (attendees != null)
309        {
310            attendeesForMessagingConnector = new HashMap<>();
311            for (CalendarEventAttendee attendee : attendees)
312            {
313                if (attendee.isExternal())
314                {
315                    attendeesForMessagingConnector.put(attendee.getEmail(), attendee.isMandatory());
316                }
317                else
318                {
319                    User user = _userManager.getUser(new UserIdentity(attendee.getLogin(), attendee.getPopulationId()));
320                    attendeesForMessagingConnector.put(user.getEmail(), attendee.isMandatory());
321                }
322            }
323        }
324        
325        _messagingConnector.updateEvent(synchronizedId, _getEventTitle(event), event.getDescription(), event.getLocation(), event.getFullDay(), event.getStartDate(), event.getEndDate(), event.getRecurrenceType(), event.getRepeatUntil(), attendeesForMessagingConnector, event.getOrganiser());
326    }
327    
328    /**
329     * Get the computed title of the event
330     * @param event the event
331     * @return the computed title of the event
332     */
333    protected String _getEventTitle(JCRCalendarEvent event)
334    {
335        Calendar calendar = event.getParent();
336        Project project = calendar.getProject();
337        
338        List<String> parameters = new ArrayList<>();
339        parameters.add(project.getTitle());
340        parameters.add(event.getTitle());
341        I18nizableText titleI18n = new I18nizableText("plugin.workspaces", "PLUGINS_WORKSPACES_PROJECT_SERVICE_MODULE_CALENDAR_ADD_EVENT_MESSAGING_CONNECTOR_TITLE", parameters);
342        
343        Request request = ContextHelper.getRequest(_context);
344        String lang = (String) request.getAttribute("sitemapLanguage");
345        
346        return _i18nUtils.translate(titleI18n, lang);
347    }
348    
349    
350    /**
351     * Get user from the user parameter
352     * @param userMap the user map from parameters
353     * @return the user
354     */
355    protected UserIdentity _getUserFromParameter(Map<String, Object> userMap)
356    {
357        if (userMap != null)
358        {
359            String login = (String) userMap.get("login");
360            String populationId = (String) userMap.get("populationId");
361            
362            return new UserIdentity(login, populationId);
363        }
364        
365        return null;
366    }
367    
368    /**
369     * Get attendees list from the attendees parameter
370     * @param attendeesList the attendees list from parameters
371     * @return the attendees list
372     */
373    protected List<CalendarEventAttendee> _getAttendeeListFromParameter(List<Map<String, Object>> attendeesList)
374    {
375        if (attendeesList != null)
376        {
377            List<CalendarEventAttendee> attendees = new ArrayList<>();
378            for (Map<String, Object> attendee : attendeesList)
379            {
380                CalendarEventAttendee calendarEventAttendee = new CalendarEventAttendee();
381                boolean isExternal = (boolean) attendee.get("external");
382                if (isExternal)
383                {
384                    calendarEventAttendee.setEmail((String) attendee.get("email"));
385                }
386                else
387                {
388                    calendarEventAttendee.setPopulationId((String) attendee.get("populationId"));
389                    calendarEventAttendee.setLogin((String) attendee.get("login"));
390                }
391                
392                calendarEventAttendee.setIsExternal(isExternal);
393                calendarEventAttendee.setIsMandatory((boolean) attendee.get("mandatory"));
394                
395                attendees.add(calendarEventAttendee);
396            }
397            return attendees;
398        }
399        
400        return null;
401    }
402    
403}