View Javadoc
1   /*
2    * Copyright (c) 2002-2022, City of Paris
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met:
8    *
9    *  1. Redistributions of source code must retain the above copyright notice
10   *     and the following disclaimer.
11   *
12   *  2. Redistributions in binary form must reproduce the above copyright notice
13   *     and the following disclaimer in the documentation and/or other materials
14   *     provided with the distribution.
15   *
16   *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
17   *     contributors may be used to endorse or promote products derived from
18   *     this software without specific prior written permission.
19   *
20   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
24   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30   * POSSIBILITY OF SUCH DAMAGE.
31   *
32   * License 1.0
33   */
34  package fr.paris.lutece.plugins.appointment.web;
35  
36  import java.io.IOException;
37  import java.io.OutputStream;
38  import java.sql.Date;
39  import java.time.LocalDate;
40  import java.time.LocalDateTime;
41  import java.time.LocalTime;
42  import java.util.ArrayList;
43  import java.util.Arrays;
44  import java.util.Collections;
45  import java.util.Comparator;
46  import java.util.HashMap;
47  import java.util.LinkedHashMap;
48  import java.util.List;
49  import java.util.Locale;
50  import java.util.Map;
51  import java.util.StringJoiner;
52  import java.util.stream.Collectors;
53  import java.util.stream.Stream;
54  
55  import javax.servlet.http.HttpServletRequest;
56  import javax.servlet.http.HttpServletResponse;
57  import javax.servlet.http.HttpSession;
58  
59  import fr.paris.lutece.portal.service.file.FileService;
60  import fr.paris.lutece.portal.service.file.IFileStoreServiceProvider;
61  import org.apache.commons.collections.CollectionUtils;
62  import org.apache.commons.lang3.ArrayUtils;
63  import org.apache.commons.lang3.StringUtils;
64  
65  import fr.paris.lutece.api.user.User;
66  import fr.paris.lutece.plugins.appointment.business.appointment.Appointment;
67  import fr.paris.lutece.plugins.appointment.business.appointment.AppointmentSlot;
68  import fr.paris.lutece.plugins.appointment.business.form.Form;
69  import fr.paris.lutece.plugins.appointment.business.message.FormMessage;
70  import fr.paris.lutece.plugins.appointment.business.planning.WeekDefinition;
71  import fr.paris.lutece.plugins.appointment.business.rule.FormRule;
72  import fr.paris.lutece.plugins.appointment.business.rule.ReservationRule;
73  import fr.paris.lutece.plugins.appointment.business.slot.Slot;
74  import fr.paris.lutece.plugins.appointment.exception.AppointmentSavedException;
75  import fr.paris.lutece.plugins.appointment.exception.SlotEditTaskExpiredTimeException;
76  import fr.paris.lutece.plugins.appointment.exception.SlotFullException;
77  import fr.paris.lutece.plugins.appointment.log.LogUtilities;
78  import fr.paris.lutece.plugins.appointment.service.AppointmentResourceIdService;
79  import fr.paris.lutece.plugins.appointment.service.AppointmentResponseService;
80  import fr.paris.lutece.plugins.appointment.service.AppointmentService;
81  import fr.paris.lutece.plugins.appointment.service.AppointmentUtilities;
82  import fr.paris.lutece.plugins.appointment.service.CommentService;
83  import fr.paris.lutece.plugins.appointment.service.EntryService;
84  import fr.paris.lutece.plugins.appointment.service.FormMessageService;
85  import fr.paris.lutece.plugins.appointment.service.FormRuleService;
86  import fr.paris.lutece.plugins.appointment.service.FormService;
87  import fr.paris.lutece.plugins.appointment.service.ReservationRuleService;
88  import fr.paris.lutece.plugins.appointment.service.SlotSafeService;
89  import fr.paris.lutece.plugins.appointment.service.SlotService;
90  import fr.paris.lutece.plugins.appointment.service.Utilities;
91  import fr.paris.lutece.plugins.appointment.service.WeekDefinitionService;
92  import fr.paris.lutece.plugins.appointment.service.addon.AppointmentAddOnManager;
93  import fr.paris.lutece.plugins.appointment.service.export.AppointmentExportService;
94  import fr.paris.lutece.plugins.appointment.service.export.ExcelAppointmentGenerator;
95  import fr.paris.lutece.plugins.appointment.service.listeners.AppointmentListenerManager;
96  import fr.paris.lutece.plugins.appointment.service.upload.AppointmentAsynchronousUploadHandler;
97  import fr.paris.lutece.plugins.appointment.web.dto.AppointmentDTO;
98  import fr.paris.lutece.plugins.appointment.web.dto.AppointmentFilterDTO;
99  import fr.paris.lutece.plugins.appointment.web.dto.AppointmentFormDTO;
100 import fr.paris.lutece.plugins.filegenerator.service.TemporaryFileGeneratorService;
101 import fr.paris.lutece.plugins.genericattributes.business.Entry;
102 import fr.paris.lutece.plugins.genericattributes.business.EntryHome;
103 import fr.paris.lutece.plugins.genericattributes.business.GenericAttributeError;
104 import fr.paris.lutece.plugins.genericattributes.business.Response;
105 import fr.paris.lutece.plugins.genericattributes.business.ResponseHome;
106 import fr.paris.lutece.plugins.workflowcore.business.state.State;
107 import fr.paris.lutece.plugins.workflowcore.business.state.StateFilter;
108 import fr.paris.lutece.plugins.workflowcore.service.state.StateService;
109 import fr.paris.lutece.plugins.workflowcore.service.task.ITask;
110 import fr.paris.lutece.plugins.workflowcore.service.task.ITaskService;
111 import fr.paris.lutece.plugins.workflowcore.service.task.TaskService;
112 import fr.paris.lutece.portal.business.file.File;
113 import fr.paris.lutece.portal.business.user.AdminUser;
114 import fr.paris.lutece.portal.business.user.AdminUserHome;
115 import fr.paris.lutece.portal.service.admin.AccessDeniedException;
116 import fr.paris.lutece.portal.service.admin.AdminAuthenticationService;
117 import fr.paris.lutece.portal.service.i18n.I18nService;
118 import fr.paris.lutece.portal.service.mailinglist.AdminMailingListService;
119 import fr.paris.lutece.portal.service.message.AdminMessage;
120 import fr.paris.lutece.portal.service.message.AdminMessageService;
121 import fr.paris.lutece.portal.service.plugin.Plugin;
122 import fr.paris.lutece.portal.service.plugin.PluginService;
123 import fr.paris.lutece.portal.service.rbac.RBACService;
124 import fr.paris.lutece.portal.service.spring.SpringContextService;
125 import fr.paris.lutece.portal.service.template.AppTemplateService;
126 import fr.paris.lutece.portal.service.util.AppLogService;
127 import fr.paris.lutece.portal.service.util.AppPathService;
128 import fr.paris.lutece.portal.service.util.AppPropertiesService;
129 import fr.paris.lutece.portal.service.workflow.WorkflowService;
130 import fr.paris.lutece.portal.util.mvc.admin.MVCAdminJspBean;
131 import fr.paris.lutece.portal.util.mvc.admin.annotations.Controller;
132 import fr.paris.lutece.portal.util.mvc.commons.annotations.Action;
133 import fr.paris.lutece.portal.util.mvc.commons.annotations.View;
134 import fr.paris.lutece.portal.util.mvc.utils.MVCUtils;
135 import fr.paris.lutece.portal.web.util.LocalizedPaginator;
136 import fr.paris.lutece.util.ReferenceList;
137 import fr.paris.lutece.util.html.AbstractPaginator;
138 import fr.paris.lutece.util.html.HtmlTemplate;
139 import fr.paris.lutece.util.url.UrlItem;
140 
141 /**
142  * This class provides the user interface to manage Appointment features ( manage, create, modify, remove )
143  * 
144  * @author Laurent Payen
145  * 
146  */
147 @Controller( controllerJsp = "ManageAppointments.jsp", controllerPath = "jsp/admin/plugins/appointment/", right = AppointmentFormJspBean.RIGHT_MANAGEAPPOINTMENTFORM )
148 public class AppointmentJspBean extends MVCAdminJspBean
149 {
150     /**
151      * Serial version UID
152      */
153     private static final long serialVersionUID = 1978001810468444844L;
154     private static final String PARAMETER_PAGE_INDEX = "page_index";
155 
156     // //////////////////////////////////////////////////////////////////////////
157     // Constants
158 
159     // templates
160     private static final String TEMPLATE_MANAGE_APPOINTMENTS_CALENDAR = "/admin/plugins/appointment/appointment/manage_appointments_calendar.html";
161     private static final String TEMPLATE_MANAGE_APPOINTMENTS_CALENDAR_MULTI_SLOT = "/admin/plugins/appointment/appointment/manage_appointments_multislot_calendar.html";
162     private static final String TEMPLATE_MANAGE_APPOINTMENTS_CALENDAR_GROUPED = "/admin/plugins/appointment/appointment/appointment_form_list_open_slots_grouped.html";
163     private static final String TEMPLATE_CREATE_APPOINTMENT = "/admin/plugins/appointment/appointment/create_appointment.html";
164     private static final String TEMPLATE_MANAGE_APPOINTMENTS = "/admin/plugins/appointment/appointment/manage_appointments.html";
165     private static final String TEMPLATE_VIEW_APPOINTMENT = "/admin/plugins/appointment/appointment/view_appointment.html";
166     private static final String TEMPLATE_HTML_CODE_FORM_ADMIN = "admin/plugins/appointment/html_code_form.html";
167     private static final String TEMPLATE_APPOINTMENT_FORM_RECAP = "/admin/plugins/appointment/appointment/appointment_form_recap.html";
168     private static final String TEMPLATE_TASKS_FORM_WORKFLOW = "admin/plugins/appointment/appointment/tasks_form_workflow.html";
169 
170     // Properties for page titles
171     private static final String PROPERTY_PAGE_TITLE_MANAGE_APPOINTMENTS = "appointment.manageAppointments.pageTitle";
172     private static final String PROPERTY_PAGE_TITLE_MANAGE_APPOINTMENTS_CALENDAR = "appointment.manageAppointmentCalendar.pageTitle";
173     private static final String PROPERTY_PAGE_TITLE_CREATE_APPOINTMENT = "appointment.name.create";
174     private static final String PROPERTY_PAGE_TITLE_VIEW_APPOINTMENT = "appointment.viewAppointment.pageTitle";
175     private static final String PROPERTY_PAGE_TITLE_RECAP_APPOINTMENT = "appointment.appointmentApp.recap.title";
176     private static final String PROPERTY_PAGE_TITLE_TASKS_FORM_WORKFLOW = "appointment.taskFormWorkflow.pageTitle";
177 
178     private static final String UNRESERVED = "appointment.message.labelStatusUnreserved";
179     private static final String RESERVED = "appointment.message.labelStatusReserved";
180 
181     // Parameters
182     private static final String PARAMETER_ID_RESPONSE = "idResponse";
183     private static final String PARAMETER_STARTING_DATE_TIME = "starting_date_time";
184     private static final String PARAMETER_ENDING_DATE_TIME = "ending_date_time";
185     private static final String PARAMETER_STARTING_DATE_OF_DISPLAY = "starting_date_of_display";
186     private static final String PARAMETER_STR_STARTING_DATE_OF_DISPLAY = "str_starting_date_of_display";
187     private static final String PARAMETER_ENDING_DATE_OF_DISPLAY = "ending_date_of_display";
188     private static final String PARAMETER_STR_ENDING_DATE_OF_DISPLAY = "str_ending_date_of_display";
189     private static final String PARAMETER_DATE_OF_DISPLAY = "date_of_display";
190     private static final String PARAMETER_DAY_OF_WEEK = "dow";
191     private static final String PARAMETER_EVENTS = "events";
192     private static final String PARAMETER_EVENTS_COMMENTS = "comment_events";
193     private static final String PARAMETER_MIN_DURATION = "min_duration";
194     private static final String PARAMETER_MIN_TIME = "min_time";
195     private static final String PARAMETER_MAX_TIME = "max_time";
196     private static final String PARAMETER_ID_APPOINTMENT = "id_appointment";
197     private static final String PARAMETER_ID_ACTION = "id_action";
198     private static final String PARAMETER_ID_FORM = "id_form";
199     private static final String PARAMETER_COME_FROM_CALENDAR = "comeFromCalendar";
200     private static final String PARAMETER_EMAIL = "email";
201     private static final String PARAMETER_EMAIL_CONFIRMATION = "emailConfirm";
202     private static final String PARAMETER_FIRST_NAME = "firstname";
203     private static final String PARAMETER_LAST_NAME = "lastname";
204     private static final String PARAMETER_BACK = "back";
205     private static final String PARAMETER_ORDER_BY = "orderBy";
206     private static final String PARAMETER_ORDER_ASC = "orderAsc";
207     private static final String PARAMETER_ID_APPOINTMENT_DELETE = "apmt";
208     private static final String PARAMETER_DELETE_AND_BACK = "eraseAll";
209     private static final String PARAMETER_SEARCH = "Search";
210     private static final String PARAMETER_RESET = "reset";
211     private static final String PARAMETER_NUMBER_OF_BOOKED_SEATS = "nbBookedSeats";
212     private static final String PARAMETER_STATUS_CANCELLED = "status_cancelled";
213     private static final String PARAMETER_MODIF_DATE = "modif_date";
214     private static final String PARAMETER_IS_MODIFICATION = "is_modification";
215     private static final String PARAMETER_NB_PLACE_TO_TAKE = "nbPlacesToTake";
216     private static final String PARAMETER_SELECTED_DEFAULT_FIELD = "selectedDefaultFieldList";
217     private static final String PARAMETER_SELECTED_CUSTOM_FIELD = "selectedCustomFieldList";
218 
219     // Markers
220     private static final String MARK_TASKS_FORM = "tasks_form";
221     private static final String MARK_APPOINTMENT_LIST = "appointment_list";
222     private static final String MARK_APPOINTMENT = "appointment";
223     private static final String MARK_PAGINATOR = "paginator";
224     private static final String MARK_NB_ITEMS_PER_PAGE = "nb_items_per_page";
225     private static final String MARK_FORM_MESSAGES = "formMessages";
226     private static final String MARK_FORM_HTML = "form_html";
227     private static final String MARK_FORM = "form";
228     private static final String MARK_MODIFICATION_DATE_APPOINTMENT = "modifDateAppointment";
229     private static final String MARK_FORM_CALENDAR_ERRORS = "formCalendarErrors";
230     private static final String MARK_FORM_ERRORS = "form_errors";
231     private static final String MARK_LIST_ERRORS = "listAllErrors";
232     private static final String MARK_LOCALE = "locale";
233     private static final String MARK_PLACES = "nbplaces";
234     private static final String MARK_STR_ENTRY = "str_entry";
235     private static final String MARK_RIGHT_CREATE = "rightCreate";
236     private static final String MARK_RIGHT_DELETE = "rightDelete";
237     private static final String MARK_RIGHT_VIEW = "rightView";
238     private static final String MARK_RIGHT_CHANGE_STATUS = "rightChangeStatus";
239     private static final String MARK_FILTER = "filter";
240     private static final String MARK_LIST_STATUS = "listStatus";
241     private static final String MARK_RESOURCE_HISTORY = "resource_history";
242     private static final String MARK_ADDON = "addon";
243     private static final String MARK_LIST_RESPONSE_RECAP_DTO = "listResponseRecapDTO";
244     private static final String MARK_LANGUAGE = "language";
245     private static final String MARK_ACTIVATE_WORKFLOW = "activateWorkflow";
246     private static final String MARK_FORM_OVERBOOKING_ALLOWED = "overbookingAllowed";
247     private static final String MARK_DEFAULT_FIELD_LIST = "defaultFieldList";
248     private static final String MARK_CUSTOM_FIELD_LIST = "customFieldList";
249     private static final String MARK_IS_OVERBOOKING = "isOverbooking";
250     private static final String MARK_MAILING_LIST = "mailing_list";
251     private static final String MARK_USER_CREATOR = "userCreator";
252     private static final String JSP_MANAGE_APPOINTMENTS = "jsp/admin/plugins/appointment/ManageAppointments.jsp";
253     private static final String ERROR_MESSAGE_SLOT_FULL = "appointment.message.error.slotFull";
254     private static final String ERROR_MESSAGE_SLOT_EDIT_TASK_EXPIRED_TIME = "appointment.message.error.appointment.edit.expired.time";
255     private static final String MARK_APPOINTMENT_DESK_ENABLED = "isDeskInstalled";
256 
257     // Messages
258     private static final String MESSAGE_CONFIRM_REMOVE_APPOINTMENT = "appointment.message.confirmRemoveAppointment";
259     private static final String MESSAGE_CONFIRM_REMOVE_MASSAPPOINTMENT = "appointment.message.confirmRemoveMassAppointment";
260     private static final String ERROR_MESSAGE_NB_MAX_APPOINTMENTS_ON_A_CATEGORY = "appointment.validation.appointment.NbMaxAppointmentsOnCategory.error";
261     private static final String ERROR_MESSAGE_NB_MIN_DAYS_BETWEEN_TWO_APPOINTMENTS = "appointment.validation.appointment.NbMinDaysBetweenTwoAppointments.error";
262     private static final String ERROR_MESSAGE_NB_MAX_APPOINTMENTS_ON_A_PERIOD = "appointment.validation.appointment.NbMaxAppointmentsOnAPeriod.error";
263 
264     // Properties
265     private static final String PROPERTY_DEFAULT_LIST_APPOINTMENT_PER_PAGE = "appointment.listAppointments.itemsPerPage";
266     private static final String PROPERTY_NB_WEEKS_TO_DISPLAY_IN_BO = "appointment.nbWeeksToDisplayInBO";
267     private static final String PROPERTY_MODULE_APPOINTMENT_DESK_NAME = "appointment.moduleAppointmentDesk.name";
268 
269     // Views
270     private static final String VIEW_MANAGE_APPOINTMENTS = "manageAppointments";
271     private static final String VIEW_CREATE_APPOINTMENT = "createAppointment";
272     private static final String VIEW_VIEW_APPOINTMENT = "viewAppointment";
273     private static final String VIEW_DISPLAY_RECAP_APPOINTMENT = "displayRecapAppointment";
274     private static final String VIEW_CALENDAR_MANAGE_APPOINTMENTS = "viewCalendarManageAppointment";
275     private static final String VIEW_WORKFLOW_ACTION_FORM = "viewWorkflowActionForm";
276     private static final String VIEW_CHANGE_DATE_APPOINTMENT = "viewChangeDateAppointment";
277 
278     // Actions
279     private static final String ACTION_DO_VALIDATE_FORM = "doValidateForm";
280     private static final String ACTION_REMOVE_APPOINTMENT = "removeAppointment";
281     private static final String ACTION_REMOVE_MASSAPPOINTMENT = "removeMassAppointment";
282     private static final String ACTION_CONFIRM_REMOVE_APPOINTMENT = "confirmRemoveAppointment";
283     private static final String ACTION_CONFIRM_REMOVE_MASS_APPOINTMENT = "confirmRemoveMassAppointment";
284     private static final String ACTION_DO_MAKE_APPOINTMENT = "doMakeAppointment";
285     private static final String ACTION_DO_PROCESS_WORKFLOW_ACTION = "doProcessWorkflowAction";
286     private static final String ACTION_DO_CHANGE_APPOINTMENT_STATUS = "doChangeAppointmentStatus";
287     private static final String ACTION_EXPORT_APPOINTMENTS = "doExportAppointments";
288 
289     // Infos
290     private static final String INFO_APPOINTMENT_CREATED = "appointment.info.appointment.created";
291     private static final String INFO_APPOINTMENT_REMOVED = "appointment.info.appointment.removed";
292     private static final String INFO_APPOINTMENT_MASSREMOVED = "appointment.info.appointment.removed";
293 
294     // Error
295     private static final String ERROR_MESSAGE_FORM_NOT_ACTIVE = "appointment.validation.appointment.formNotActive";
296     private static final String ERROR_MESSAGE_NO_STARTING_VALIDITY_DATE = "appointment.validation.appointment.noStartingValidityDate";
297     private static final String ERROR_MESSAGE_FORM_NO_MORE_VALID = "appointment.validation.appointment.formNoMoreValid";
298     private static final String MESSAGE_UNVAILABLE_SLOT = "appointment.slot.unvailable";
299     private static final String ERROR_MESSAGE_REPORT_APPOINTMENT = "appointment.message.error.report.appointment";
300 
301     // Constants
302     private static final String DEFAULT_CURRENT_PAGE = "1";
303     public static final String ACTIVATEWORKFLOW = AppPropertiesService.getProperty( "appointment.activate.workflow" );
304     public static final String PREVIOUS_FORM = "calendar";
305     private static final String LAST_NAME = "last_name";
306     private static final String FIRST_NAME = "first_name";
307     private static final String EMAIL = "email";
308     private static final String PHONE_NUMBER = "phone_number";
309     private static final String NB_BOOKED_SEATS = "nbBookedSeats";
310     private static final String DATE_APPOINTMENT = "date_appointment";
311     private static final String ADMIN = "admin";
312     private static final String STATUS = "status";
313     // services
314 
315     // Session variable to store working values
316     private String _strCurrentPageIndex;
317     private int _nItemsPerPage;
318     private int _nDefaultItemsPerPage;
319     private int _nNbPlacesToTake;
320     private AppointmentFilterDTO _filter;
321     private AppointmentFormDTO _appointmentForm;
322     private AppointmentDTO _notValidatedAppointment;
323     private AppointmentDTO _validatedAppointment;
324     private List<Integer> _listAppointmentsIds;
325     List<GenericAttributeError> listFormErrors = new ArrayList<>( );
326     Plugin _moduleAppointmentDesk = PluginService.getPlugin( AppPropertiesService.getProperty( PROPERTY_MODULE_APPOINTMENT_DESK_NAME ) );
327 
328     /**
329      * Get the page to manage appointments. Appointments are displayed in a calendar.
330      * 
331      * @param request
332      *            The request
333      * @return The HTML code to display
334      * @throws AccessDeniedException
335      */
336     @View( value = VIEW_CALENDAR_MANAGE_APPOINTMENTS, defaultView = true )
337     public synchronized String getViewCalendarManageAppointments( HttpServletRequest request ) throws AccessDeniedException
338     {
339         String strIdForm = request.getParameter( PARAMETER_ID_FORM );
340 
341         if ( !RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE, strIdForm, AppointmentResourceIdService.PERMISSION_VIEW_FORM, (User) getUser( ) ) )
342         {
343             throw new AccessDeniedException( AppointmentResourceIdService.PERMISSION_VIEW_FORM );
344         }
345 
346         cleanSession( request.getSession( ) );
347         String strIdAppointment = request.getParameter( PARAMETER_ID_APPOINTMENT );
348         String nbPlacesToTake = request.getParameter( PARAMETER_NB_PLACE_TO_TAKE );
349         boolean bError = false;
350 
351         if ( StringUtils.isNotEmpty( strIdAppointment ) )
352         {
353             // If we want to change the date of an appointment
354             AppointmentDTO appointmentDTO = AppointmentService.buildAppointmentDTOFromIdAppointment( Integer.parseInt( strIdAppointment ) );
355             if ( appointmentDTO.getIsCancelled( ) )
356             {
357                 addError( ERROR_MESSAGE_REPORT_APPOINTMENT, getLocale( ) );
358                 bError = true;
359             }
360             else
361             {
362                 _validatedAppointment = appointmentDTO;
363                 AppointmentService.addAppointmentResponses( _validatedAppointment );
364                 nbPlacesToTake = Integer.toString( _validatedAppointment.getNbBookedSeats( ) );
365             }
366         }
367         int nIdForm = Integer.parseInt( strIdForm );
368         AppointmentFormDTO appointmentForm = FormService.buildAppointmentFormWithoutReservationRule( nIdForm );
369 
370         if ( !appointmentForm.getIsActive( ) )
371         {
372             addError( ERROR_MESSAGE_FORM_NOT_ACTIVE, getLocale( ) );
373             bError = true;
374         }
375         // Check if the date of display and the endDateOfDisplay are in the
376         // validity date range of the form
377         if ( appointmentForm.getDateStartValidity( ) == null )
378         {
379             addError( ERROR_MESSAGE_NO_STARTING_VALIDITY_DATE, getLocale( ) );
380             bError = true;
381         }
382 
383         int nNbWeeksToDisplay = AppPropertiesService.getPropertyInt( PROPERTY_NB_WEEKS_TO_DISPLAY_IN_BO, appointmentForm.getNbWeeksToDisplay( ) );
384         LocalDate startingDateOfDisplay = LocalDate.now( ).minusWeeks( nNbWeeksToDisplay );
385         LocalDate endingDateOfDisplay = LocalDate.now( ).plusWeeks( nNbWeeksToDisplay );
386         if ( appointmentForm.getDateEndValidity( ) != null )
387         {
388             if ( startingDateOfDisplay.isAfter( endingDateOfDisplay ) )
389             {
390                 addError( ERROR_MESSAGE_FORM_NO_MORE_VALID, getLocale( ) );
391                 bError = true;
392             }
393             if ( endingDateOfDisplay.isAfter( appointmentForm.getDateEndValidity( ).toLocalDate( ) ) )
394             {
395                 endingDateOfDisplay = appointmentForm.getDateEndValidity( ).toLocalDate( );
396             }
397         }
398         String strDateOfDisplay = request.getParameter( PARAMETER_DATE_OF_DISPLAY );
399         LocalDate dateOfDisplay = LocalDate.now( );
400         if ( StringUtils.isNotEmpty( strDateOfDisplay ) )
401         {
402             dateOfDisplay = LocalDate.parse( strDateOfDisplay );
403         }
404         List<Slot> listSlot = new ArrayList<>( );
405         List<WeekDefinition> listWeekDefinition = WeekDefinitionService.findListWeekDefinition( nIdForm );
406         Map<WeekDefinition, ReservationRule> mapReservationRule = ReservationRuleService.findAllReservationRule( nIdForm, listWeekDefinition );
407         List<ReservationRule> listReservationRules = new ArrayList<>( mapReservationRule.values( ) );
408 
409         LocalTime maxEndingTime = WeekDefinitionService.getMaxEndingTimeOfAListOfWeekDefinition( listReservationRules );
410         LocalTime minStartingTime = WeekDefinitionService.getMinStartingTimeOfAListOfWeekDefinition( listReservationRules );
411         List<String> listDayOfWeek = new ArrayList<>( WeekDefinitionService.getSetDaysOfWeekOfAListOfWeekDefinitionForFullCalendar( listReservationRules ) );
412         if ( !bError )
413         {
414 
415             boolean isNewNbPlacesToTake = ( nbPlacesToTake != null && StringUtils.isNumeric( nbPlacesToTake ) );
416             if ( appointmentForm.getIsMultislotAppointment( ) && ( ( _nNbPlacesToTake != 0 || isNewNbPlacesToTake ) && nbPlacesToTake != null ) )
417             {
418 
419                 _nNbPlacesToTake = isNewNbPlacesToTake ? Integer.parseInt( nbPlacesToTake ) : _nNbPlacesToTake;
420                 listSlot = SlotService.buildListSlot( nIdForm, mapReservationRule, startingDateOfDisplay, endingDateOfDisplay, _nNbPlacesToTake,
421                         appointmentForm.getBoOverbooking( ) && RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE, strIdForm,
422                                 AppointmentResourceIdService.PERMISSION_OVERBOOKING_FORM, (User) getUser( ) ) );
423 
424             }
425             else
426             {
427 
428                 _nNbPlacesToTake = 0;
429                 listSlot = SlotService.buildListSlot( nIdForm, mapReservationRule, startingDateOfDisplay, endingDateOfDisplay );
430             }
431 
432             // Tag as passed the slots passed
433             List<Slot> listSlotsPassed = listSlot.stream( ).filter( s -> s.getEndingDateTime( ).isBefore( LocalDateTime.now( ) ) )
434                     .collect( Collectors.toList( ) );
435             for ( Slot slotPassed : listSlotsPassed )
436             {
437                 slotPassed.setIsPassed( Boolean.TRUE );
438             }
439         }
440         Map<String, Object> model = getModel( );
441         if ( bError )
442         {
443             model.put( MARK_FORM_CALENDAR_ERRORS, bError );
444         }
445         // If we change the date of an appointment
446         // filter the list of slot with only the ones that have enough places at
447         // the moment of the edition
448         if ( _validatedAppointment != null )
449         {
450             int nbBookedSeats = _validatedAppointment.getNbBookedSeats( );
451             listSlot = listSlot.stream( ).filter( s -> s.getNbPotentialRemainingPlaces( ) >= nbBookedSeats && s.getIsOpen( ) ).collect( Collectors.toList( ) );
452             model.put( MARK_MODIFICATION_DATE_APPOINTMENT, true );
453         }
454         else
455         {
456             model.put( MARK_MODIFICATION_DATE_APPOINTMENT, false );
457         }
458 
459         model.put( MARK_FORM, appointmentForm );
460         model.put( PARAMETER_ID_FORM, nIdForm );
461         model.put( MARK_FORM_MESSAGES, FormMessageService.findFormMessageByIdForm( nIdForm ) );
462         model.put( PARAMETER_STARTING_DATE_OF_DISPLAY, startingDateOfDisplay );
463         model.put( PARAMETER_STR_STARTING_DATE_OF_DISPLAY, startingDateOfDisplay.format( Utilities.getFormatter( ) ) );
464         model.put( PARAMETER_ENDING_DATE_OF_DISPLAY, endingDateOfDisplay );
465         model.put( PARAMETER_STR_ENDING_DATE_OF_DISPLAY, endingDateOfDisplay.format( Utilities.getFormatter( ) ) );
466         model.put( PARAMETER_DATE_OF_DISPLAY, dateOfDisplay );
467         model.put( PARAMETER_DAY_OF_WEEK, listDayOfWeek );
468         model.put( PARAMETER_EVENTS, listSlot );
469         model.put( PARAMETER_EVENTS_COMMENTS, CommentService
470                 .buildCommentDTO( CommentService.finListComments( Date.valueOf( startingDateOfDisplay ), Date.valueOf( endingDateOfDisplay ), nIdForm ) ) );
471         model.put( PARAMETER_MIN_TIME, minStartingTime );
472         model.put( PARAMETER_MAX_TIME, maxEndingTime );
473         model.put( PARAMETER_MIN_DURATION, LocalTime.MIN.plusMinutes( AppointmentUtilities.THIRTY_MINUTES ) );
474         model.put( MARK_FORM_OVERBOOKING_ALLOWED, appointmentForm.getBoOverbooking( ) && RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE, strIdForm,
475 
476                 AppointmentResourceIdService.PERMISSION_OVERBOOKING_FORM, (User) getUser( ) ) );
477         model.put( MARK_LOCALE, getLocale( ) );
478         model.put( MARK_MAILING_LIST, AdminMailingListService.getMailingLists( getUser( ) ) );
479         model.put( AppointmentUtilities.MARK_PERMISSION_ADD_COMMENT, String.valueOf( RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE,
480                 String.valueOf( appointmentForm.getIdForm( ) ), AppointmentResourceIdService.PERMISSION_ADD_COMMENT_FORM, (User) getUser( ) ) ) );
481         model.put( AppointmentUtilities.MARK_PERMISSION_MODERATE_COMMENT, String.valueOf( RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE,
482                 String.valueOf( appointmentForm.getIdForm( ) ), AppointmentResourceIdService.PERMISSION_MODERATE_COMMENT_FORM, (User) getUser( ) ) ) );
483         model.put( AppointmentUtilities.MARK_PERMISSION_ACCESS_CODE, getUser( ).getAccessCode( ) );
484         model.put( MARK_APPOINTMENT_DESK_ENABLED, ( _moduleAppointmentDesk != null && _moduleAppointmentDesk.isInstalled( ) ) );
485 
486         if ( appointmentForm.getIsMultislotAppointment( ) && _nNbPlacesToTake <= 0 )
487         {
488 
489             return getPage( PROPERTY_PAGE_TITLE_MANAGE_APPOINTMENTS_CALENDAR, TEMPLATE_MANAGE_APPOINTMENTS_CALENDAR_MULTI_SLOT, model );
490 
491         }
492         else
493             if ( appointmentForm.getIsMultislotAppointment( ) && _nNbPlacesToTake >= 1 )
494             {
495 
496                 return getPage( PROPERTY_PAGE_TITLE_MANAGE_APPOINTMENTS_CALENDAR, TEMPLATE_MANAGE_APPOINTMENTS_CALENDAR_GROUPED, model );
497 
498             }
499             else
500             {
501 
502                 return getPage( PROPERTY_PAGE_TITLE_MANAGE_APPOINTMENTS_CALENDAR, TEMPLATE_MANAGE_APPOINTMENTS_CALENDAR, model );
503             }
504     }
505 
506     /**
507      * Get the page to manage appointments
508      * 
509      * @param request
510      *            The request
511      * @return The HTML code to display
512      * @throws AccessDeniedException
513      */
514     @View( value = VIEW_MANAGE_APPOINTMENTS )
515     public synchronized String getManageAppointments( HttpServletRequest request ) throws AccessDeniedException
516     {
517         String strIdForm = request.getParameter( PARAMETER_ID_FORM );
518 
519         if ( !RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE, strIdForm, AppointmentResourceIdService.PERMISSION_VIEW_FORM, (User) getUser( ) ) )
520         {
521             throw new AccessDeniedException( AppointmentResourceIdService.PERMISSION_VIEW_FORM );
522         }
523         String strModifDateAppointment = request.getParameter( PARAMETER_MODIF_DATE );
524         if ( strModifDateAppointment != null && Boolean.parseBoolean( strModifDateAppointment ) )
525         {
526             return getViewChangeDateAppointment( request );
527         }
528         // Clean session
529         AppointmentAsynchronousUploadHandler.getHandler( ).removeSessionFiles( request.getSession( ) );
530         _notValidatedAppointment = null;
531         _validatedAppointment = null;
532         _appointmentForm = null;
533         int nIdForm = Integer.parseInt( strIdForm );
534 
535         // If it is a new search
536         if ( request.getParameter( PARAMETER_SEARCH ) != null )
537         {
538             // Populate the filter
539             populate( _filter, request );
540         }
541         else
542             if ( request.getParameter( PARAMETER_RESET ) != null || _filter == null || _filter.getIdForm( ) != nIdForm )
543             {
544                 _filter = new AppointmentFilterDTO( );
545                 _filter.setIdForm( nIdForm );
546                 // if we come from the calendar, need to get the starting and ending
547                 // time of the slot
548                 String strStartingDateTime = request.getParameter( PARAMETER_STARTING_DATE_TIME );
549                 String strEndingDateTime = request.getParameter( PARAMETER_ENDING_DATE_TIME );
550                 if ( strStartingDateTime != null && strEndingDateTime != null )
551                 {
552                     LocalDateTime startingDateTime = LocalDateTime.parse( strStartingDateTime );
553                     LocalDateTime endingDateTime = LocalDateTime.parse( strEndingDateTime );
554                     _filter.setStartingDateOfSearch( Date.valueOf( startingDateTime.toLocalDate( ) ) );
555                     _filter.setStartingTimeOfSearch( startingDateTime.toLocalTime( ).toString( ) );
556                     _filter.setEndingDateOfSearch( Date.valueOf( endingDateTime.toLocalDate( ) ) );
557                     _filter.setEndingTimeOfSearch( endingDateTime.toLocalTime( ).toString( ) );
558                 }
559             }
560         _strCurrentPageIndex = AbstractPaginator.getPageIndex( request, AbstractPaginator.PARAMETER_PAGE_INDEX, _strCurrentPageIndex );
561         String strOrderBy = request.getParameter( PARAMETER_ORDER_BY );
562         String strOrderAsc = request.getParameter( PARAMETER_ORDER_ASC );
563         if ( strOrderBy == null )
564         {
565             strOrderBy = DATE_APPOINTMENT;
566         }
567 
568         boolean bAsc = Boolean.FALSE;
569         if ( strOrderAsc != null )
570         {
571             bAsc = Boolean.parseBoolean( strOrderAsc );
572         }
573         _filter.setOrderBy( strOrderBy );
574         _filter.setOrderAsc( bAsc );
575 
576 
577         if ( _strCurrentPageIndex == null )
578         {
579             _strCurrentPageIndex = DEFAULT_CURRENT_PAGE;
580         }
581         if ( DEFAULT_CURRENT_PAGE.equals( _strCurrentPageIndex ) )
582         {
583             _listAppointmentsIds = AppointmentService.findListAppointmentsIdsByFilter( _filter );
584         }
585         _nItemsPerPage = AbstractPaginator.getItemsPerPage( request, AbstractPaginator.PARAMETER_ITEMS_PER_PAGE, _nItemsPerPage, _nDefaultItemsPerPage );
586         List<AppointmentDTO> listAppointmentsDTO = findListAppointmentsDTOByFilterByPage( );
587         // If it is an order by
588         listAppointmentsDTO = orderList( listAppointmentsDTO );
589         if ( StringUtils.isNotEmpty( request.getParameter( PARAMETER_DELETE_AND_BACK ) ) )
590         {
591             String [ ] tabIdAppointmentToDelete = request.getParameterValues( PARAMETER_ID_APPOINTMENT_DELETE );
592             if ( tabIdAppointmentToDelete != null )
593             {
594                 request.getSession( ).setAttribute( PARAMETER_ID_APPOINTMENT_DELETE, tabIdAppointmentToDelete );
595                 return getConfirmRemoveMassAppointment( request, nIdForm );
596             }
597         }
598 
599         UrlItem url = new UrlItem( JSP_MANAGE_APPOINTMENTS );
600         url.addParameter( MVCUtils.PARAMETER_VIEW, VIEW_MANAGE_APPOINTMENTS );
601         url.addParameter( PARAMETER_ID_FORM, strIdForm );
602         String strUrl = url.getUrl( );
603         LocalizedPaginator<Integer> paginator = new LocalizedPaginator<>(_listAppointmentsIds, _nItemsPerPage, strUrl, PARAMETER_PAGE_INDEX,
604                 _strCurrentPageIndex, getLocale( ) );
605         AppointmentFormDTO form = FormService.buildAppointmentFormLight( nIdForm );
606         Map<String, Object> model = getModel( );
607         model.put( MARK_FORM, form );
608         model.put( MARK_FORM_MESSAGES, FormMessageService.findFormMessageByIdForm( nIdForm ) );
609         model.put( MARK_NB_ITEMS_PER_PAGE, Integer.toString( _nItemsPerPage ) );
610         model.put( MARK_PAGINATOR, paginator );
611         model.put( MARK_LANGUAGE, getLocale( ) );
612         model.put( MARK_ACTIVATE_WORKFLOW, ACTIVATEWORKFLOW );
613         if ( ( form.getIdWorkflow( ) > 0 ) && WorkflowService.getInstance( ).isAvailable( ) )
614         {
615             StateService stateService = SpringContextService.getBean( StateService.BEAN_SERVICE );
616             int nIdWorkflow = form.getIdWorkflow( );
617             StateFilter stateFilter = new StateFilter( );
618             stateFilter.setIdWorkflow( nIdWorkflow );
619             for ( AppointmentDTO appointment : listAppointmentsDTO )
620             {
621                 State stateAppointment = stateService.findByResource( appointment.getIdAppointment( ), Appointment.APPOINTMENT_RESOURCE_TYPE, nIdWorkflow );
622                 if ( stateAppointment != null )
623                 {
624                     appointment.setState( stateAppointment );
625                 }
626                 appointment.setListWorkflowActions( WorkflowService.getInstance( ).getActions( appointment.getIdAppointment( ),
627                         Appointment.APPOINTMENT_RESOURCE_TYPE, form.getIdWorkflow( ), (User) getUser( ) ) );
628             }
629         }
630         User user = getUser( );
631         model.put( MARK_APPOINTMENT_LIST, listAppointmentsDTO );
632         model.put( MARK_FILTER, _filter );
633         model.put( MARK_LIST_STATUS, getListStatus( ) );
634         model.put( MARK_RIGHT_CREATE,
635                 RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE, strIdForm, AppointmentResourceIdService.PERMISSION_CREATE_APPOINTMENT, user ) );
636         model.put( MARK_RIGHT_DELETE,
637                 RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE, strIdForm, AppointmentResourceIdService.PERMISSION_DELETE_APPOINTMENT, user ) );
638         model.put( MARK_RIGHT_VIEW,
639                 RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE, strIdForm, AppointmentResourceIdService.PERMISSION_VIEW_APPOINTMENT, user ) );
640         model.put( MARK_RIGHT_CHANGE_STATUS, RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE, strIdForm,
641                 AppointmentResourceIdService.PERMISSION_CHANGE_APPOINTMENT_STATUS, user ) );
642         model.put( MARK_DEFAULT_FIELD_LIST, AppointmentExportService.getDefaultColumnList( getLocale( ) ) );
643         model.put( MARK_CUSTOM_FIELD_LIST, AppointmentExportService.getCustomColumnList( strIdForm ) );
644         model.put( MARK_LOCALE, getLocale( ) );
645         model.put( MARK_APPOINTMENT_DESK_ENABLED, ( _moduleAppointmentDesk != null && _moduleAppointmentDesk.isInstalled( ) ) );
646 
647         return getPage( PROPERTY_PAGE_TITLE_MANAGE_APPOINTMENTS, TEMPLATE_MANAGE_APPOINTMENTS, model );
648     }
649 
650     /**
651      * Manages the removal form of a appointment whose identifier is in the HTTP request
652      * 
653      * @param request
654      *            The HTTP request
655      * @return the HTML code to confirm
656      */
657     @Action( ACTION_CONFIRM_REMOVE_APPOINTMENT )
658     public synchronized String getConfirmRemoveAppointment( HttpServletRequest request )
659     {
660         UrlItem url = new UrlItem( getActionUrl( ACTION_REMOVE_APPOINTMENT ) );
661         url.addParameter( PARAMETER_ID_APPOINTMENT, request.getParameter( PARAMETER_ID_APPOINTMENT ) );
662         url.addParameter( PARAMETER_ID_FORM, request.getParameter( PARAMETER_ID_FORM ) );
663         String strMessageUrl = AdminMessageService.getMessageUrl( request, MESSAGE_CONFIRM_REMOVE_APPOINTMENT, url.getUrl( ), AdminMessage.TYPE_CONFIRMATION );
664         return redirect( request, strMessageUrl );
665     }
666 
667     /**
668      * Handles the removal form of a appointment
669      * 
670      * @param request
671      *            The HTTP request
672      * @return the JSP URL to display the form to manage appointments
673      * @throws AccessDeniedException
674      *             If the user is not authorized to access this feature
675      */
676 
677     @Action( ACTION_REMOVE_APPOINTMENT )
678     public synchronized String doRemoveAppointment( HttpServletRequest request ) throws AccessDeniedException
679     {
680         int nIdAppointment = Integer.parseInt( request.getParameter( PARAMETER_ID_APPOINTMENT ) );
681         Integer idForm = Integer.parseInt( request.getParameter( PARAMETER_ID_FORM ) );
682         if ( !RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE, Integer.toString( idForm ),
683                 AppointmentResourceIdService.PERMISSION_DELETE_APPOINTMENT, (User) getUser( ) ) )
684         {
685             throw new AccessDeniedException( AppointmentResourceIdService.PERMISSION_DELETE_APPOINTMENT );
686         }
687         AppointmentService.deleteAppointment( nIdAppointment );
688         AppLogService.info( LogUtilities.buildLog( ACTION_REMOVE_APPOINTMENT, Integer.toString( nIdAppointment ), getUser( ) ) );
689         addInfo( INFO_APPOINTMENT_REMOVED, getLocale( ) );
690 
691         return redirect( request, VIEW_MANAGE_APPOINTMENTS, PARAMETER_ID_FORM, idForm );
692     }
693 
694     /**
695      * Manages the removal form of a appointment whose identifier is in the HTTP request
696      * 
697      * @param request
698      *            The HTTP request
699      * @return the HTML code to confirm
700      */
701     @Action( ACTION_CONFIRM_REMOVE_MASS_APPOINTMENT )
702     public synchronized String getConfirmRemoveMassAppointment( HttpServletRequest request, int nIdForm )
703     {
704         UrlItem url = new UrlItem( getActionUrl( ACTION_REMOVE_MASSAPPOINTMENT ) );
705         url.addParameter( PARAMETER_ID_FORM, nIdForm );
706         String strMessageUrl = AdminMessageService.getMessageUrl( request, MESSAGE_CONFIRM_REMOVE_MASSAPPOINTMENT, url.getUrl( ),
707                 AdminMessage.TYPE_CONFIRMATION );
708         return redirect( request, strMessageUrl );
709     }
710 
711     /**
712      * Handles the removal form of a appointment
713      * 
714      * @param request
715      *            The HTTP request
716      * @return the JSP URL to display the form to manage appointments
717      * @throws AccessDeniedException
718      *             If the user is not authorized to access this feature
719      */
720     @Action( ACTION_REMOVE_MASSAPPOINTMENT )
721     public synchronized String doRemoveMassAppointment( HttpServletRequest request ) throws AccessDeniedException
722     {
723         String [ ] tabIdAppointmentToDelete = (String [ ]) request.getSession( ).getAttribute( PARAMETER_ID_APPOINTMENT_DELETE );
724         request.getSession( ).removeAttribute( PARAMETER_ID_APPOINTMENT_DELETE );
725         Integer idForm = Integer.parseInt( request.getParameter( PARAMETER_ID_FORM ) );
726         if ( !RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE, Integer.toString( idForm ),
727                 AppointmentResourceIdService.PERMISSION_DELETE_APPOINTMENT, (User) getUser( ) ) )
728         {
729             throw new AccessDeniedException( AppointmentResourceIdService.PERMISSION_DELETE_APPOINTMENT );
730         }
731         if ( tabIdAppointmentToDelete != null )
732         {
733             for ( String strIdAppointment : tabIdAppointmentToDelete )
734             {
735                 AppointmentService.deleteAppointment( Integer.valueOf( strIdAppointment ) );
736                 AppLogService.info( LogUtilities.buildLog( ACTION_REMOVE_APPOINTMENT, strIdAppointment, getUser( ) ) );
737             }
738             addInfo( INFO_APPOINTMENT_MASSREMOVED, getLocale( ) );
739         }
740 
741         return redirect( request, VIEW_MANAGE_APPOINTMENTS, PARAMETER_ID_FORM, idForm );
742     }
743 
744     /**
745      * View details of an appointment
746      * 
747      * @param request
748      *            The request
749      * @return The HTML content to display
750      * @throws AccessDeniedException
751      *             If the user is not authorized to access this feature
752      */
753     @View( VIEW_VIEW_APPOINTMENT )
754     public synchronized String getViewAppointment( HttpServletRequest request ) throws AccessDeniedException
755     {
756         String strIdAppointment = request.getParameter( PARAMETER_ID_APPOINTMENT );
757         String strIdForm = request.getParameter( PARAMETER_ID_FORM );
758         int nIdForm = Integer.parseInt( strIdForm );
759         Form form = FormService.findFormLightByPrimaryKey( nIdForm );
760         int nItemsPerPage = AbstractPaginator.getItemsPerPage( request, AbstractPaginator.PARAMETER_ITEMS_PER_PAGE, _nItemsPerPage, _nDefaultItemsPerPage );
761         int nIdAppointment = Integer.parseInt( strIdAppointment );
762         AppointmentDTO appointmentDTO = AppointmentService.buildAppointmentDTOFromIdAppointment( nIdAppointment );
763         if ( !RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE, strIdForm, AppointmentResourceIdService.PERMISSION_VIEW_APPOINTMENT,
764                 (User) getUser( ) ) )
765         {
766             throw new AccessDeniedException( AppointmentResourceIdService.PERMISSION_VIEW_APPOINTMENT );
767         }
768         Map<String, Object> model = getModel( );
769         model.put( MARK_APPOINTMENT, appointmentDTO );
770         if ( appointmentDTO.getAdminUserCreate( ) != null )
771         {
772 
773             model.put( MARK_USER_CREATOR, AdminUserHome.findUserByLogin( appointmentDTO.getAdminUserCreate( ) ) );
774         }
775         model.put( MARK_FORM_MESSAGES, FormMessageService.findFormMessageByIdForm( nIdForm ) );
776         model.put( MARK_FORM, form );
777         model.put( MARK_NB_ITEMS_PER_PAGE, Integer.toString( nItemsPerPage ) );
778         if ( ( form.getIdWorkflow( ) > 0 ) && WorkflowService.getInstance( ).isAvailable( ) )
779         {
780             model.put( MARK_RESOURCE_HISTORY, WorkflowService.getInstance( ).getDisplayDocumentHistory( nIdAppointment, Appointment.APPOINTMENT_RESOURCE_TYPE,
781                     form.getIdWorkflow( ), request, getLocale( ), (User) getUser( ) ) );
782         }
783         if ( ( form.getIdWorkflow( ) > 0 ) && WorkflowService.getInstance( ).isAvailable( ) )
784         {
785             StateService stateService = SpringContextService.getBean( StateService.BEAN_SERVICE );
786             int nIdWorkflow = form.getIdWorkflow( );
787             StateFilter stateFilter = new StateFilter( );
788             stateFilter.setIdWorkflow( nIdWorkflow );
789             State stateAppointment = stateService.findByResource( appointmentDTO.getIdAppointment( ), Appointment.APPOINTMENT_RESOURCE_TYPE, nIdWorkflow );
790             if ( stateAppointment != null )
791             {
792                 appointmentDTO.setState( stateAppointment );
793             }
794             appointmentDTO.setListWorkflowActions( WorkflowService.getInstance( ).getActions( appointmentDTO.getIdAppointment( ),
795                     Appointment.APPOINTMENT_RESOURCE_TYPE, form.getIdWorkflow( ), (User) getUser( ) ) );
796         }
797         Locale locale = getLocale( );
798         List<Response> listResponse = AppointmentResponseService.findListResponse( nIdAppointment );
799         for ( Response response : listResponse )
800         {
801             if ( response.getFile( ) != null )
802             {
803                 IFileStoreServiceProvider fileStoreService = FileService.getInstance( ).getFileStoreServiceProvider( );
804                 File file = fileStoreService.getFile( response.getFile( ).getFileKey( ) );
805                 response.setFile( file );
806             }
807             if ( response.getEntry( ) != null )
808             {
809                 response.setEntry( EntryHome.findByPrimaryKey( response.getEntry( ).getIdEntry( ) ) );
810             }
811         }
812         appointmentDTO.setListResponse( listResponse );
813         model.put( MARK_LIST_RESPONSE_RECAP_DTO, AppointmentUtilities.buildListResponse( appointmentDTO, request, locale ) );
814         model.put( MARK_ADDON, AppointmentAddOnManager.getAppointmentAddOn( appointmentDTO.getIdAppointment( ), getLocale( ) ) );
815         User user = getUser( );
816         model.put( MARK_RIGHT_CREATE,
817                 RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE, strIdForm, AppointmentResourceIdService.PERMISSION_CREATE_APPOINTMENT, user ) );
818         model.put( MARK_RIGHT_DELETE,
819                 RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE, strIdForm, AppointmentResourceIdService.PERMISSION_DELETE_APPOINTMENT, user ) );
820         model.put( MARK_RIGHT_VIEW,
821                 RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE, strIdForm, AppointmentResourceIdService.PERMISSION_VIEW_APPOINTMENT, user ) );
822         model.put( MARK_RIGHT_CHANGE_STATUS, RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE, strIdForm,
823                 AppointmentResourceIdService.PERMISSION_CHANGE_APPOINTMENT_STATUS, user ) );
824         model.put( MARK_LANGUAGE, getLocale( ) );
825         model.put( MARK_ACTIVATE_WORKFLOW, ACTIVATEWORKFLOW );
826         model.put( MARK_LOCALE, getLocale( ) );
827         return getPage( PROPERTY_PAGE_TITLE_VIEW_APPOINTMENT, TEMPLATE_VIEW_APPOINTMENT, model );
828     }
829 
830     /**
831      * Do download a file from an appointment response
832      * 
833      * @param request
834      *            The request
835      * @param response
836      *            The response
837      * @return nothing.
838      * @throws AccessDeniedException
839      *             If the user is not authorized to access this feature
840      */
841     @Action( ACTION_EXPORT_APPOINTMENTS )
842     public synchronized String doExportAppointments( HttpServletRequest request ) throws AccessDeniedException
843     {
844         String strIdForm = request.getParameter( PARAMETER_ID_FORM );
845         if ( StringUtils.isEmpty( strIdForm ) || !StringUtils.isNumeric( strIdForm ) )
846         {
847             return redirect( request, AppointmentFormJspBean.getURLManageAppointmentForms( request ) );
848         }
849         if ( !RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE, strIdForm, AppointmentResourceIdService.PERMISSION_VIEW_APPOINTMENT,
850                 (User) getUser( ) ) )
851         {
852             throw new AccessDeniedException( AppointmentResourceIdService.PERMISSION_VIEW_APPOINTMENT );
853         }
854         Locale locale = getLocale( );
855         List<AppointmentDTO> listAppointmentsDTO = new ArrayList<>( );
856         if ( _filter != null && _filter.getIdForm( ) == Integer.parseInt( strIdForm ) )
857         {
858 
859             listAppointmentsDTO = AppointmentService.findListAppointmentsDTOByFilter( _filter );
860         }
861 
862         List<String> defaultColumnList = new ArrayList<>( );
863         List<Integer> customColumnList = new ArrayList<>( );
864         if ( ArrayUtils.isNotEmpty( request.getParameterValues( PARAMETER_SELECTED_DEFAULT_FIELD ) ) )
865         {
866             defaultColumnList = Arrays.asList( request.getParameterValues( PARAMETER_SELECTED_DEFAULT_FIELD ) );
867         }
868         if ( ArrayUtils.isNotEmpty( request.getParameterValues( PARAMETER_SELECTED_CUSTOM_FIELD ) ) )
869         {
870             customColumnList = Arrays.asList( request.getParameterValues( PARAMETER_SELECTED_CUSTOM_FIELD ) ).stream( ).map( Integer::parseInt )
871                     .collect( Collectors.toList( ) );
872         }
873 
874         ExcelAppointmentGeneratorexport/ExcelAppointmentGenerator.html#ExcelAppointmentGenerator">ExcelAppointmentGenerator generator = new ExcelAppointmentGenerator( defaultColumnList, locale, listAppointmentsDTO, customColumnList );
875 
876         TemporaryFileGeneratorService.getInstance( ).generateFile( generator, getUser( ) );
877         addInfo( "appointment.export.async.message", getLocale( ) );
878 
879         return getManageAppointments( request );
880     }
881 
882     /**
883      * Returns the form to create an appointment
884      * 
885      * @param request
886      *            The HTTP request
887      * @return the HTML code of the appointment form
888      * @throws AccessDeniedException
889      *             If the user is not authorized to access this feature
890      */
891     @View( VIEW_CREATE_APPOINTMENT )
892     public synchronized String getViewCreateAppointment( HttpServletRequest request ) throws AccessDeniedException
893     {
894         clearUploadFilesIfNeeded( request.getSession( ) );
895         String strIdForm = request.getParameter( PARAMETER_ID_FORM );
896         int nIdForm = Integer.parseInt( strIdForm );
897 
898         String strNbPlacesToTake = request.getParameter( PARAMETER_NB_PLACE_TO_TAKE );
899         if ( !RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE, strIdForm, AppointmentResourceIdService.PERMISSION_CREATE_APPOINTMENT,
900                 (User) getUser( ) ) )
901         {
902             throw new AccessDeniedException( AppointmentResourceIdService.PERMISSION_CREATE_APPOINTMENT );
903         }
904         FormRule formRule = FormRuleService.findFormRuleWithFormId( nIdForm );
905         Locale locale = getLocale( );
906 
907         String isModification = request.getParameter( PARAMETER_IS_MODIFICATION );
908         boolean bModificationForm = false;
909         List<Slot> listSlot = null;
910         if ( isModification != null )
911 
912         {
913             bModificationForm = true;
914 
915         }
916         else
917         {
918             if ( strNbPlacesToTake != null )
919             {
920 
921                 _nNbPlacesToTake = Integer.parseInt( strNbPlacesToTake );
922             }
923             int nNbConsecutiveSlot = ( _nNbPlacesToTake == 0 ) ? 1 : _nNbPlacesToTake;
924             LocalDateTime startingDateTime = LocalDateTime.parse( request.getParameter( PARAMETER_STARTING_DATE_TIME ) );
925 
926             // Get all the week definitions
927             List<WeekDefinition> listWeekDefinition = WeekDefinitionService.findListWeekDefinition( nIdForm );
928             Map<WeekDefinition, ReservationRule> mapReservationRule = ReservationRuleService.findAllReservationRule( nIdForm, listWeekDefinition );
929 
930             listSlot = SlotService.buildListSlot( nIdForm, mapReservationRule, startingDateTime.toLocalDate( ), startingDateTime.toLocalDate( ) );
931 
932             if ( formRule.getBoOverbooking( ) && RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE, strIdForm,
933                     AppointmentResourceIdService.PERMISSION_OVERBOOKING_FORM, (User) getUser( ) ) )
934             {
935                 listSlot = listSlot.stream( ).filter( s -> ( ( startingDateTime.compareTo( s.getStartingDateTime( ) ) <= 0 ) && ( s.getIsOpen( ) ) ) )
936                         .limit( nNbConsecutiveSlot ).collect( Collectors.toList( ) );
937 
938             }
939             else
940             {
941                 listSlot = listSlot.stream( ).filter(
942                         s -> ( ( startingDateTime.compareTo( s.getStartingDateTime( ) ) <= 0 ) && ( s.getNbRemainingPlaces( ) > 0 ) && ( s.getIsOpen( ) ) ) )
943                         .limit( nNbConsecutiveSlot ).collect( Collectors.toList( ) );
944 
945             }
946             if ( listSlot == null || listSlot.stream( ).noneMatch( slot -> slot.getStartingDateTime( ).isEqual( startingDateTime ) )
947                     || ( _nNbPlacesToTake > 0 && _nNbPlacesToTake != listSlot.size( ) ) || !AppointmentUtilities.isConsecutiveSlots( listSlot ) )
948             {
949                 addError( ERROR_MESSAGE_SLOT_FULL, locale );
950                 return redirect( request, VIEW_CALENDAR_MANAGE_APPOINTMENTS, PARAMETER_ID_FORM, nIdForm );
951             }
952         }
953 
954         // Get the not validated appointment in session if it exists
955 
956         if ( _notValidatedAppointment == null )
957         {
958 
959             if ( _validatedAppointment != null )
960             {
961 
962                 // Try to get the validated appointment in session
963                 // (in case the user click on back button in the recap view (or
964                 // modification)
965                 _notValidatedAppointment = _validatedAppointment;
966                 _validatedAppointment = null;
967             }
968             else
969             {
970                 // Need to get back the informations the user has entered
971                 _notValidatedAppointment = new AppointmentDTO( );
972 
973             }
974         }
975         else
976             if ( _nNbPlacesToTake == 0 && bModificationForm )
977             {
978                 _nNbPlacesToTake = _notValidatedAppointment.getNbBookedSeats( );
979             }
980         if ( !bModificationForm )
981         {
982             boolean bool = true;
983             _notValidatedAppointment.setSlot( null );
984             _notValidatedAppointment.setIdForm( nIdForm );
985             _notValidatedAppointment.setNbMaxPotentialBookedSeats( 0 );
986             for ( Slot slot : listSlot )
987             {
988 
989                 if ( slot.getIdSlot( ) == 0 )
990                 {
991 
992                     slot = SlotSafeService.createSlot( slot );
993 
994                 }
995                 else
996                 {
997 
998                     slot = SlotService.findSlotById( slot.getIdSlot( ) );
999                 }
1000 
1001                 // Need to check competitive access
1002                 // May be the slot is already taken at the same time
1003                 if ( slot.getNbPotentialRemainingPlaces( ) <= 0 && !formRule.getBoOverbooking( ) && !RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE,
1004                         strIdForm, AppointmentResourceIdService.PERMISSION_OVERBOOKING_FORM, (User) getUser( ) ) )
1005                 {
1006                     _notValidatedAppointment = null;
1007                     addError( ERROR_MESSAGE_SLOT_FULL, locale );
1008                     return redirect( request, VIEW_CALENDAR_MANAGE_APPOINTMENTS, PARAMETER_ID_FORM, nIdForm );
1009                 }
1010 
1011                 _notValidatedAppointment.addSlot( slot );
1012 
1013                 if ( bool )
1014                 {
1015 
1016                     LocalDateTime startingDateTime = LocalDateTime.parse( request.getParameter( PARAMETER_STARTING_DATE_TIME ) );
1017 
1018                     _notValidatedAppointment.setDateOfTheAppointment( slot.getDate( ).format( Utilities.getFormatter( ) ) );
1019                     _notValidatedAppointment.setEndingDateTime( listSlot.get( listSlot.size( ) - 1 ).getEndingDateTime( ) );
1020                     _notValidatedAppointment.setStartingDateTime( startingDateTime );
1021                     ReservationRule reservationRule = ReservationRuleService.findReservationRuleByIdFormAndClosestToDateOfApply( nIdForm, slot.getDate( ) );
1022                     _appointmentForm = FormService.buildAppointmentForm( nIdForm, reservationRule );
1023                     bool = false;
1024                 }
1025                 AppointmentUtilities.putTimerInSession( request, slot.getIdSlot( ), _notValidatedAppointment, _appointmentForm.getMaxPeoplePerAppointment( ) );
1026             }
1027 
1028             if ( _notValidatedAppointment.getNbMaxPotentialBookedSeats( ) == 0 && !formRule.getBoOverbooking( ) && !RBACService
1029                     .isAuthorized( AppointmentFormDTO.RESOURCE_TYPE, strIdForm, AppointmentResourceIdService.PERMISSION_OVERBOOKING_FORM, (User) getUser( ) ) )
1030 
1031             {
1032                 addError( ERROR_MESSAGE_SLOT_FULL, locale );
1033                 return redirect( request, VIEW_CALENDAR_MANAGE_APPOINTMENTS, PARAMETER_ID_FORM, nIdForm );
1034             }
1035         }
1036 
1037         Map<String, Object> model = getModel( );
1038         if ( CollectionUtils.isNotEmpty( listFormErrors ) )
1039         {
1040             model.put( MARK_FORM_ERRORS, listFormErrors );
1041             listFormErrors = new ArrayList<>( );
1042         }
1043         List<Entry> listEntryFirstLevel = EntryService.getFilter( _appointmentForm.getIdForm( ), false );
1044         StringBuilder strBuffer = new StringBuilder( );
1045         for ( Entry entry : listEntryFirstLevel )
1046         {
1047             EntryService.getHtmlEntry( model, entry.getIdEntry( ), strBuffer, locale, false, _notValidatedAppointment );
1048         }
1049 
1050         boolean isOverbooking = !_appointmentForm.getIsMultislotAppointment( ) && formRule.getBoOverbooking( ) && RBACService
1051                 .isAuthorized( AppointmentFormDTO.RESOURCE_TYPE, strIdForm, AppointmentResourceIdService.PERMISSION_OVERBOOKING_FORM, (User) getUser( ) );
1052 
1053         model.put( MARK_STR_ENTRY, strBuffer.toString( ) );
1054         model.put( MARK_FORM, _appointmentForm );
1055         model.put( MARK_APPOINTMENT, _notValidatedAppointment );
1056         model.put( PARAMETER_DATE_OF_DISPLAY, _notValidatedAppointment.getSlot( ).get( 0 ).getDate( ) );
1057         model.put( MARK_PLACES, _notValidatedAppointment.getNbMaxPotentialBookedSeats( ) );
1058         model.put( MARK_IS_OVERBOOKING, isOverbooking );
1059         FormMessage formMessages = FormMessageService.findFormMessageByIdForm( nIdForm );
1060         model.put( MARK_FORM_MESSAGES, formMessages );
1061         model.put( MARK_LOCALE, locale );
1062         model.put( MARK_LIST_ERRORS, AppointmentDTO.getAllErrors( locale ) );
1063         HtmlTemplate templateForm = AppTemplateService.getTemplate( TEMPLATE_HTML_CODE_FORM_ADMIN, getLocale( ), model );
1064         model.put( MARK_FORM_HTML, templateForm.getHtml( ) );
1065         model.put( MARK_LOCALE, getLocale( ) );
1066 
1067 
1068         return getPage( PROPERTY_PAGE_TITLE_CREATE_APPOINTMENT, TEMPLATE_CREATE_APPOINTMENT, model );
1069     }
1070 
1071     /**
1072      * Do validate data entered by a user to fill a form
1073      * 
1074      * @param request
1075      *            The request
1076      * @return The next URL to redirect to
1077      * @throws AccessDeniedException
1078      *             If the user is not authorized to access this feature
1079      */
1080     @Action( ACTION_DO_VALIDATE_FORM )
1081     public synchronized String doValidateForm( HttpServletRequest request ) throws AccessDeniedException
1082     {
1083         String strIdForm = request.getParameter( PARAMETER_ID_FORM );
1084         int nIdForm = Integer.parseInt( strIdForm );
1085         String strEmail = request.getParameter( PARAMETER_EMAIL );
1086         String strEmailConfirm = request.getParameter( PARAMETER_EMAIL_CONFIRMATION );
1087         Locale locale = getLocale( );
1088         AppointmentUtilities.checkDateOfTheAppointmentIsNotBeforeNow( _notValidatedAppointment, locale, listFormErrors );
1089         AppointmentUtilities.checkEmail( strEmail, strEmailConfirm, _appointmentForm, locale, listFormErrors );
1090 
1091         if ( _appointmentForm.getBoOverbooking( ) && RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE, strIdForm,
1092                 AppointmentResourceIdService.PERMISSION_OVERBOOKING_FORM, (User) getUser( ) ) )
1093         {
1094 
1095             _notValidatedAppointment.setOverbookingAllowed( true );
1096         }
1097 
1098         int nbBookedSeats = _nNbPlacesToTake;
1099         if ( _nNbPlacesToTake == 0 )
1100         {
1101 
1102             nbBookedSeats = AppointmentUtilities.checkAndReturnNbBookedSeats( request.getParameter( PARAMETER_NUMBER_OF_BOOKED_SEATS ), _appointmentForm,
1103                     _notValidatedAppointment, locale, listFormErrors );
1104 
1105         }
1106 
1107         if ( _appointmentForm.getEnableMandatoryEmail( )
1108                 && !AppointmentUtilities.checkNbDaysBetweenTwoAppointmentsTaken( _notValidatedAppointment, strEmail, _appointmentForm ) )
1109         {
1110             addWarning( ERROR_MESSAGE_NB_MIN_DAYS_BETWEEN_TWO_APPOINTMENTS, locale );
1111         }
1112         if ( _appointmentForm.getEnableMandatoryEmail( )
1113                 && !AppointmentUtilities.checkNbMaxAppointmentsOnAGivenPeriod( _notValidatedAppointment, strEmail, _appointmentForm ) )
1114         {
1115             addWarning( ERROR_MESSAGE_NB_MAX_APPOINTMENTS_ON_A_PERIOD, locale );
1116         }
1117         List<AppointmentDTO> listAppointments = new ArrayList<>( );
1118         if ( _appointmentForm.getEnableMandatoryEmail( )
1119                 && !AppointmentUtilities.checkNbMaxAppointmentsDefinedOnCategory( _notValidatedAppointment, strEmail, _appointmentForm, listAppointments ) )
1120         {
1121             StringJoiner builder = new StringJoiner( StringUtils.SPACE );
1122             String lf = System.getProperty( "line.separator" );
1123             for ( AppointmentDTO appt : listAppointments )
1124             {
1125                 builder.add( appt.getLastName( ) );
1126                 builder.add( appt.getFirstName( ) );
1127                 builder.add( appt.getDateOfTheAppointment( ) );
1128                 builder.add( appt.getStartingTime( ).toString( ) );
1129                 builder.add( lf );
1130             }
1131             Object [ ] tabAppointment = {
1132                     builder.toString( )
1133             };
1134 
1135             String strErrorMessageDateWithAppointments = I18nService.getLocalizedString( ERROR_MESSAGE_NB_MAX_APPOINTMENTS_ON_A_CATEGORY, tabAppointment,
1136                     locale );
1137             addWarning( strErrorMessageDateWithAppointments );
1138         }
1139         AppointmentUtilities.fillAppointmentDTO( _notValidatedAppointment, nbBookedSeats, strEmail, strEmailConfirm, request.getParameter( PARAMETER_FIRST_NAME ),
1140                 request.getParameter( PARAMETER_LAST_NAME ) );
1141         AppointmentUtilities.validateFormAndEntries( _notValidatedAppointment, request, listFormErrors, true );
1142         AppointmentUtilities.fillInListResponseWithMapResponse( _notValidatedAppointment );
1143         AppointmentUtilities.setAppointmentPhoneNumberValuesFromResponse( _notValidatedAppointment );
1144 
1145         if ( CollectionUtils.isNotEmpty( listFormErrors ) )
1146         {
1147             LinkedHashMap<String, String> additionalParameters = new LinkedHashMap<>( );
1148             additionalParameters.put( PARAMETER_ID_FORM, strIdForm );
1149             additionalParameters.put( PARAMETER_STARTING_DATE_TIME, _notValidatedAppointment.getStartingDateTime( ).toString( ) );
1150             additionalParameters.put( PARAMETER_ENDING_DATE_TIME, _notValidatedAppointment.getEndingDateTime( ).toString( ) );
1151             return redirect( request, VIEW_CREATE_APPOINTMENT, additionalParameters );
1152         }
1153         _validatedAppointment = _notValidatedAppointment;
1154         _notValidatedAppointment = null;
1155         return redirect( request, VIEW_DISPLAY_RECAP_APPOINTMENT, PARAMETER_ID_FORM, nIdForm );
1156     }
1157 
1158     /**
1159      * Return to the display recap view with the new date selected on the calendar
1160      * 
1161      * @param request
1162      *            the request
1163      * @return to the display recap view
1164      * @throws AccessDeniedException
1165      */
1166     @View( VIEW_CHANGE_DATE_APPOINTMENT )
1167     public synchronized String getViewChangeDateAppointment( HttpServletRequest request )
1168     {
1169         String strIdForm = request.getParameter( PARAMETER_ID_FORM );
1170         Locale locale = getLocale( );
1171         int nIdForm = Integer.parseInt( strIdForm );
1172         LocalDateTime startingDateTime = LocalDateTime.parse( request.getParameter( PARAMETER_STARTING_DATE_TIME ) );
1173         LocalDateTime endingDateTime = LocalDateTime.parse( request.getParameter( PARAMETER_ENDING_DATE_TIME ) );
1174         // Get all the week definitions
1175         List<WeekDefinition> listWeekDefinition = WeekDefinitionService.findListWeekDefinition( nIdForm );
1176         Map<WeekDefinition, ReservationRule> mapReservationRule = ReservationRuleService.findAllReservationRule( nIdForm, listWeekDefinition );
1177 
1178         List<Slot> listSlot = SlotService.buildListSlot( nIdForm, mapReservationRule, startingDateTime.toLocalDate( ), endingDateTime.toLocalDate( ) );
1179 
1180         FormRule formRule = FormRuleService.findFormRuleWithFormId( nIdForm );
1181         if ( formRule.getBoOverbooking( ) && RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE, strIdForm,
1182                 AppointmentResourceIdService.PERMISSION_OVERBOOKING_FORM, (User) getUser( ) ) )
1183         {
1184 
1185             listSlot = listSlot.stream( ).filter( s -> ( ( startingDateTime.compareTo( s.getStartingDateTime( ) ) <= 0 )
1186                     && ( endingDateTime.compareTo( s.getEndingDateTime( ) ) >= 0 ) && ( s.getIsOpen( ) ) ) ).collect( Collectors.toList( ) );
1187 
1188         }
1189         else
1190         {
1191 
1192             listSlot = listSlot.stream( )
1193                     .filter( s -> ( ( startingDateTime.compareTo( s.getStartingDateTime( ) ) <= 0 )
1194                             && ( endingDateTime.compareTo( s.getEndingDateTime( ) ) >= 0 ) && ( s.getNbRemainingPlaces( ) > 0 ) && ( s.getIsOpen( ) ) ) )
1195                     .collect( Collectors.toList( ) );
1196         }
1197         boolean bool = true;
1198 
1199         // If nIdSlot == 0, the slot has not been created yet
1200         _validatedAppointment.setSlot( null );
1201         _validatedAppointment.setNbMaxPotentialBookedSeats( 0 );
1202         for ( Slot slot : listSlot )
1203         {
1204             if ( slot.getIdSlot( ) == 0 )
1205             {
1206                 slot = SlotSafeService.createSlot( slot );
1207             }
1208             else
1209             {
1210                 slot = SlotService.findSlotById( slot.getIdSlot( ) );
1211             }
1212 
1213             if ( bool )
1214             {
1215                 _validatedAppointment.setDateOfTheAppointment( slot.getDate( ).format( Utilities.getFormatter( ) ) );
1216                 ReservationRule reservationRule = ReservationRuleService.findReservationRuleByIdFormAndClosestToDateOfApply( nIdForm, slot.getDate( ) );
1217                 _appointmentForm = FormService.buildAppointmentForm( nIdForm, reservationRule );
1218                 bool = false;
1219             }
1220             // Need to check competitive access
1221             // May be the slot is already taken at the same time
1222             if ( slot.getNbPotentialRemainingPlaces( ) <= 0
1223                     && ( !_appointmentForm.getBoOverbooking( ) || !RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE, strIdForm,
1224                             AppointmentResourceIdService.PERMISSION_OVERBOOKING_FORM, (User) getUser( ) ) ) )
1225             {
1226                 addError( ERROR_MESSAGE_SLOT_FULL, locale );
1227                 return redirect( request, VIEW_CALENDAR_MANAGE_APPOINTMENTS, PARAMETER_ID_FORM, nIdForm );
1228             }
1229 
1230             _validatedAppointment.addSlot( slot );
1231 
1232             AppointmentUtilities.putTimerInSession( request, slot.getIdSlot( ), _validatedAppointment, _appointmentForm.getMaxPeoplePerAppointment( ) );
1233         }
1234 
1235         if ( _validatedAppointment.getNbMaxPotentialBookedSeats( ) == 0 && ( !_appointmentForm.getBoOverbooking( ) || !RBACService
1236                 .isAuthorized( AppointmentFormDTO.RESOURCE_TYPE, strIdForm, AppointmentResourceIdService.PERMISSION_OVERBOOKING_FORM, (User) getUser( ) ) ) )
1237         {
1238             addError( ERROR_MESSAGE_SLOT_FULL, locale );
1239             return redirect( request, VIEW_CALENDAR_MANAGE_APPOINTMENTS, PARAMETER_ID_FORM, nIdForm );
1240         }
1241 
1242         for ( Response response : _validatedAppointment.getListResponse( ) )
1243         {
1244             if ( response.getFile( ) != null )
1245             {
1246                 IFileStoreServiceProvider fileStoreService = FileService.getInstance( ).getFileStoreServiceProvider( );
1247                 File file = fileStoreService.getFile( response.getFile( ).getFileKey( ) );
1248                 response.setFile( file );
1249             }
1250         }
1251 
1252         Map<String, String> additionalParameters = new HashMap<>( );
1253         additionalParameters.put( PARAMETER_ID_FORM, Integer.toString( nIdForm ) );
1254         additionalParameters.put( PARAMETER_COME_FROM_CALENDAR, Boolean.TRUE.toString( ) );
1255         return redirect( request, VIEW_DISPLAY_RECAP_APPOINTMENT, additionalParameters );
1256     }
1257 
1258     /**
1259      * Display the recap before validating an appointment
1260      * 
1261      * @param request
1262      *            The request
1263      * @return The HTML content to display or the next URL to redirect to
1264      */
1265     @View( VIEW_DISPLAY_RECAP_APPOINTMENT )
1266     public synchronized String displayRecapAppointment( HttpServletRequest request )
1267     {
1268 
1269         Map<String, Object> model = getModel( );
1270         String strComeFromCalendar = request.getParameter( PARAMETER_COME_FROM_CALENDAR );
1271         if ( StringUtils.isNotEmpty( strComeFromCalendar ) )
1272         {
1273             model.put( PARAMETER_COME_FROM_CALENDAR, strComeFromCalendar );
1274             model.put( PARAMETER_DATE_OF_DISPLAY, _validatedAppointment.getSlot( ).get( 0 ).getDate( ) );
1275         }
1276         model.put( MARK_FORM_MESSAGES, FormMessageService.findFormMessageByIdForm( _validatedAppointment.getIdForm( ) ) );
1277         model.put( MARK_APPOINTMENT, _validatedAppointment );
1278         Locale locale = getLocale( );
1279         model.put( MARK_ADDON, AppointmentAddOnManager.getAppointmentAddOn( _validatedAppointment.getIdAppointment( ), getLocale( ) ) );
1280         model.put( MARK_LIST_RESPONSE_RECAP_DTO, AppointmentUtilities.buildListResponse( _validatedAppointment, request, locale ) );
1281         model.put( MARK_FORM, _appointmentForm );
1282         model.put( MARK_LOCALE, getLocale( ) );
1283         return getPage( PROPERTY_PAGE_TITLE_RECAP_APPOINTMENT, TEMPLATE_APPOINTMENT_FORM_RECAP, model );
1284     }
1285 
1286     /**
1287      * Do save an appointment into the database if it is valid
1288      * 
1289      * @param request
1290      *            The request
1291      * @return The XPage to display
1292      * @throws AccessDeniedException
1293      *             If the user is not authorized to access this feature
1294      */
1295     @Action( ACTION_DO_MAKE_APPOINTMENT )
1296     public synchronized String doMakeAppointment( HttpServletRequest request ) throws AccessDeniedException
1297     {
1298         boolean overbookingAllowed = false;
1299         if ( StringUtils.isNotEmpty( request.getParameter( PARAMETER_BACK ) ) )
1300         {
1301             return redirect( request, VIEW_CREATE_APPOINTMENT, PARAMETER_ID_FORM, _validatedAppointment.getIdForm( ) );
1302         }
1303         if ( _appointmentForm.getBoOverbooking( ) && RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE,
1304                 Integer.toString( _appointmentForm.getIdForm( ) ), AppointmentResourceIdService.PERMISSION_OVERBOOKING_FORM, (User) getUser( ) ) )
1305         {
1306             overbookingAllowed = true;
1307         }
1308         if ( !RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE, Integer.toString( _appointmentForm.getIdForm( ) ),
1309                 AppointmentResourceIdService.PERMISSION_CREATE_APPOINTMENT, (User) getUser( ) ) || !_appointmentForm.getIsActive( ) )
1310         {
1311             throw new AccessDeniedException( AppointmentResourceIdService.PERMISSION_CREATE_APPOINTMENT );
1312         }
1313         int nIdAppointment;
1314         if ( _validatedAppointment.getIdAppointment( ) == 0 )
1315         {
1316             // set the admin user who is creating the appointment
1317             AdminUser adminLuteceUser = AdminAuthenticationService.getInstance( ).getRegisteredUser( request );
1318             _validatedAppointment.setAdminUserCreate( adminLuteceUser.getAccessCode( ) );
1319         }
1320         try
1321         {
1322             _validatedAppointment.setOverbookingAllowed( overbookingAllowed );
1323             nIdAppointment = SlotSafeService.saveAppointment( _validatedAppointment, request );
1324 
1325         }
1326         catch( SlotFullException e )
1327         {
1328             addError( ERROR_MESSAGE_SLOT_FULL, getLocale( ) );
1329             return redirect( request, VIEW_CALENDAR_MANAGE_APPOINTMENTS, PARAMETER_ID_FORM, _validatedAppointment.getIdForm( ) );
1330         }
1331         catch( SlotEditTaskExpiredTimeException e )
1332         {
1333             addError( ERROR_MESSAGE_SLOT_EDIT_TASK_EXPIRED_TIME, getLocale( ) );
1334             return redirect( request, VIEW_CALENDAR_MANAGE_APPOINTMENTS, PARAMETER_ID_FORM, _validatedAppointment.getIdForm( ) );
1335         }
1336         catch( AppointmentSavedException e )
1337         {
1338 
1339             nIdAppointment = _validatedAppointment.getIdAppointment( );
1340             AppLogService.error( "Error Save appointment: " + e.getMessage( ), e );
1341         }
1342         _nNbPlacesToTake = 0;
1343         AppLogService.info( LogUtilities.buildLog( ACTION_DO_MAKE_APPOINTMENT, Integer.toString( nIdAppointment ), getUser( ) ) );
1344         addInfo( INFO_APPOINTMENT_CREATED, getLocale( ) );
1345         AppointmentAsynchronousUploadHandler.getHandler( ).removeSessionFiles( request.getSession( ) );
1346         Map<String, String> additionalParameters = new HashMap<>( );
1347         additionalParameters.put( PARAMETER_ID_FORM, Integer.toString( _appointmentForm.getIdForm( ) ) );
1348         additionalParameters.put( PARAMETER_DATE_OF_DISPLAY, _validatedAppointment.getSlot( ).get( 0 ).getDate( ).toString( ) );
1349         _validatedAppointment = null;
1350         return redirect( request, VIEW_CALENDAR_MANAGE_APPOINTMENTS, additionalParameters );
1351     }
1352 
1353     /**
1354      * Do download a file from an appointment response stored in session and not yet on server fs
1355      * 
1356      * @param request
1357      *            The request
1358      * @param httpResponse
1359      *            The response
1360      * @return nothing.
1361      * @throws AccessDeniedException
1362      *             If the user is not authorized to access this feature
1363      */
1364     public synchronized String getDownloadFileFromSession( HttpServletRequest request, HttpServletResponse httpResponse ) throws AccessDeniedException
1365     {
1366         String strIdResponse = request.getParameter( PARAMETER_ID_RESPONSE );
1367         File respfile = null;
1368         if ( StringUtils.isEmpty( strIdResponse ) || !StringUtils.isNumeric( strIdResponse ) )
1369         {
1370             return redirect( request, AppointmentFormJspBean.getURLManageAppointmentForms( request ) );
1371         }
1372 
1373         int nIdResponse = Integer.parseInt( strIdResponse );
1374         List<Response> lResponse = _validatedAppointment.getListResponse( );
1375 
1376         for ( Response response : lResponse )
1377         {
1378             if ( response.getEntry( ).getIdEntry( ) == nIdResponse && response.getFile( ) != null )
1379             {
1380                 respfile = response.getFile( );
1381                 break;
1382             }
1383         }
1384 
1385         if ( respfile == null )
1386         {
1387             return redirect( request, AppointmentFormJspBean.getURLManageAppointmentForms( request ) );
1388         }
1389 
1390         httpResponse.setHeader( "Content-Disposition", "attachment; filename=\"" + respfile.getTitle( ) + "\";" );
1391         httpResponse.setHeader( "Content-type", respfile.getMimeType( ) );
1392         httpResponse.addHeader( "Content-Encoding", "UTF-8" );
1393         httpResponse.addHeader( "Pragma", "public" );
1394         httpResponse.addHeader( "Expires", "0" );
1395         httpResponse.addHeader( "Cache-Control", "must-revalidate,post-check=0,pre-check=0" );
1396 
1397         try
1398         {
1399             OutputStream os = httpResponse.getOutputStream( );
1400             os.write( respfile.getPhysicalFile( ).getValue( ) );
1401             // We do not close the output stream in finally clause because it is
1402             // the response stream,
1403             // and an error message needs to be displayed if an exception occurs
1404             os.close( );
1405         }
1406         catch( IOException e )
1407         {
1408             AppLogService.error( e.getStackTrace( ), e );
1409         }
1410 
1411         return StringUtils.EMPTY;
1412     }
1413 
1414     /**
1415      * Do download a file from an appointment response
1416      * 
1417      * @param request
1418      *            The request
1419      * @param httpResponse
1420      *            The response
1421      * @return nothing.
1422      * @throws AccessDeniedException
1423      *             If the user is not authorized to access this feature
1424      */
1425     public synchronized String getDownloadFile( HttpServletRequest request, HttpServletResponse httpResponse ) throws AccessDeniedException
1426     {
1427         String strIdResponse = request.getParameter( PARAMETER_ID_RESPONSE );
1428 
1429         if ( StringUtils.isEmpty( strIdResponse ) || !StringUtils.isNumeric( strIdResponse ) )
1430         {
1431             return redirect( request, AppointmentFormJspBean.getURLManageAppointmentForms( request ) );
1432         }
1433 
1434         int nIdResponse = Integer.parseInt( strIdResponse );
1435         Response response = ResponseHome.findByPrimaryKey( nIdResponse );
1436         IFileStoreServiceProvider fileStoreService = FileService.getInstance( ).getFileStoreServiceProvider( );
1437         File file = fileStoreService.getFile( response.getFile( ).getFileKey( ) );
1438 
1439         httpResponse.setHeader( "Content-Disposition", "attachment; filename=\"" + file.getTitle( ) + "\";" );
1440         httpResponse.setHeader( "Content-type", file.getMimeType( ) );
1441         httpResponse.addHeader( "Content-Encoding", "UTF-8" );
1442         httpResponse.addHeader( "Pragma", "public" );
1443         httpResponse.addHeader( "Expires", "0" );
1444         httpResponse.addHeader( "Cache-Control", "must-revalidate,post-check=0,pre-check=0" );
1445 
1446         try
1447         {
1448             OutputStream os = httpResponse.getOutputStream( );
1449             os.write( file.getPhysicalFile( ).getValue( ) );
1450             // We do not close the output stream in finnaly clause because it is
1451             // the response stream,
1452             // and an error message needs to be displayed if an exception occurs
1453             os.close( );
1454         }
1455         catch( IOException e )
1456         {
1457             AppLogService.error( e.getStackTrace( ), e );
1458         }
1459 
1460         return StringUtils.EMPTY;
1461     }
1462 
1463     /**
1464      * Default constructor
1465      */
1466     public AppointmentJspBean( )
1467     {
1468         _nDefaultItemsPerPage = AppPropertiesService.getPropertyInt( PROPERTY_DEFAULT_LIST_APPOINTMENT_PER_PAGE, 10 );
1469     }
1470 
1471     private void cleanSession( HttpSession session )
1472     {
1473         _filter = null;
1474         _strCurrentPageIndex = null;
1475         _notValidatedAppointment = null;
1476         _validatedAppointment = null;
1477         AppointmentAsynchronousUploadHandler.getHandler( ).removeSessionFiles( session );
1478     }
1479 
1480     /**
1481      * Clear uploaded files if needed.
1482      * 
1483      * @param session
1484      *            The session of the current user
1485      */
1486     private void clearUploadFilesIfNeeded( HttpSession session )
1487     {
1488         // If we do not reload an appointment, we clear uploaded files.
1489         if ( _notValidatedAppointment == null && _validatedAppointment == null )
1490         {
1491             AppointmentAsynchronousUploadHandler.getHandler( ).removeSessionFiles( session );
1492         }
1493     }
1494 
1495     /**
1496      * Order the list of the appointment in the result tab with the order by and order asc given
1497      * 
1498      * @param listAppointmentsDTO
1499      *            the llist of appointments
1500      */
1501     private List<AppointmentDTO> orderList( List<AppointmentDTO> listAppointmentsDTO )
1502     {
1503         List<AppointmentDTO> sortedList = new ArrayList<>( );
1504         if ( CollectionUtils.isNotEmpty( listAppointmentsDTO ) )
1505         {
1506             sortedList.addAll( listAppointmentsDTO );
1507         }
1508 
1509         Stream<AppointmentDTO> stream = null;
1510         switch( _filter.getOrderBy( ) )
1511         {
1512             case LAST_NAME:
1513                 stream = sortedList.stream( ).sorted( ( a1, a2 ) -> a1.getLastName( ).compareTo( a2.getLastName( ) ) );
1514                 break;
1515             case FIRST_NAME:
1516                 stream = sortedList.stream( ).sorted( ( a1, a2 ) -> a1.getFirstName( ).compareTo( a2.getFirstName( ) ) );
1517                 break;
1518             case EMAIL:
1519                 stream = sortedList.stream( ).sorted( ( a1, a2 ) -> a1.getEmail( ).compareTo( a2.getEmail( ) ) );
1520                 break;
1521             case PHONE_NUMBER:
1522             	// Added 'Comparator.nullsLast' to avoid NullPointerException when comparing null values (a user's phone number might be NULL in the database)
1523             	stream = sortedList.stream( ).sorted( Comparator.comparing( AppointmentDTO::getPhoneNumber, Comparator.nullsLast( Comparator.naturalOrder() ) ) );
1524                 break;
1525             case NB_BOOKED_SEATS:
1526                 stream = sortedList.stream( ).sorted( ( a1, a2 ) -> Integer.compare( a1.getNbBookedSeats( ), a2.getNbBookedSeats( ) ) );
1527                 break;
1528             case DATE_APPOINTMENT:
1529                 stream = sortedList.stream( ).sorted( ( a1, a2 ) -> a1.getStartingDateTime( ).compareTo( a2.getStartingDateTime( ) ) );
1530                 break;
1531             case ADMIN:
1532                 stream = sortedList.stream( ).sorted( ( a1, a2 ) -> a1.getAdminUser( ).compareTo( a2.getAdminUser( ) ) );
1533                 break;
1534             case STATUS:
1535                 stream = sortedList.stream( ).sorted( ( a1, a2 ) -> Boolean.compare( a1.getIsCancelled( ), a2.getIsCancelled( ) ) );
1536                 break;
1537             default:
1538                 stream = sortedList.stream( ).sorted( ( a1, a2 ) -> a1.getStartingDateTime( ).compareTo( a2.getStartingDateTime( ) ) );
1539         }
1540         sortedList = stream.collect( Collectors.toList( ) );
1541         if ( !_filter.isOrderAsc( ) )
1542         {
1543             Collections.reverse( sortedList );
1544         }
1545         return sortedList;
1546     }
1547 
1548     /**
1549      * Get the URL to display the form of a workflow action. If the action has no form, then the user is redirected to the page to execute the workflow action
1550      * 
1551      * @param request
1552      *            The request
1553      * @param strIdAppointment
1554      *            The id of the appointment
1555      * @param strIdAction
1556      *            The id of the workflow action
1557      * @return The URL
1558      */
1559     public static String getUrlExecuteWorkflowAction( HttpServletRequest request, String strIdAppointment, String strIdAction )
1560     {
1561         UrlItem url = new UrlItem( AppPathService.getBaseUrl( request ) + JSP_MANAGE_APPOINTMENTS );
1562         url.addParameter( MVCUtils.PARAMETER_VIEW, VIEW_WORKFLOW_ACTION_FORM );
1563         url.addParameter( PARAMETER_ID_APPOINTMENT, strIdAppointment );
1564         url.addParameter( PARAMETER_ID_ACTION, strIdAction );
1565 
1566         return url.getUrl( );
1567     }
1568 
1569     /**
1570      * Get the workflow action form before processing the action. If the action does not need to display any form, then redirect the user to the workflow action
1571      * processing page.
1572      * 
1573      * @param request
1574      *            The request
1575      * @return The HTML content to display, or the next URL to redirect the user to
1576      */
1577     @View( VIEW_WORKFLOW_ACTION_FORM )
1578     public synchronized String getWorkflowActionForm( HttpServletRequest request )
1579     {
1580         String strIdAction = request.getParameter( PARAMETER_ID_ACTION );
1581         String strIdAppointment = request.getParameter( PARAMETER_ID_APPOINTMENT );
1582         if ( StringUtils.isNotEmpty( strIdAction ) && StringUtils.isNumeric( strIdAction ) && StringUtils.isNotEmpty( strIdAppointment )
1583                 && StringUtils.isNumeric( strIdAppointment ) )
1584         {
1585             int nIdAction = Integer.parseInt( strIdAction );
1586             int nIdAppointment = Integer.parseInt( strIdAppointment );
1587             if ( WorkflowService.getInstance( ).isDisplayTasksForm( nIdAction, getLocale( ) ) )
1588             {
1589                 ITaskService taskService = SpringContextService.getBean( TaskService.BEAN_SERVICE );
1590                 List<ITask> listActionTasks = taskService.getListTaskByIdAction( nIdAction, getLocale( ) );
1591                 if ( listActionTasks.stream( ).anyMatch( task -> task.getTaskType( ).getKey( ).equals( "taskReportAppointment" ) )
1592                 /*
1593                  * && WorkflowService.getInstance( ).canProcessAction( nIdAppointment, Appointment.APPOINTMENT_RESOURCE_TYPE, nIdAction, nExternalParentId,
1594                  * request, false, null )
1595                  */ )
1596                 {
1597                     AppointmentDTO appointment = AppointmentService.buildAppointmentDTOFromIdAppointment( nIdAppointment );
1598                     Map<String, String> additionalParameters = new HashMap<>( );
1599                     additionalParameters.put( PARAMETER_ID_APPOINTMENT, strIdAppointment );
1600                     additionalParameters.put( PARAMETER_NB_PLACE_TO_TAKE, String.valueOf( appointment.getNbBookedSeats( ) ) );
1601                     additionalParameters.put( PARAMETER_ID_FORM, String.valueOf( appointment.getIdForm( ) ) );
1602 
1603                     return redirect( request, VIEW_CALENDAR_MANAGE_APPOINTMENTS, additionalParameters );
1604                 }
1605                 String strHtmlTasksForm = WorkflowService.getInstance( ).getDisplayTasksForm( nIdAppointment, Appointment.APPOINTMENT_RESOURCE_TYPE, nIdAction,
1606                         request, getLocale( ), null );
1607                 Map<String, Object> model = new HashMap<>( );
1608                 model.put( MARK_TASKS_FORM, strHtmlTasksForm );
1609                 model.put( PARAMETER_ID_ACTION, nIdAction );
1610                 model.put( PARAMETER_ID_APPOINTMENT, nIdAppointment );
1611                 return getPage( PROPERTY_PAGE_TITLE_TASKS_FORM_WORKFLOW, TEMPLATE_TASKS_FORM_WORKFLOW, model );
1612             }
1613             return doProcessWorkflowAction( request );
1614         }
1615         return redirect( request, AppointmentFormJspBean.getURLManageAppointmentForms( request ) );
1616     }
1617 
1618     /**
1619      * Do process a workflow action over an appointment
1620      * 
1621      * @param request
1622      *            The request
1623      * @return The next URL to redirect to
1624      */
1625     @Action( ACTION_DO_PROCESS_WORKFLOW_ACTION )
1626     public synchronized String doProcessWorkflowAction( HttpServletRequest request )
1627     {
1628         String strIdAction = request.getParameter( PARAMETER_ID_ACTION );
1629         String strIdAppointment = request.getParameter( PARAMETER_ID_APPOINTMENT );
1630         if ( StringUtils.isNotEmpty( strIdAction ) && StringUtils.isNumeric( strIdAction ) && StringUtils.isNotEmpty( strIdAppointment )
1631                 && StringUtils.isNumeric( strIdAppointment ) )
1632         {
1633             int nIdAction = Integer.parseInt( strIdAction );
1634             int nIdAppointment = Integer.parseInt( strIdAppointment );
1635             Appointment appointment = AppointmentService.findAppointmentById( nIdAppointment );
1636 
1637             List<AppointmentSlot> listApptSlot = appointment.getListAppointmentSlot( );
1638             Slot slot = SlotService.findSlotById( listApptSlot.get( 0 ).getIdSlot( ) );
1639 
1640             if ( request.getParameter( PARAMETER_BACK ) == null )
1641             {
1642                 try
1643                 {
1644                     if ( WorkflowService.getInstance( ).isDisplayTasksForm( nIdAction, getLocale( ) ) )
1645                     {
1646                         String strError = WorkflowService.getInstance( ).doSaveTasksForm( nIdAppointment, Appointment.APPOINTMENT_RESOURCE_TYPE, nIdAction,
1647                                 slot.getIdForm( ), request, getLocale( ), null );
1648                         if ( strError != null )
1649                         {
1650                             return redirect( request, strError );
1651                         }
1652                     }
1653                     else
1654                     {
1655                         ITaskService taskService = SpringContextService.getBean( TaskService.BEAN_SERVICE );
1656                         List<ITask> listActionTasks = taskService.getListTaskByIdAction( nIdAction, getLocale( ) );
1657                         for ( ITask task : listActionTasks )
1658                         {
1659                             if ( task.getTaskType( ).getKey( ).equals( "taskChangeAppointmentStatus" ) && ( appointment.getIsCancelled( ) ) )
1660                             {
1661                                 for ( AppointmentSlot apptSlt : listApptSlot )
1662                                 {
1663 
1664                                     Slot slt = SlotService.findSlotById( apptSlt.getIdSlot( ) );
1665 
1666                                     if ( apptSlt.getNbPlaces( ) > slt.getNbRemainingPlaces( ) )
1667                                     {
1668 
1669                                         return redirect( request,
1670                                                 AdminMessageService.getMessageUrl( request, MESSAGE_UNVAILABLE_SLOT, AdminMessage.TYPE_STOP ) );
1671 
1672                                     }
1673                                 }
1674                             }
1675                         }
1676 
1677                         WorkflowService.getInstance( ).doProcessAction( nIdAppointment, Appointment.APPOINTMENT_RESOURCE_TYPE, nIdAction, slot.getIdForm( ),
1678                                 request, getLocale( ), false, null );
1679                         AppointmentListenerManager.notifyAppointmentWFActionTriggered( nIdAppointment, nIdAction );
1680                     }
1681                 }
1682                 catch( Exception e )
1683                 {
1684                     AppLogService.error( "Error Workflow", e );
1685                 }
1686                 Map<String, String> mapParams = new HashMap<>( );
1687                 mapParams.put( PARAMETER_ID_FORM, Integer.toString( slot.getIdForm( ) ) );
1688                 return redirect( request, VIEW_MANAGE_APPOINTMENTS, mapParams );
1689             }
1690             return redirect( request, VIEW_MANAGE_APPOINTMENTS, PARAMETER_ID_FORM, slot.getIdForm( ) );
1691         }
1692         return redirect( request, AppointmentFormJspBean.getURLManageAppointmentForms( request ) );
1693     }
1694 
1695     /**
1696      * Do change the status of an appointment
1697      * 
1698      * @param request
1699      *            The request
1700      * @return The next URL to redirect to
1701      * @throws AccessDeniedException
1702      *             If the user is not authorized to access this feature
1703      */
1704     @Action( ACTION_DO_CHANGE_APPOINTMENT_STATUS )
1705     public synchronized String doChangeAppointmentStatus( HttpServletRequest request ) throws AccessDeniedException
1706     {
1707         String strIdAppointment = request.getParameter( PARAMETER_ID_APPOINTMENT );
1708         String strStatusCancelled = request.getParameter( PARAMETER_STATUS_CANCELLED );
1709         if ( StringUtils.isNotEmpty( strIdAppointment ) && StringUtils.isNumeric( strIdAppointment ) && StringUtils.isNotEmpty( strStatusCancelled ) )
1710         {
1711             int nIdAppointment = Integer.parseInt( strIdAppointment );
1712             boolean bStatusCancelled = Boolean.parseBoolean( strStatusCancelled );
1713             Appointment appointment = AppointmentService.findAppointmentById( nIdAppointment );
1714             int idSlot = appointment.getListAppointmentSlot( ).get( 0 ).getIdSlot( );
1715             Slot slot = SlotService.findSlotById( idSlot );
1716             if ( !RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE, Integer.toString( slot.getIdForm( ) ),
1717                     AppointmentResourceIdService.PERMISSION_CHANGE_APPOINTMENT_STATUS, (User) getUser( ) ) )
1718             {
1719                 throw new AccessDeniedException( AppointmentResourceIdService.PERMISSION_CHANGE_APPOINTMENT_STATUS );
1720             }
1721             if ( appointment.getIsCancelled( ) && !bStatusCancelled )
1722             {
1723 
1724                 for ( AppointmentSlot apptSlot : appointment.getListAppointmentSlot( ) )
1725                 {
1726 
1727                     slot = SlotService.findSlotById( idSlot );
1728 
1729                     if ( ( apptSlot.getNbPlaces( ) > slot.getNbRemainingPlaces( ) ) )
1730                     {
1731                         return redirect( request, AdminMessageService.getMessageUrl( request, MESSAGE_UNVAILABLE_SLOT, AdminMessage.TYPE_STOP ) );
1732                     }
1733 
1734                 }
1735             }
1736             if ( appointment.getIsCancelled( ) != bStatusCancelled )
1737             {
1738                 appointment.setIsCancelled( bStatusCancelled );
1739                 AppointmentService.updateAppointment( appointment );
1740                 AppLogService.info( LogUtilities.buildLog( ACTION_DO_CHANGE_APPOINTMENT_STATUS, strIdAppointment, getUser( ) ) );
1741             }
1742             return redirect( request, VIEW_MANAGE_APPOINTMENTS, PARAMETER_ID_FORM, slot.getIdForm( ) );
1743         }
1744         return redirect( request, AppointmentFormJspBean.getURLManageAppointmentForms( request ) );
1745     }
1746 
1747     /**
1748      * List of all the available status of an appointment
1749      * 
1750      * @return the list of the status
1751      */
1752     private ReferenceList getListStatus( )
1753     {
1754         ReferenceList refListStatus = new ReferenceList( );
1755         refListStatus.addItem( -1, StringUtils.EMPTY );
1756         refListStatus.addItem( 0, I18nService.getLocalizedString( RESERVED, getLocale( ) ) );
1757         refListStatus.addItem( 1, I18nService.getLocalizedString( UNRESERVED, getLocale( ) ) );
1758         return refListStatus;
1759     }
1760 
1761     private List<AppointmentDTO> findListAppointmentsDTOByFilterByPage( )
1762     {
1763         int currentPage;
1764         try
1765         {
1766             currentPage = Integer.parseInt( _strCurrentPageIndex );
1767         }
1768         catch( NumberFormatException ex )
1769         {
1770             currentPage = 1;
1771         }
1772 
1773         int skip = ( currentPage - 1 ) * _nItemsPerPage;
1774 
1775         List<Integer> listIdAppointment = _filter.getListIdAppointment( );
1776 
1777         _filter.setListIdAppointment( _listAppointmentsIds.stream( ).skip( skip ).limit( _nItemsPerPage ).collect( Collectors.toList( ) ) );
1778 
1779         List<AppointmentDTO> listAppointmentsDTO = AppointmentService.findListAppointmentsDTOByFilter( _filter );
1780 
1781         _filter.setListIdAppointment( listIdAppointment );
1782 
1783         return listAppointmentsDTO;
1784     }
1785 
1786 }