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.time.DayOfWeek;
37  import java.time.LocalDate;
38  import java.time.LocalDateTime;
39  import java.time.LocalTime;
40  import java.time.temporal.TemporalField;
41  import java.time.temporal.WeekFields;
42  import java.util.ArrayList;
43  import java.util.Comparator;
44  import java.util.HashMap;
45  import java.util.LinkedHashMap;
46  import java.util.List;
47  import java.util.Locale;
48  import java.util.Map;
49  import java.util.Set;
50  import java.util.StringJoiner;
51  import java.util.stream.Collectors;
52  import java.util.stream.Stream;
53  
54  import javax.servlet.http.HttpServletRequest;
55  
56  import fr.paris.lutece.portal.business.file.File;
57  import fr.paris.lutece.portal.service.file.FileService;
58  import fr.paris.lutece.portal.service.file.IFileStoreServiceProvider;
59  import org.apache.commons.codec.binary.Base64;
60  import org.apache.commons.collections.CollectionUtils;
61  import org.apache.commons.lang3.StringUtils;
62  
63  import fr.paris.lutece.plugins.appointment.business.appointment.Appointment;
64  import fr.paris.lutece.plugins.appointment.business.appointment.AppointmentSlot;
65  import fr.paris.lutece.plugins.appointment.business.calendar.CalendarTemplate;
66  import fr.paris.lutece.plugins.appointment.business.calendar.CalendarTemplateHome;
67  import fr.paris.lutece.plugins.appointment.business.form.Form;
68  import fr.paris.lutece.plugins.appointment.business.message.FormMessage;
69  import fr.paris.lutece.plugins.appointment.business.planning.WeekDefinition;
70  import fr.paris.lutece.plugins.appointment.business.rule.ReservationRule;
71  import fr.paris.lutece.plugins.appointment.business.slot.Slot;
72  import fr.paris.lutece.plugins.appointment.exception.AppointmentSavedException;
73  import fr.paris.lutece.plugins.appointment.exception.SlotEditTaskExpiredTimeException;
74  import fr.paris.lutece.plugins.appointment.exception.SlotFullException;
75  import fr.paris.lutece.plugins.appointment.log.LogUtilities;
76  import fr.paris.lutece.plugins.appointment.service.AppointmentResponseService;
77  import fr.paris.lutece.plugins.appointment.service.AppointmentService;
78  import fr.paris.lutece.plugins.appointment.service.AppointmentUtilities;
79  import fr.paris.lutece.plugins.appointment.service.EntryService;
80  import fr.paris.lutece.plugins.appointment.service.FormMessageService;
81  import fr.paris.lutece.plugins.appointment.service.FormService;
82  import fr.paris.lutece.plugins.appointment.service.ReservationRuleService;
83  import fr.paris.lutece.plugins.appointment.service.SlotSafeService;
84  import fr.paris.lutece.plugins.appointment.service.SlotService;
85  import fr.paris.lutece.plugins.appointment.service.UserService;
86  import fr.paris.lutece.plugins.appointment.service.Utilities;
87  import fr.paris.lutece.plugins.appointment.service.WeekDefinitionService;
88  import fr.paris.lutece.plugins.appointment.service.listeners.AppointmentListenerManager;
89  import fr.paris.lutece.plugins.appointment.service.upload.AppointmentAsynchronousUploadHandler;
90  import fr.paris.lutece.plugins.appointment.web.dto.AppointmentDTO;
91  import fr.paris.lutece.plugins.appointment.web.dto.AppointmentFilterDTO;
92  import fr.paris.lutece.plugins.appointment.web.dto.AppointmentFormDTO;
93  import fr.paris.lutece.plugins.genericattributes.business.Entry;
94  import fr.paris.lutece.plugins.genericattributes.business.GenericAttributeError;
95  import fr.paris.lutece.plugins.genericattributes.business.Response;
96  import fr.paris.lutece.plugins.workflowcore.service.task.ITask;
97  import fr.paris.lutece.plugins.workflowcore.service.task.ITaskService;
98  import fr.paris.lutece.plugins.workflowcore.service.task.TaskService;
99  import fr.paris.lutece.portal.service.accesscontrol.AccessControlService;
100 import fr.paris.lutece.portal.service.admin.AccessDeniedException;
101 import fr.paris.lutece.portal.service.captcha.CaptchaSecurityService;
102 import fr.paris.lutece.portal.service.i18n.I18nService;
103 import fr.paris.lutece.portal.service.image.ImageResource;
104 import fr.paris.lutece.portal.service.message.SiteMessageException;
105 import fr.paris.lutece.portal.service.security.LuteceUser;
106 import fr.paris.lutece.portal.service.security.SecurityService;
107 import fr.paris.lutece.portal.service.security.UserNotSignedException;
108 import fr.paris.lutece.portal.service.spring.SpringContextService;
109 import fr.paris.lutece.portal.service.template.AppTemplateService;
110 import fr.paris.lutece.portal.service.util.AppLogService;
111 import fr.paris.lutece.portal.service.util.AppPathService;
112 import fr.paris.lutece.portal.service.util.AppPropertiesService;
113 import fr.paris.lutece.portal.service.workflow.WorkflowService;
114 import fr.paris.lutece.portal.util.mvc.commons.annotations.Action;
115 import fr.paris.lutece.portal.util.mvc.commons.annotations.View;
116 import fr.paris.lutece.portal.util.mvc.utils.MVCMessage;
117 import fr.paris.lutece.portal.util.mvc.utils.MVCUtils;
118 import fr.paris.lutece.portal.util.mvc.xpage.MVCApplication;
119 import fr.paris.lutece.portal.util.mvc.xpage.annotations.Controller;
120 import fr.paris.lutece.portal.web.xpages.XPage;
121 import fr.paris.lutece.util.ErrorMessage;
122 import fr.paris.lutece.util.html.HtmlTemplate;
123 import fr.paris.lutece.util.url.UrlItem;
124 
125 /**
126  * This class provides a simple implementation of an Appointment XPage (On Front Office)
127  * 
128  * @author Laurent Payen
129  *
130  */
131 @Controller( xpageName = AppointmentApp.XPAGE_NAME, pageTitleI18nKey = AppointmentApp.MESSAGE_DEFAULT_PAGE_TITLE, pagePathI18nKey = AppointmentApp.MESSAGE_DEFAULT_PATH )
132 public class AppointmentApp extends MVCApplication
133 {
134 
135     /**
136      * Default page of XPages of this app
137      */
138     public static final String MESSAGE_DEFAULT_PATH = "appointment.appointment.name";
139 
140     /**
141      * Default page title of XPages of this app
142      */
143     public static final String MESSAGE_DEFAULT_PAGE_TITLE = "appointment.appointmentApp.defaultTitle";
144 
145     /**
146      * The name of the XPage
147      */
148     protected static final String XPAGE_NAME = "appointment";
149 
150     /**
151      * Generated serial version UID
152      */
153     private static final long serialVersionUID = 5741361182728887387L;
154 
155     // Templates
156     private static final String TEMPLATE_APPOINTMENT_FORM_LIST = "/skin/plugins/appointment/appointment_form_list.html";
157     private static final String TEMPLATE_APPOINTMENT_FORM = "/skin/plugins/appointment/appointment_form.html";
158     private static final String TEMPLATE_APPOINTMENT_FORM_RECAP = "/skin/plugins/appointment/appointment_form_recap.html";
159     private static final String TEMPLATE_APPOINTMENT_CREATED = "skin/plugins/appointment/appointment_created.html";
160     private static final String TEMPLATE_CANCEL_APPOINTMENT = "skin/plugins/appointment/cancel_appointment.html";
161     private static final String TEMPLATE_APPOINTMENT_CANCELED = "skin/plugins/appointment/appointment_canceled.html";
162     private static final String TEMPLATE_MY_APPOINTMENTS = "skin/plugins/appointment/my_appointments.html";
163     private static final String TEMPLATE_HTML_CODE_FORM = "skin/plugins/appointment/html_code_form.html";
164     private static final String TEMPLATE_HTML_CODE_NB_PLACES_TO_TAKE_FORM = "skin/plugins/appointment/appointment_nb_places_to_take_form.html";
165     private static final String TEMPLATE_TASKS_FORM_WORKFLOW = "skin/plugins/appointment/tasks_form_workflow.html";
166     private static final String TEMPLATE_ERROR_APPOINTMENT_REFERENCE = "skin/plugins/appointment/error_appointment_reference.html";
167     
168     // Views
169     public static final String VIEW_APPOINTMENT_FORM = "getViewAppointmentForm";
170     public static final String VIEW_APPOINTMENT_CALENDAR = "getViewAppointmentCalendar";
171     private static final String VIEW_APPOINTMENT_FORM_LIST = "getViewFormList";
172     private static final String VIEW_DISPLAY_RECAP_APPOINTMENT = "displayRecapAppointment";
173     private static final String VIEW_GET_APPOINTMENT_CREATED = "getAppointmentCreated";
174     private static final String VIEW_APPOINTMENT_CANCELED = "getAppointmentCanceled";
175     private static final String VIEW_GET_MY_APPOINTMENTS = "getMyAppointments";
176     private static final String VIEW_GET_VIEW_CANCEL_APPOINTMENT = "getViewCancelAppointment";
177     private static final String VIEW_WORKFLOW_ACTION_FORM = "viewWorkflowActionForm";
178     private static final String VIEW_CHANGE_DATE_APPOINTMENT = "viewChangeDateAppointment";
179 
180     // Actions
181     private static final String ACTION_DO_VALIDATE_FORM = "doValidateForm";
182     private static final String ACTION_DO_MAKE_APPOINTMENT = "doMakeAppointment";
183     private static final String ACTION_DO_CANCEL_APPOINTMENT = "doCancelAppointment";
184     private static final String ACTION_DO_PROCESS_WORKFLOW_ACTION = "doProcessWorkflowAction";
185 
186     // Parameters
187     private static final String PARAMETER_STARTING_DATE_TIME = "starting_date_time";
188     private static final String PARAMETER_ENDING_DATE_OF_DISPLAY = "ending_date_of_display";
189     private static final String PARAMETER_STR_ENDING_DATE_OF_DISPLAY = "str_ending_date_of_display";
190     private static final String PARAMETER_DATE_OF_DISPLAY = "date_of_display";
191     private static final String PARAMETER_DAY_OF_WEEK = "dow";
192     private static final String PARAMETER_HIDDEN_DAYS = "hidden_days";
193     private static final String PARAMETER_ID_FORM = "id_form";
194     private static final String PARAMETER_EVENTS = "events";
195     private static final String PARAMETER_MIN_DURATION = "min_duration";
196     private static final String PARAMETER_MIN_TIME = "min_time";
197     private static final String PARAMETER_MAX_TIME = "max_time";
198     private static final String PARAMETER_EMAIL = "email";
199     private static final String PARAMETER_EMAIL_CONFIRMATION = "emailConfirm";
200     private static final String PARAMETER_FIRST_NAME = "firstname";
201     private static final String PARAMETER_LAST_NAME = "lastname";
202     private static final String PARAMETER_NUMBER_OF_BOOKED_SEATS = "nbBookedSeats";
203     private static final String PARAMETER_BACK = "back";
204     private static final String PARAMETER_REF_APPOINTMENT = "refAppointment";
205     private static final String PARAMETER_FROM_MY_APPOINTMENTS = "fromMyappointments";
206     private static final String PARAMETER_REFERER = "referer";
207     private static final String PARAMETER_WEEK_VIEW = "week_view";
208     private static final String PARAMETER_DAY_VIEW = "day_view";
209     private static final String PARAMETER_ANCHOR = "anchor";
210     private static final String PARAMETER_MODIFICATION_FORM = "mod";
211     private static final String PARAMETER_MIN_DATE_OF_OPEN_DAY = "min_date_of_open_day";
212     private static final String PARAMETER_MAX_DATE_OF_OPEN_DAY = "max_date_of_open_day";
213     private static final String PARAMETER_IS_MODIFICATION = "is_modification";
214     private static final String PARAMETER_NB_PLACE_TO_TAKE = "nbPlacesToTake";
215     private static final String PARAMETER_ID_ACTION = "id_action";
216     private static final String PARAMETER_MODIF_DATE = "modif_date";
217 
218     // Mark
219     private static final String MARK_MODIFICATION_DATE_APPOINTMENT = "modifDateAppointment";
220     private static final String MARK_NBPLACESTOTAKE = "nbPlacesToTake";
221     private static final String MARK_MAX_NBPLACESTOTAKE = "maxNbPlacesToTake";
222     private static final String MARK_INFOS = "infos";
223     private static final String MARK_LOCALE = "locale";
224     private static final String MARK_FORM = "form";
225     private static final String MARK_USER = "user";
226     private static final String MARK_FORM_MESSAGES = "formMessages";
227     private static final String MARK_STR_ENTRY = "str_entry";
228     private static final String MARK_APPOINTMENT = "appointment";
229     private static final String MARK_LIST_ERRORS = "listAllErrors";
230     private static final String MARK_PLACES = "nbplaces";
231     private static final String MARK_DATE_APPOINTMENT = "dateAppointment";
232     private static final String MARK_STARTING_TIME_APPOINTMENT = "startingTimeAppointment";
233     private static final String MARK_ENDING_TIME_APPOINTMENT = "endingTimeAppointment";
234     private static final String MARK_FORM_LIST = "form_list";
235     private static final String MARK_FORM_HTML = "form_html";
236     private static final String MARK_FORM_NB_PLACES_TO_TAKE_HTML = "mark_nb_places_to_take_form";
237     private static final String MARK_FORM_ERRORS = "form_errors";
238     private static final String MARK_FORM_CALENDAR_ERRORS = "formCalendarErrors";
239     private static final String MARK_CAPTCHA = "captcha";
240     private static final String MARK_REF = "%%REF%%";
241     private static final String MARK_DATE_APP = "%%DATE%%";
242     private static final String MARK_TIME_BEGIN = "%%HEURE_DEBUT%%";
243     private static final String MARK_TIME_END = "%%HEURE_FIN%%";
244     private static final String MARK_LIST_APPOINTMENTS = "list_appointments";
245     private static final String MARK_BACK_URL = "backUrl";
246     private static final String MARK_FROM_URL = "fromUrl";
247     private static final String MARK_LIST_RESPONSE_RECAP_DTO = "listResponseRecapDTO";
248     private static final String MARK_DATA = "data";
249     private static final String MARK_BASE_64 = "base64";
250     private static final String MARK_SEMI_COLON = ";";
251     private static final String MARK_COMMA = ",";
252     private static final String MARK_COLON = ":";
253     private static final String MARK_ICONS = "icons";
254     private static final String MARK_ICON_NULL = "NULL";
255     private static final String MARK_ANCHOR = "#";
256     private static final String MARK_APPOINTMENT_ALREADY_CANCELLED = "alreadyCancelled";
257     private static final String MARK_NO_APPOINTMENT_WITH_THIS_REFERENCE = "noAppointmentWithThisReference";
258     private static final String MARK_APPOINTMENT_PASSED = "appointmentPassed";
259     private static final String MARK_TASKS_FORM = "tasks_form";
260     private static final String MARK_LOCALE_DATE_TIME = "localeDateTime";
261     private static final String MARK_USER_PREFERRED_NAME = "preferred_user_name";
262 
263     // Errors
264     private static final String ERROR_MESSAGE_SLOT_FULL = "appointment.message.error.slotFull";
265     private static final String ERROR_MESSAGE_SLOT_EDIT_TASK_EXPIRED_TIME = "appointment.message.error.appointment.edit.expired.time";
266     private static final String ERROR_MESSAGE_CAPTCHA = "portal.admin.message.wrongCaptcha";
267     private static final String ERROR_MESSAGE_NB_MIN_DAYS_BETWEEN_TWO_APPOINTMENTS = "appointment.validation.appointment.NbMinDaysBetweenTwoAppointments.error";
268     private static final String ERROR_MESSAGE_NB_MAX_APPOINTMENTS_ON_A_PERIOD = "appointment.validation.appointment.NbMaxAppointmentsOnAPeriod.error";
269     private static final String ERROR_MESSAGE_NB_MAX_APPOINTMENTS_ON_A_CATEGORY = "appointment.validation.appointment.NbMaxAppointmentsOnCategory.error";
270     private static final String ERROR_MESSAGE_FORM_NOT_ACTIVE = "appointment.validation.appointment.formNotActive";
271     private static final String ERROR_MESSAGE_NO_STARTING_VALIDITY_DATE = "appointment.validation.appointment.noStartingValidityDate";
272     private static final String ERROR_MESSAGE_FORM_NO_MORE_VALID = "appointment.validation.appointment.formNoMoreValid";
273     private static final String ERROR_MESSAGE_REPORT_APPOINTMENT = "appointment.message.error.report.appointment";
274     private static final String ERROR_MESSAGE_NO_AVAILABLE_SLOT = "appointment.validation.appointment.noAvailableSlot";
275 
276     private static final String ERROR_MESSAGE_NB_PLACE_TO_TAKE_TO_BIG = "appointment.message.error.nbplacestotake.toobig";
277 
278     // Messages
279     private static final String MESSAGE_CANCEL_APPOINTMENT_PAGE_TITLE = "appointment.cancelAppointment.pageTitle";
280     private static final String MESSAGE_MY_APPOINTMENTS_PAGE_TITLE = "appointment.myAppointments.name";
281     private static final String MESSAGE_WF_ACTION_SUCESS = "appointment.wf.action.success";
282     
283     // Properties
284     private static final String PROPERTY_USER_ATTRIBUTE_FIRST_NAME = "appointment.userAttribute.firstName";
285     private static final String PROPERTY_USER_ATTRIBUTE_LAST_NAME = "appointment.userAttribute.lastName";
286     private static final String PROPERTY_USER_ATTRIBUTE_PREFERED_NAME = "appointment.userAttribute.preferred_username";
287     private static final String PROPERTY_USER_ATTRIBUTE_EMAIL = "appointment.userAttribute.email";
288     private static final String AGENDA_WEEK = "agendaWeek";
289     private static final String BASIC_WEEK = "basicWeek";
290     private static final String AGENDA_DAY = "agendaDay";
291     private static final String BASIC_DAY = "basicDay";
292     private static final String STEP_3 = "step3";
293 
294     // Local variables
295     private transient CaptchaSecurityService _captchaSecurityService;
296     private int _nNbPlacesToTake;
297     private String _strNbPlacesToTakeLength;
298     private AppointmentFormDTO _appointmentForm;
299     private AppointmentDTO _notValidatedAppointment;
300     private AppointmentDTO _validatedAppointment;
301 
302     /**
303      * Get the calendar view
304      * 
305      * @param request
306      * @return the Xpage
307      * @throws AccessDeniedException
308      */
309     @SuppressWarnings( "unchecked" )
310     @View( VIEW_APPOINTMENT_CALENDAR )
311     public synchronized XPage getViewAppointmentCalendar( HttpServletRequest request ) throws AccessDeniedException
312     {
313         Map<String, Object> model = getModel( );
314         Locale locale = getLocale( request );
315         _nNbPlacesToTake = 0;
316         
317         int nIdForm = Integer.parseInt( request.getParameter( PARAMETER_ID_FORM ) );
318         String nbPlacesToTake = request.getParameter( PARAMETER_NB_PLACE_TO_TAKE );
319         String refAppointment = request.getParameter( PARAMETER_REF_APPOINTMENT );
320 
321         _appointmentForm = FormService.buildAppointmentFormWithoutReservationRule( nIdForm );
322         _strNbPlacesToTakeLength = String.valueOf(_appointmentForm.getNbConsecutiveSlots());
323         boolean bError = false;
324         if ( !_appointmentForm.getIsActive( ) )
325         {
326             addError( ERROR_MESSAGE_FORM_NOT_ACTIVE, locale );
327             bError = true;
328         }
329         
330         FormMessage formMessages = FormMessageService.findFormMessageByIdForm( nIdForm );
331 
332         if ( StringUtils.isNotEmpty( refAppointment ) )
333         {
334             // If we want to change the date of an appointment
335             AppointmentDTO appointmentDTO = AppointmentService.buildAppointmentDTOFromRefAppointment( refAppointment );
336             // Check if an appointment Object was found and built with the given reference
337             if( appointmentDTO == null )
338             {
339             	// When the appointment Object doesn't exist, we display an error message to the user
340             	return getXPage( TEMPLATE_ERROR_APPOINTMENT_REFERENCE, locale, model );
341             }
342             if ( appointmentDTO.getIsCancelled( ) || appointmentDTO.getStartingDateTime( ).isBefore( LocalDateTime.now( ) ) )
343             {
344                 addError( ERROR_MESSAGE_REPORT_APPOINTMENT, locale );
345                 bError = true;
346             }
347             else
348             {
349                 _validatedAppointment = appointmentDTO;
350                 AppointmentService.addAppointmentResponses( _validatedAppointment );
351                 nbPlacesToTake = Integer.toString( _validatedAppointment.getNbBookedSeats( ) );
352             }
353         }
354         LocalDate startingDateOfDisplay = LocalDate.now( );
355         // Check if the date of display and the endDateOfDisplay are in the
356         // validity date range of the form
357         LocalDate startingValidityDate = null;
358         if ( _appointmentForm.getDateStartValidity( ) == null )
359         {
360             addError( ERROR_MESSAGE_NO_STARTING_VALIDITY_DATE, locale );
361             bError = true;
362         }
363         else
364         {
365             startingValidityDate = _appointmentForm.getDateStartValidity( ).toLocalDate( );
366         }
367         if ( startingValidityDate != null && startingValidityDate.isAfter( startingDateOfDisplay ) )
368         {
369             startingDateOfDisplay = startingValidityDate;
370         }
371         // Get the nb weeks to display
372         int nNbWeeksToDisplay = _appointmentForm.getNbWeeksToDisplay( );
373         // Calculate the ending date of display with the nb weeks to display
374         // since today
375         // We calculate the number of weeks including the current week, so it
376         // will end to the (n) next sunday
377         TemporalField fieldISO = WeekFields.of( locale ).dayOfWeek( );
378         LocalDate dateOfSunday = startingDateOfDisplay.with( fieldISO, DayOfWeek.SUNDAY.getValue( ) );
379         LocalDate endingDateOfDisplay = dateOfSunday.plusWeeks( (long) nNbWeeksToDisplay - 1 );
380         // if the ending date of display is after the ending validity date of
381         // the form
382         // assign the ending date of display with the ending validity date of
383         // the form
384         LocalDate endingValidityDate = null;
385         if ( _appointmentForm.getDateEndValidity( ) != null )
386         {
387             endingValidityDate = _appointmentForm.getDateEndValidity( ).toLocalDate( );
388             if ( endingDateOfDisplay.isAfter( endingValidityDate ) )
389             {
390                 endingDateOfDisplay = endingValidityDate;
391             }
392             if ( startingDateOfDisplay.isAfter( endingDateOfDisplay ) )
393             {
394                 addError( ERROR_MESSAGE_FORM_NO_MORE_VALID, locale );
395                 bError = true;
396             }
397         }
398         // Get the current date of display of the calendar, if it exists
399         String strDateOfDisplay = request.getParameter( PARAMETER_DATE_OF_DISPLAY );
400         LocalDate dateOfDisplay = startingDateOfDisplay;
401         if ( StringUtils.isNotEmpty( strDateOfDisplay ) )
402         {
403             dateOfDisplay = LocalDate.parse( strDateOfDisplay );
404         }
405         // Get all the week definitions
406         List<WeekDefinition> listWeekDefinition = WeekDefinitionService.findWeekDefinitionByDateOfApply( nIdForm, startingDateOfDisplay, endingDateOfDisplay );
407 
408         Map<WeekDefinition, ReservationRule> mapReservationRule = ReservationRuleService.findAllReservationRule( nIdForm, listWeekDefinition );
409         List<ReservationRule> listReservationRules = new ArrayList<>( mapReservationRule.values( ) );
410         // Get the min time of all the week definitions
411         LocalTime minStartingTime = WeekDefinitionService.getMinStartingTimeOfAListOfWeekDefinition( listReservationRules );
412         // Get the max time of all the week definitions
413         LocalTime maxEndingTime = WeekDefinitionService.getMaxEndingTimeOfAListOfWeekDefinition( listReservationRules );
414         // Get all the working days of all the week definitions
415 
416         List<String> listStrBase0OpenDaysOfWeek = new ArrayList<>(
417                 WeekDefinitionService.getSetDaysOfWeekOfAListOfWeekDefinitionForFullCalendar( listReservationRules ) );
418         // Build the slots if no errors
419         List<Slot> listSlots = new ArrayList<>( );
420         if ( !bError )
421         {
422             boolean isNewNbPlacesToTake = ( nbPlacesToTake != null && StringUtils.isNumeric( nbPlacesToTake ) );
423             if ( _appointmentForm.getIsMultislotAppointment( ) && ( _nNbPlacesToTake != 0 || isNewNbPlacesToTake ) )
424             {
425                 _nNbPlacesToTake = isNewNbPlacesToTake ? Integer.parseInt( nbPlacesToTake ) : _nNbPlacesToTake;
426                 listSlots = SlotService.buildListSlot( nIdForm, mapReservationRule, startingDateOfDisplay, endingDateOfDisplay, _nNbPlacesToTake );
427 
428             }
429             else
430             {
431                 _nNbPlacesToTake = 0;
432                 listSlots = SlotService.buildListSlot( nIdForm, mapReservationRule, startingDateOfDisplay, endingDateOfDisplay );
433             }
434             
435             if ( _nNbPlacesToTake > Integer.parseInt( _strNbPlacesToTakeLength ) )
436             {
437             	addError( ERROR_MESSAGE_NB_PLACE_TO_TAKE_TO_BIG, locale );
438             }
439 
440             // Check the Access Controls of the form, when displaying the calendar
441             XPage accessControlPage = AccessControlService.getInstance( ).doExecuteAccessControl(
442             		request,
443             		nIdForm,
444             		Form.RESOURCE_TYPE,
445             		null
446             		);
447             if ( accessControlPage != null )
448             {
449             	return accessControlPage;
450             }
451 
452             // Get the min time from now before a user can take an appointment (in hours)
453             int minTimeBeforeAppointment = _appointmentForm.getMinTimeBeforeAppointment( );
454             LocalDateTime dateTimeBeforeAppointment = LocalDateTime.now( ).plusHours( minTimeBeforeAppointment );
455             // Filter the list of slots
456             if ( CollectionUtils.isNotEmpty( listSlots ) )
457             {
458                 listSlots = listSlots.stream( ).filter( s -> s.getStartingDateTime( ).isAfter( dateTimeBeforeAppointment ) ).collect( Collectors.toList( ) );
459             }
460 
461             // If we change the date of an appointment
462             // filter the list of slot with only the ones that have enough places at
463             // the moment of the edition
464             if ( _validatedAppointment != null )
465             {
466                 int nbBookedSeats = _validatedAppointment.getNbBookedSeats( );
467                 listSlots = listSlots.stream( ).filter( s -> s.getNbPotentialRemainingPlaces( ) >= nbBookedSeats && s.getIsOpen( ) )
468                         .collect( Collectors.toList( ) );
469                 model.put( MARK_MODIFICATION_DATE_APPOINTMENT, true );
470                 model.put( PARAMETER_REF_APPOINTMENT, refAppointment );
471             }
472             else
473             {
474                 model.put( MARK_MODIFICATION_DATE_APPOINTMENT, false );
475             }
476 
477             LocalDate firstDateOfFreeOpenSlot = null;
478             if ( CollectionUtils.isNotEmpty( listSlots ) )
479             {
480                 // Need to find the first available slot from now (with time)
481                 List<Slot> listAvailableSlots = listSlots.stream( ).filter( s -> ( s.getNbPotentialRemainingPlaces( ) > 0 && s.getIsOpen( ) == Boolean.TRUE ) )
482                         .collect( Collectors.toList( ) );
483                 if ( CollectionUtils.isNotEmpty( listAvailableSlots ) )
484                 {
485                     firstDateOfFreeOpenSlot = listAvailableSlots.stream( ).min( ( s1, s2 ) -> s1.getStartingDateTime( ).compareTo( s2.getStartingDateTime( ) ) )
486                             .get( ).getDate( );
487                 }
488             }
489             if (firstDateOfFreeOpenSlot == null) {
490                 if (formMessages != null && StringUtils.isNotEmpty(formMessages.getNoAvailableSlot())) {
491                     addError(formMessages.getNoAvailableSlot());
492                 } else {
493                     addError(ERROR_MESSAGE_NO_AVAILABLE_SLOT, locale);
494                 }
495                 bError = true;
496             }
497             // Display the week with the first available slot
498             if ( firstDateOfFreeOpenSlot != null && firstDateOfFreeOpenSlot.isAfter( dateOfDisplay ) )
499             {
500                 dateOfDisplay = firstDateOfFreeOpenSlot;
501             }
502         }
503         if ( bError )
504         {
505             model.put( MARK_FORM_CALENDAR_ERRORS, bError );
506         }
507         if ( formMessages != null && StringUtils.isNotEmpty( formMessages.getCalendarDescription( ) ) )
508         {
509             List<ErrorMessage> listInfos = (List<ErrorMessage>) model.get( MARK_INFOS );
510             if ( listInfos == null )
511             {
512                 listInfos = new ArrayList<>( );
513                 model.put( MARK_INFOS, listInfos );
514             }
515             MVCMessage message = new MVCMessage( formMessages.getCalendarDescription( ) );
516             listInfos.add( message );
517         }
518 
519         CalendarTemplate calendarTemplate = CalendarTemplateHome.findByPrimaryKey( _appointmentForm.getCalendarTemplateId( ) );
520         List<String> listHiddenDays = Stream.of( "0", "1", "2", "3", "4", "5", "6" ).collect( Collectors.toList( ) );
521 
522         /**
523          * Calculate the hidden days and set the view (Day and week) with the type of calendar
524          */
525         String dayView = null;
526         String weekView = null;
527         switch( calendarTemplate.getTitle( ) )
528         {
529             case CalendarTemplate.FREE_SLOTS:
530                 // Keep only the available slots
531                 listSlots = listSlots.stream( ).filter( s -> ( ( s.getNbRemainingPlaces( ) > 0 ) && ( s.getIsOpen( ) ) ) ).collect( Collectors.toList( ) );
532                 listHiddenDays.clear( );
533                 dayView = BASIC_DAY;
534                 weekView = BASIC_WEEK;
535                 break;
536             case CalendarTemplate.FREE_SLOTS_GROUPED:
537                 // Keep only the available slots
538                 listHiddenDays.clear( );
539                 dayView = BASIC_DAY;
540                 weekView = BASIC_WEEK;
541                 break;
542             case CalendarTemplate.CALENDAR_OPEN_DAYS:
543                 // update the list of the days to hide
544                 listHiddenDays.removeAll( listStrBase0OpenDaysOfWeek );
545                 dayView = AGENDA_DAY;
546                 weekView = AGENDA_WEEK;
547                 break;
548             case CalendarTemplate.FREE_SLOTS_ON_OPEN_DAYS:
549                 // Keep only the available slots
550                 listSlots = listSlots.stream( ).filter( s -> ( ( s.getNbRemainingPlaces( ) > 0 ) && ( s.getIsOpen( ) ) ) ).collect( Collectors.toList( ) );
551                 // update the list of the days to hide
552                 listHiddenDays.removeAll( listStrBase0OpenDaysOfWeek );
553                 dayView = BASIC_DAY;
554                 weekView = BASIC_WEEK;
555                 break;
556             case CalendarTemplate.CALENDAR:
557             default:
558                 listHiddenDays.clear( );
559                 dayView = AGENDA_DAY;
560                 weekView = AGENDA_WEEK;
561                 break;
562         }
563         // Get the min and max date of the open days (for the week navigation on
564         // open days calendar templates)
565         Set<Integer> setOpenDays = WeekDefinitionService.getOpenDaysOfWeek( listReservationRules );
566         model.put( PARAMETER_MIN_DATE_OF_OPEN_DAY,
567                 LocalDate.now( ).with( DayOfWeek.of( setOpenDays.stream( ).min( Comparator.naturalOrder( ) ).orElse( 1 ) ) ) );
568         model.put( PARAMETER_MAX_DATE_OF_OPEN_DAY,
569                 endingDateOfDisplay.with( DayOfWeek.of( setOpenDays.stream( ).max( Comparator.naturalOrder( ) ).orElse( 1 ) ) ) );
570 
571         model.put( MARK_FORM, _appointmentForm );
572         model.put( PARAMETER_ID_FORM, nIdForm );
573         model.put( MARK_FORM_MESSAGES, formMessages );
574         model.put( PARAMETER_ENDING_DATE_OF_DISPLAY, endingDateOfDisplay );
575         model.put( PARAMETER_STR_ENDING_DATE_OF_DISPLAY, endingDateOfDisplay.format( Utilities.getFormatter( ) ) );
576         model.put( PARAMETER_DATE_OF_DISPLAY, dateOfDisplay );
577         model.put( PARAMETER_DAY_OF_WEEK, listStrBase0OpenDaysOfWeek );
578         model.put( PARAMETER_MIN_TIME, AppointmentUtilities.getMinTimeToDisplay( minStartingTime ) );
579         model.put( PARAMETER_MAX_TIME, AppointmentUtilities.getMaxTimeToDisplay( maxEndingTime ) );
580         model.put( PARAMETER_MIN_DURATION, LocalTime.MIN.plusMinutes( AppointmentUtilities.THIRTY_MINUTES ) );
581         model.put( MARK_NBPLACESTOTAKE, _nNbPlacesToTake );
582         model.put( PARAMETER_EVENTS, listSlots );
583         model.put( PARAMETER_HIDDEN_DAYS, listHiddenDays );
584         model.put( PARAMETER_DAY_VIEW, dayView );
585         model.put( PARAMETER_WEEK_VIEW, weekView );
586         model.put( MARK_MAX_NBPLACESTOTAKE, Integer.valueOf( _strNbPlacesToTakeLength ) );
587         HtmlTemplate templateNbPlacesToTakeForm = AppTemplateService.getTemplate( TEMPLATE_HTML_CODE_NB_PLACES_TO_TAKE_FORM, locale, model );
588         model.put( MARK_FORM_NB_PLACES_TO_TAKE_HTML, templateNbPlacesToTakeForm.getHtml( ) );
589 
590         return getXPage( calendarTemplate.getTemplatePath( ), locale, model );
591     }
592 
593     /**
594      * Get the form appointment view (front office)
595      * 
596      * @param request
597      *            the request
598      * @return the xpage
599      * @throws UserNotSignedException
600      */
601     @View( VIEW_APPOINTMENT_FORM )
602     public synchronized XPage getViewAppointmentForm( HttpServletRequest request ) throws UserNotSignedException, AccessDeniedException
603     {
604         String strIdForm = request.getParameter( PARAMETER_ID_FORM );
605         String strNbPlacesToTake = request.getParameter( PARAMETER_NB_PLACE_TO_TAKE );
606         String strModifDateAppointment = request.getParameter( PARAMETER_MODIF_DATE );
607 
608         if ( strModifDateAppointment != null && Boolean.parseBoolean( strModifDateAppointment ) && _validatedAppointment != null
609                 && _validatedAppointment.getIdAppointment( ) != 0 )
610         {
611             return getViewChangeDateAppointment( request );
612         }
613         if ( strNbPlacesToTake != null )
614         {
615             _nNbPlacesToTake = Integer.parseInt( strNbPlacesToTake );
616         }
617 
618         int nIdForm = Integer.parseInt( strIdForm );
619         if ( _appointmentForm == null || _appointmentForm.getIdForm( ) != nIdForm )
620         {
621             _appointmentForm = FormService.buildAppointmentFormWithoutReservationRule( nIdForm );
622         }
623         if ( !_appointmentForm.getIsActive( ) )
624         {
625             addError( ERROR_MESSAGE_FORM_NOT_ACTIVE, getLocale( request ) );
626             return redirect( request, VIEW_APPOINTMENT_CALENDAR, PARAMETER_ID_FORM, nIdForm, PARAMETER_NB_PLACE_TO_TAKE, _nNbPlacesToTake );
627         }
628         if(!_appointmentForm.getIsMultislotAppointment())
629         {
630             _nNbPlacesToTake = 0;
631         }
632         checkMyLuteceAuthentication( _appointmentForm, request );
633         // Patch needed for authentication after being on the form
634         String secondAttempt = request.getParameter( "secondAttempt" );
635         boolean bTestSecondAttempt = Boolean.FALSE;
636         if ( StringUtils.isNotEmpty( secondAttempt ) )
637         {
638             bTestSecondAttempt = Boolean.TRUE;
639         }
640         // Need to manage the anchor
641         String anchor = request.getParameter( PARAMETER_ANCHOR );
642         if ( StringUtils.isNotEmpty( anchor ) )
643         {
644             LinkedHashMap<String, String> additionalParameters = new LinkedHashMap<>( );
645             additionalParameters.put( PARAMETER_ID_FORM, strIdForm );
646             additionalParameters.put( PARAMETER_STARTING_DATE_TIME, request.getParameter( PARAMETER_STARTING_DATE_TIME ) );
647             additionalParameters.put( PARAMETER_NB_PLACE_TO_TAKE, Integer.toString( _nNbPlacesToTake ) );
648             additionalParameters.put( PARAMETER_ANCHOR, MARK_ANCHOR + anchor );
649             return redirect( request, VIEW_APPOINTMENT_FORM, additionalParameters );
650 
651         }
652 
653         String isModification = request.getParameter( PARAMETER_IS_MODIFICATION );
654         boolean bModificationForm = false;
655         List<Slot> listSlot = null;
656         if ( isModification != null )
657 
658         {
659             bModificationForm = true;
660 
661         }
662         else
663         {
664 
665             int nNbConsecutiveSlot = ( _nNbPlacesToTake == 0 ) ? 1 : _nNbPlacesToTake;
666             LocalDateTime startingDateTime = LocalDateTime.parse( request.getParameter( PARAMETER_STARTING_DATE_TIME ) );
667             if ( !isAuthorizedDate( startingDateTime, getLocale( request ) ) )
668             {
669 
670                 addError( ERROR_MESSAGE_SLOT_FULL, getLocale( request ) );
671                 return redirect( request, VIEW_APPOINTMENT_CALENDAR, PARAMETER_ID_FORM, nIdForm, PARAMETER_NB_PLACE_TO_TAKE, _nNbPlacesToTake );
672             }
673             // Get all the week definitions
674             List<WeekDefinition> listWeekDefinition = WeekDefinitionService.findListWeekDefinition( nIdForm );
675             Map<WeekDefinition, ReservationRule> mapReservationRule = ReservationRuleService.findAllReservationRule( nIdForm, listWeekDefinition );
676 
677             listSlot = SlotService.buildListSlot( nIdForm, mapReservationRule, startingDateTime.toLocalDate( ), startingDateTime.toLocalDate( ) );
678             listSlot = listSlot.stream( ).filter(
679                     s -> ( ( startingDateTime.compareTo( s.getStartingDateTime( ) ) <= 0 ) && ( s.getNbRemainingPlaces( ) > 0 ) && ( s.getIsOpen( ) ) ) )
680                     .limit( nNbConsecutiveSlot ).collect( Collectors.toList( ) );
681 
682             if ( listSlot == null || listSlot.stream( ).noneMatch( slot -> slot.getStartingDateTime( ).isEqual( startingDateTime ) )
683                     || ( _nNbPlacesToTake > 0 && listSlot.size( ) != _nNbPlacesToTake ) || !AppointmentUtilities.isConsecutiveSlots( listSlot ) )
684             {
685                 addError( ERROR_MESSAGE_SLOT_FULL, getLocale( request ) );
686                 return redirect( request, VIEW_APPOINTMENT_CALENDAR, PARAMETER_ID_FORM, nIdForm, PARAMETER_NB_PLACE_TO_TAKE, _nNbPlacesToTake );
687             }
688 
689         }
690 
691         if ( _notValidatedAppointment == null || _notValidatedAppointment.getIdForm( ) != _appointmentForm.getIdForm( ) )
692         {
693             if ( _validatedAppointment != null && _validatedAppointment.getIdForm( ) == _appointmentForm.getIdForm( ) )
694             {
695 
696                 // Try to get the validated appointment in session
697                 // (in case the user click on back button in the recap view (or
698                 // modification)
699                 _notValidatedAppointment = _validatedAppointment;
700                 _validatedAppointment = null;
701             }
702             else
703             {
704                 // Need to get back the informations the user has entered
705                 _notValidatedAppointment = new AppointmentDTO( );
706             }
707         }
708         if ( !bModificationForm )
709         {
710 
711             boolean bool = true;
712             _notValidatedAppointment.setIdForm( nIdForm );
713             _notValidatedAppointment.setSlot( null );
714             _notValidatedAppointment.setNbMaxPotentialBookedSeats( 0 );
715             for ( Slot slot : listSlot )
716             {
717 
718                 // If nIdSlot == 0, the slot has not been created yet
719                 if ( slot.getIdSlot( ) == 0 )
720                 {
721                     slot = SlotSafeService.createSlot( slot );
722                 }
723                 else
724                 {
725                     slot = SlotService.findSlotById( slot.getIdSlot( ) );
726                 }
727 
728                 // Need to check competitive access
729                 // May be the slot is already taken at the same time
730                 if ( !bTestSecondAttempt && slot.getNbPotentialRemainingPlaces( ) == 0 )
731                 {
732                     _notValidatedAppointment = null;
733                     addError( ERROR_MESSAGE_SLOT_FULL, getLocale( request ) );
734                     return redirect( request, VIEW_APPOINTMENT_CALENDAR, PARAMETER_ID_FORM, nIdForm, PARAMETER_NB_PLACE_TO_TAKE, _nNbPlacesToTake );
735                 }
736 
737                 _notValidatedAppointment.addSlot( slot );
738 
739                 if ( bool )
740                 {
741                     _notValidatedAppointment.setDateOfTheAppointment( slot.getDate( ).format( Utilities.getFormatter( ) ) );
742                     if ( SecurityService.getInstance( ).getRegisteredUser( request ) != null )
743                     {
744                         setUserInfo( request, _notValidatedAppointment );
745                     }
746                     FormService.fillAppointmentFormWithReservationRulePart( _appointmentForm,
747                             ReservationRuleService.findReservationRuleByIdFormAndClosestToDateOfApply( nIdForm, slot.getDate( ) ) );
748                     bool = false;
749                 }
750                 AppointmentUtilities.putTimerInSession( request, slot.getIdSlot( ), _notValidatedAppointment, _appointmentForm.getMaxPeoplePerAppointment( ) );
751             }
752             if ( _notValidatedAppointment.getNbMaxPotentialBookedSeats( ) == 0 )
753             {
754                 addError( ERROR_MESSAGE_SLOT_FULL, getLocale( request ) );
755                 return redirect( request, VIEW_APPOINTMENT_CALENDAR, PARAMETER_ID_FORM, nIdForm, PARAMETER_NB_PLACE_TO_TAKE, _nNbPlacesToTake );
756             }
757         }
758         else
759         {
760             // Modification of the Form only, Need to redirect for the anchor
761             if ( StringUtils.isEmpty( request.getParameter( PARAMETER_MODIFICATION_FORM ) ) )
762             {
763                 LinkedHashMap<String, String> additionalParameters = new LinkedHashMap<>( );
764                 additionalParameters.put( PARAMETER_ID_FORM, strIdForm );
765                 additionalParameters.put( PARAMETER_MODIFICATION_FORM, String.valueOf( Boolean.TRUE ) );
766                 additionalParameters.put( PARAMETER_IS_MODIFICATION, String.valueOf( Boolean.TRUE ) );
767                 additionalParameters.put( PARAMETER_ANCHOR, MARK_ANCHOR + STEP_3 );
768                 return redirect( request, VIEW_APPOINTMENT_FORM, additionalParameters );
769             }
770         }
771         Map<String, Object> model = getModel( );
772         Locale locale = getLocale( request );
773         StringBuilder strBuffer = new StringBuilder( );
774         List<Entry> listEntryFirstLevel = EntryService.getFilter( _appointmentForm.getIdForm( ), true );
775         for ( Entry entry : listEntryFirstLevel )
776         {
777             EntryService.getHtmlEntry( model, entry.getIdEntry( ), strBuffer, locale, true, _notValidatedAppointment );
778         }
779         FormMessage formMessages = FormMessageService.findFormMessageByIdForm( nIdForm );
780 
781         if ( _nNbPlacesToTake != 0 )
782         {
783 
784             _notValidatedAppointment.setNbBookedSeats( _nNbPlacesToTake );
785         }
786         model.put( MARK_APPOINTMENT, _notValidatedAppointment );
787         model.put( MARK_NBPLACESTOTAKE, _nNbPlacesToTake );
788         model.put( PARAMETER_DATE_OF_DISPLAY, _notValidatedAppointment.getSlot( ).get( 0 ).getDate( ) );
789         model.put( MARK_FORM, _appointmentForm );
790         model.put( MARK_FORM_MESSAGES, formMessages );
791         model.put( MARK_STR_ENTRY, strBuffer.toString( ) );
792         model.put( MARK_LOCALE, locale );
793         model.put( MARK_PLACES, _notValidatedAppointment.getNbMaxPotentialBookedSeats( ) );
794         model.put( MARK_LIST_ERRORS, AppointmentDTO.getAllErrors( locale ) );
795         LuteceUser user = SecurityService.getInstance( ).getRegisteredUser( request );
796         if( user != null )
797         {
798         	model.put( MARK_USER,  user );
799         	model.put( MARK_USER_PREFERRED_NAME, user.getUserInfo( AppPropertiesService.getProperty( PROPERTY_USER_ATTRIBUTE_PREFERED_NAME, StringUtils.EMPTY ) ) );
800         }
801         else
802         {
803         	model.put( MARK_USER,  null );
804         }
805 
806         // Check the Access Controls of the form
807         XPage accessControlPage = AccessControlService.getInstance( ).doExecuteAccessControl(
808         		request,
809         		nIdForm,
810         		Form.RESOURCE_TYPE,
811         		null
812         		);
813         if ( accessControlPage != null )
814         {
815         	return accessControlPage;
816         }
817 
818         HtmlTemplate templateForm = AppTemplateService.getTemplate( TEMPLATE_HTML_CODE_FORM, locale, model );
819         model.put( MARK_FORM_HTML, templateForm.getHtml( ) );
820         XPage xPage = getXPage( TEMPLATE_APPOINTMENT_FORM, locale, model );
821 
822         if ( _appointmentForm.getDisplayTitleFo( ) )
823         {
824             xPage.setTitle( _appointmentForm.getTitle( ) );
825         }
826         return xPage;
827     }
828 
829     /**
830      * Do validate data entered by a user to fill a form
831      * 
832      * @param request
833      *            The request
834      * @return The next URL to redirect to
835      * @throws SiteMessageException
836      * @throws UserNotSignedException
837      * @throws AccessDeniedException
838      */
839     @Action( ACTION_DO_VALIDATE_FORM )
840     public synchronized XPage doValidateForm( HttpServletRequest request ) throws UserNotSignedException, AccessDeniedException
841     {
842         checkMyLuteceAuthentication( _appointmentForm, request );
843         String strIdForm = request.getParameter( PARAMETER_ID_FORM );
844         if ( _notValidatedAppointment == null || _appointmentForm == null || _notValidatedAppointment.getIdForm( ) != _appointmentForm.getIdForm( ) )
845         {
846 
847             addError( ERROR_MESSAGE_FORM_NO_MORE_VALID, getLocale( request ) );
848             _notValidatedAppointment = null;
849             _validatedAppointment = null;
850             return redirectView( request, VIEW_APPOINTMENT_FORM_LIST );
851         }
852         List<GenericAttributeError> listFormErrors = new ArrayList<>( );
853         Locale locale = request.getLocale( );
854         String strEmail = request.getParameter( PARAMETER_EMAIL );
855         String strEmailConfirm = request.getParameter( PARAMETER_EMAIL_CONFIRMATION );
856         String strFirstName = request.getParameter( PARAMETER_FIRST_NAME );
857         String strLastName = request.getParameter( PARAMETER_LAST_NAME );
858         AppointmentUtilities.checkDateOfTheAppointmentIsNotBeforeNow( _notValidatedAppointment, locale, listFormErrors );
859         AppointmentUtilities.checkEmail( strEmail, strEmailConfirm, _appointmentForm, locale, listFormErrors );
860         int nbBookedSeats = _nNbPlacesToTake;
861         if ( _nNbPlacesToTake == 0 )
862         {
863 
864             nbBookedSeats = AppointmentUtilities.checkAndReturnNbBookedSeats( request.getParameter( PARAMETER_NUMBER_OF_BOOKED_SEATS ), _appointmentForm,
865                     _notValidatedAppointment, locale, listFormErrors );
866 
867         }
868         AppointmentUtilities.fillAppointmentDTO( _notValidatedAppointment, nbBookedSeats, strEmail, strEmailConfirm, strFirstName, strLastName );
869         AppointmentUtilities.validateFormAndEntries( _notValidatedAppointment, request, listFormErrors, false );
870         AppointmentUtilities.fillInListResponseWithMapResponse( _notValidatedAppointment );
871         AppointmentUtilities.setAppointmentPhoneNumberValuesFromResponse( _notValidatedAppointment );
872 
873         boolean bErrors = false;
874         if ( _appointmentForm.getEnableMandatoryEmail( )
875                 && !AppointmentUtilities.checkNbDaysBetweenTwoAppointmentsTaken( _notValidatedAppointment, strEmail, _appointmentForm ) )
876         {
877             addError( ERROR_MESSAGE_NB_MIN_DAYS_BETWEEN_TWO_APPOINTMENTS, locale );
878             bErrors = true;
879         }
880         if ( _appointmentForm.getEnableMandatoryEmail( )
881                 && !AppointmentUtilities.checkNbMaxAppointmentsOnAGivenPeriod( _notValidatedAppointment, strEmail, _appointmentForm ) )
882         {
883             addError( ERROR_MESSAGE_NB_MAX_APPOINTMENTS_ON_A_PERIOD, locale );
884             bErrors = true;
885         }
886         
887         List<AppointmentDTO> listAppointments = new ArrayList<>( );
888         if ( _appointmentForm.getEnableMandatoryEmail( )
889                 && !AppointmentUtilities.checkNbMaxAppointmentsDefinedOnCategory( _notValidatedAppointment, strEmail, _appointmentForm, listAppointments ) )
890         {
891             StringJoiner builder = new StringJoiner( StringUtils.SPACE );
892             String lf = System.getProperty( "line.separator" );
893             for ( AppointmentDTO appt : listAppointments )
894             {
895                 builder.add( appt.getLastName( ) );
896                 builder.add( appt.getFirstName( ) );
897                 builder.add( appt.getDateOfTheAppointment( ) );
898                 builder.add( appt.getStartingTime( ).toString( ) );
899                 builder.add( lf );
900             }
901             Object [ ] tabAppointment = {
902                     builder.toString( )
903             };
904 
905             String strErrorMessageDateWithAppointments = I18nService.getLocalizedString( ERROR_MESSAGE_NB_MAX_APPOINTMENTS_ON_A_CATEGORY, tabAppointment,
906                     locale );
907             addError( strErrorMessageDateWithAppointments );
908             bErrors = true;
909         }
910         if ( CollectionUtils.isNotEmpty( listFormErrors ) )
911         {
912             getModel( ).put( MARK_FORM_ERRORS, listFormErrors );
913             for ( GenericAttributeError error : listFormErrors )
914             {
915             	addError( error.getErrorMessage( ) );
916             }
917             bErrors = true;
918         }
919         if ( bErrors )
920         {
921             LinkedHashMap<String, String> additionalParameters = new LinkedHashMap<>( );
922             additionalParameters.put( PARAMETER_ID_FORM, strIdForm );
923             additionalParameters.put( PARAMETER_MODIFICATION_FORM, String.valueOf( Boolean.TRUE ) );
924             additionalParameters.put( PARAMETER_IS_MODIFICATION, String.valueOf( Boolean.TRUE ) );
925             additionalParameters.put( PARAMETER_ANCHOR, MARK_ANCHOR + STEP_3 );
926             return redirect( request, VIEW_APPOINTMENT_FORM, additionalParameters );
927         }
928         _validatedAppointment = _notValidatedAppointment;
929         _notValidatedAppointment = null;
930         String anchor = request.getParameter( PARAMETER_ANCHOR );
931         if ( StringUtils.isNotEmpty( anchor ) )
932         {
933             Map<String, String> additionalParameters = new HashMap<>( );
934             additionalParameters.put( PARAMETER_ANCHOR, MARK_ANCHOR + anchor );
935 
936             return redirect( request, VIEW_DISPLAY_RECAP_APPOINTMENT, additionalParameters );
937         }
938         else
939         {
940             return redirectView( request, VIEW_DISPLAY_RECAP_APPOINTMENT );
941         }
942     }
943 
944     /**
945      * Display the recap before validating an appointment
946      * 
947      * @param request
948      *            The request
949      * @return The HTML content to display or the next URL to redirect to
950      * @throws UserNotSignedException
951      * @throws AccessDeniedException
952      */
953     @View( VIEW_DISPLAY_RECAP_APPOINTMENT )
954     public synchronized XPage displayRecapAppointment( HttpServletRequest request ) throws UserNotSignedException, AccessDeniedException
955     {
956         checkMyLuteceAuthentication( _appointmentForm, request );
957         String anchor = request.getParameter( PARAMETER_ANCHOR );
958         String strModifDateAppointment = request.getParameter( PARAMETER_MODIF_DATE );
959 
960         if ( StringUtils.isNotEmpty( anchor ) )
961         {
962             Map<String, String> additionalParameters = new HashMap<>( );
963             additionalParameters.put( PARAMETER_ANCHOR, MARK_ANCHOR + anchor );
964             return redirect( request, VIEW_DISPLAY_RECAP_APPOINTMENT, additionalParameters );
965         }
966 
967         if ( _validatedAppointment == null || _appointmentForm == null || _validatedAppointment.getIdForm( ) != _appointmentForm.getIdForm( ) )
968         {
969             addError( ERROR_MESSAGE_FORM_NO_MORE_VALID, getLocale( request ) );
970             _notValidatedAppointment = null;
971             _validatedAppointment = null;
972             return redirectView( request, VIEW_APPOINTMENT_FORM_LIST );
973         }
974         Map<String, Object> model = new HashMap<>( );
975         if ( _appointmentForm.getEnableCaptcha( ) && getCaptchaService( ).isAvailable( ) )
976         {
977             model.put( MARK_CAPTCHA, getCaptchaService( ).getHtmlCode( ) );
978         }
979         if ( strModifDateAppointment != null && Boolean.parseBoolean( strModifDateAppointment ) )
980         {
981             model.put( MARK_MODIFICATION_DATE_APPOINTMENT, Boolean.TRUE );
982 
983         }
984         else
985         {
986 
987             model.put( MARK_MODIFICATION_DATE_APPOINTMENT, Boolean.FALSE );
988 
989         }
990         model.put( MARK_FORM_MESSAGES, FormMessageService.findFormMessageByIdForm( _validatedAppointment.getIdForm( ) ) );
991         fillCommons( model );
992         model.put( MARK_APPOINTMENT, _validatedAppointment );
993         Locale locale = getLocale( request );
994         model.put( MARK_LIST_RESPONSE_RECAP_DTO, AppointmentUtilities.buildListResponse( _validatedAppointment, request, locale ) );
995         model.put( MARK_FORM, _appointmentForm );
996         model.put( MARK_NBPLACESTOTAKE, _nNbPlacesToTake );
997         model.put( PARAMETER_DATE_OF_DISPLAY, _validatedAppointment.getSlot( ).get( 0 ).getDate( ) );
998 
999         return getXPage( TEMPLATE_APPOINTMENT_FORM_RECAP, locale, model );
1000     }
1001 
1002     /**
1003      * Do save an appointment into the database if it is valid
1004      * 
1005      * @param request
1006      *            The request
1007      * @return The XPage to display
1008      * @throws UserNotSignedException
1009      * @throws AccessDeniedException
1010      */
1011     @Action( ACTION_DO_MAKE_APPOINTMENT )
1012     public synchronized XPage doMakeAppointment( HttpServletRequest request ) throws UserNotSignedException, AccessDeniedException
1013     {
1014         checkMyLuteceAuthentication( _appointmentForm, request );
1015         if ( _validatedAppointment == null || _appointmentForm == null || _validatedAppointment.getIdForm( ) != _appointmentForm.getIdForm( ) )
1016         {
1017             addError( ERROR_MESSAGE_FORM_NO_MORE_VALID, getLocale( request ) );
1018             _notValidatedAppointment = null;
1019             _validatedAppointment = null;
1020             return redirectView( request, VIEW_APPOINTMENT_FORM_LIST );
1021         }
1022         if ( !_appointmentForm.getIsActive( ) )
1023         {
1024             addError( ERROR_MESSAGE_FORM_NOT_ACTIVE, getLocale( request ) );
1025             return redirect( request, VIEW_APPOINTMENT_CALENDAR, PARAMETER_ID_FORM, _appointmentForm.getIdForm( ), PARAMETER_NB_PLACE_TO_TAKE,
1026                     _nNbPlacesToTake );
1027         }
1028 
1029         if ( StringUtils.isNotEmpty( request.getParameter( PARAMETER_BACK ) ) )
1030         {
1031             LinkedHashMap<String, String> parameters = new LinkedHashMap<>( );
1032             parameters.put( PARAMETER_ID_FORM, String.valueOf( _validatedAppointment.getIdForm( ) ) );
1033             parameters.put( PARAMETER_IS_MODIFICATION, String.valueOf( Boolean.TRUE ) );
1034 
1035             return redirect( request, VIEW_APPOINTMENT_FORM, parameters );
1036         }
1037         if ( _appointmentForm.getEnableCaptcha( ) && getCaptchaService( ).isAvailable( ) && !getCaptchaService( ).validate( request ) )
1038         {
1039             addError( ERROR_MESSAGE_CAPTCHA, getLocale( request ) );
1040             return redirect( request, VIEW_DISPLAY_RECAP_APPOINTMENT, PARAMETER_ID_FORM, _validatedAppointment.getIdForm( ) );
1041         }
1042 
1043         int nIdAppointment;
1044         try
1045         {
1046 
1047             nIdAppointment = SlotSafeService.saveAppointment( _validatedAppointment, request );
1048 
1049         }
1050         catch( SlotFullException e )
1051         {
1052 
1053             addError( ERROR_MESSAGE_SLOT_FULL, getLocale( request ) );
1054             return redirect( request, VIEW_APPOINTMENT_CALENDAR, PARAMETER_ID_FORM, _validatedAppointment.getIdForm( ), PARAMETER_NB_PLACE_TO_TAKE,
1055                     _nNbPlacesToTake );
1056         }
1057         catch( SlotEditTaskExpiredTimeException e )
1058         {
1059             addError( ERROR_MESSAGE_SLOT_EDIT_TASK_EXPIRED_TIME, getLocale( request ) );
1060             return redirect( request, VIEW_APPOINTMENT_CALENDAR, PARAMETER_ID_FORM, _validatedAppointment.getIdForm( ), PARAMETER_NB_PLACE_TO_TAKE,
1061                     _nNbPlacesToTake );
1062         }
1063         catch( AppointmentSavedException e )
1064         {
1065             nIdAppointment = _validatedAppointment.getIdAppointment( );
1066             AppLogService.error( "Error Save appointment: " + e.getMessage( ), e );
1067         }
1068         AppLogService.info( LogUtilities.buildLog( ACTION_DO_MAKE_APPOINTMENT, Integer.toString( nIdAppointment ), null ) );
1069         AppointmentAsynchronousUploadHandler.getHandler( ).removeSessionFiles( request.getSession( ) );
1070         _nNbPlacesToTake = 0;
1071         int nIdForm = _validatedAppointment.getIdForm( );
1072        // _validatedAppointment = null;
1073 
1074         // Remove the session data of this form
1075         AccessControlService.getInstance( ).cleanSessionData( request, nIdForm, Form.RESOURCE_TYPE );
1076 
1077         String anchor = request.getParameter( PARAMETER_ANCHOR );
1078         if ( StringUtils.isNotEmpty( anchor ) )
1079         {
1080             LinkedHashMap<String, String> additionalParameters = new LinkedHashMap<>( );
1081             additionalParameters.put( PARAMETER_ID_FORM, String.valueOf( nIdForm ) );
1082             additionalParameters.put( PARAMETER_ANCHOR, MARK_ANCHOR + anchor );
1083             return redirect( request, VIEW_GET_APPOINTMENT_CREATED, additionalParameters );
1084         }
1085         else
1086         {
1087             return redirect( request, VIEW_GET_APPOINTMENT_CREATED, PARAMETER_ID_FORM, nIdForm );
1088         }
1089     }
1090 
1091     /**
1092      * Get the page to notify the user that the appointment has been created
1093      * 
1094      * @param request
1095      *            The request
1096      * @return The XPage to display
1097      */
1098     @View( VIEW_GET_APPOINTMENT_CREATED )
1099     public synchronized XPage getAppointmentCreated( HttpServletRequest request )
1100     {
1101     	if( _validatedAppointment == null )
1102     	{
1103     		return redirectView( request, VIEW_APPOINTMENT_FORM_LIST );
1104     	}
1105         int nIdForm = Integer.parseInt( request.getParameter( PARAMETER_ID_FORM ) );
1106         FormMessage formMessages = FormMessageService.findFormMessageByIdForm( nIdForm );
1107         AppointmentFormDTO form = FormService.buildAppointmentForm( nIdForm, 0 );
1108         Slot firstSlot = _validatedAppointment.getSlot().get(0);
1109         Slot lastSlot = firstSlot;
1110         if (  _validatedAppointment.getSlot().size( ) > 1 )
1111         {
1112                     lastSlot = _validatedAppointment.getSlot().get(_validatedAppointment.getSlot().size( ) -1 );
1113         }
1114         String strTimeBegin = firstSlot.getStartingDateTime( ).toLocalTime( ).toString( );
1115         String strTimeEnd = lastSlot.getEndingDateTime( ).toLocalTime( ).toString( );
1116         formMessages.setTextAppointmentCreated( formMessages.getTextAppointmentCreated( ).replace( MARK_REF, _validatedAppointment.getReference( ) )
1117                 .replace( MARK_DATE_APP, firstSlot.getStartingDateTime().format( Utilities.getFormatter( ) ) )
1118                 .replace( MARK_TIME_BEGIN, strTimeBegin ).replace( MARK_TIME_END, strTimeEnd) );
1119         Map<String, Object> model = new HashMap<>( );
1120         model.put( MARK_LIST_RESPONSE_RECAP_DTO, AppointmentUtilities.buildListResponse( _validatedAppointment, request, getLocale( request ) ) );
1121         model.put( MARK_DATE_APPOINTMENT, firstSlot.getDate().format( Utilities.getFormatter( ) ) );
1122         model.put( MARK_STARTING_TIME_APPOINTMENT, firstSlot.getStartingTime( ) );
1123         model.put( MARK_ENDING_TIME_APPOINTMENT, lastSlot.getEndingTime( ) );
1124         model.put( MARK_USER,  _validatedAppointment.getUser( ) );
1125         model.put( MARK_PLACES, _validatedAppointment.getNbPlaces( ) );
1126         model.put( MARK_FORM, form );
1127         model.put( MARK_FORM_MESSAGES, formMessages );
1128         _appointmentForm = null;
1129         _validatedAppointment = null;
1130         return getXPage( TEMPLATE_APPOINTMENT_CREATED, getLocale( request ), model );
1131     }
1132 
1133     /**
1134      * Return to the display recap view with the new date selected on the calendar
1135      * 
1136      * @param request
1137      *            the request
1138      * @return to the display recap view
1139      * @throws AccessDeniedException
1140      * @throws SiteMessageException
1141      */
1142 
1143     @View( VIEW_CHANGE_DATE_APPOINTMENT )
1144     public synchronized XPage getViewChangeDateAppointment( HttpServletRequest request )
1145     {
1146         String strIdForm = request.getParameter( PARAMETER_ID_FORM );
1147         LocalDateTime startingDateTime = LocalDateTime.parse( request.getParameter( PARAMETER_STARTING_DATE_TIME ) );
1148         Locale locale = getLocale( request );
1149         int nIdForm = Integer.parseInt( strIdForm );
1150         Form form = FormService.findFormLightByPrimaryKey( nIdForm );
1151 
1152         if ( !form.getIsActive( ) || _validatedAppointment.getStartingDateTime( ).isBefore( LocalDateTime.now( ) )
1153                 || !isAuthorizedDate( startingDateTime, locale ) )
1154         {
1155             _validatedAppointment = null;
1156             addError( ERROR_MESSAGE_FORM_NOT_ACTIVE, getLocale( request ) );
1157             return redirect( request, VIEW_APPOINTMENT_CALENDAR, PARAMETER_ID_FORM, form.getIdForm( ), PARAMETER_NB_PLACE_TO_TAKE, _nNbPlacesToTake );
1158         }
1159         int nNbConsecutiveSlot = ( _nNbPlacesToTake == 0 ) ? 1 : _nNbPlacesToTake;
1160         List<WeekDefinition> listWeekDefinition = WeekDefinitionService.findListWeekDefinition( nIdForm );
1161         Map<WeekDefinition, ReservationRule> mapReservationRule = ReservationRuleService.findAllReservationRule( nIdForm, listWeekDefinition );
1162         List<Slot> listSlot = SlotService.buildListSlot( nIdForm, mapReservationRule, startingDateTime.toLocalDate( ), startingDateTime.toLocalDate( ) );
1163         listSlot = listSlot.stream( )
1164                 .filter( s -> ( ( startingDateTime.compareTo( s.getStartingDateTime( ) ) <= 0 ) && ( s.getNbRemainingPlaces( ) > 0 ) && ( s.getIsOpen( ) ) ) )
1165                 .limit( nNbConsecutiveSlot ).collect( Collectors.toList( ) );
1166 
1167         if ( listSlot == null || ( _nNbPlacesToTake > 0 && listSlot.size( ) != _nNbPlacesToTake ) || !AppointmentUtilities.isConsecutiveSlots( listSlot ) )
1168         {
1169             addError( ERROR_MESSAGE_SLOT_FULL, getLocale( request ) );
1170             return redirect( request, VIEW_APPOINTMENT_CALENDAR, PARAMETER_ID_FORM, nIdForm, PARAMETER_NB_PLACE_TO_TAKE, _nNbPlacesToTake );
1171         }
1172 
1173         boolean bool = true;
1174         _validatedAppointment.setSlot( null );
1175         _validatedAppointment.setNbMaxPotentialBookedSeats( 0 );
1176         for ( Slot slot : listSlot )
1177         {
1178 
1179             if ( slot.getIdSlot( ) == 0 )
1180             {
1181 
1182                 slot = SlotSafeService.createSlot( slot );
1183 
1184             }
1185             else
1186             {
1187 
1188                 slot = SlotService.findSlotById( slot.getIdSlot( ) );
1189             }
1190 
1191             // Need to check competitive access
1192             // May be the slot is already taken at the same time
1193             if ( slot.getNbPotentialRemainingPlaces( ) <= 0 )
1194             {
1195                 addError( ERROR_MESSAGE_SLOT_FULL, locale );
1196                 return redirect( request, VIEW_APPOINTMENT_CALENDAR, PARAMETER_ID_FORM, nIdForm, PARAMETER_NB_PLACE_TO_TAKE, _nNbPlacesToTake );
1197             }
1198 
1199             _validatedAppointment.addSlot( slot );
1200 
1201             if ( bool )
1202             {
1203                 _validatedAppointment.setDateOfTheAppointment( slot.getDate( ).format( Utilities.getFormatter( ) ) );
1204                 ReservationRule reservationRule = ReservationRuleService.findReservationRuleByIdFormAndClosestToDateOfApply( nIdForm, slot.getDate( ) );
1205                 _appointmentForm = FormService.buildAppointmentForm( nIdForm, reservationRule );
1206                 bool = false;
1207             }
1208             AppointmentUtilities.putTimerInSession( request, slot.getIdSlot( ), _validatedAppointment, _appointmentForm.getMaxPeoplePerAppointment( ) );
1209         }
1210 
1211         if ( _validatedAppointment.getNbMaxPotentialBookedSeats( ) == 0 )
1212         {
1213             addError( ERROR_MESSAGE_SLOT_FULL, locale );
1214             return redirect( request, VIEW_APPOINTMENT_CALENDAR, PARAMETER_ID_FORM, nIdForm, PARAMETER_NB_PLACE_TO_TAKE, _nNbPlacesToTake );
1215         }
1216 
1217         for ( Response response : _validatedAppointment.getListResponse( ) )
1218         {
1219             if ( response.getFile( ) != null )
1220             {
1221                 IFileStoreServiceProvider fileStoreService = FileService.getInstance( ).getFileStoreServiceProvider( );
1222                 File file = fileStoreService.getFile( response.getFile( ).getFileKey( ) );
1223                 response.setFile( file );
1224             }
1225         }
1226 
1227         Map<String, String> additionalParameters = new HashMap<>( );
1228         additionalParameters.put( PARAMETER_MODIF_DATE, "true" );
1229         additionalParameters.put( PARAMETER_ID_FORM, Integer.toString( nIdForm ) );
1230         return redirect( request, VIEW_DISPLAY_RECAP_APPOINTMENT, additionalParameters );
1231     }
1232 
1233     /**
1234      * Get the view of all the forms on front office side
1235      * 
1236      * @param request
1237      *            the request
1238      * @return the xpage
1239      */
1240     @View( value = VIEW_APPOINTMENT_FORM_LIST, defaultView = true )
1241     public synchronized XPage getFormList( HttpServletRequest request )
1242     {
1243         Locale locale = getLocale( request );
1244         _appointmentForm = null;
1245         _validatedAppointment = null;
1246         String strHtmlContent = getFormListHtml( locale, getModel( ) );
1247 
1248         XPage xPage = getXPage( );
1249         xPage.setContent( strHtmlContent );
1250         return xPage;
1251     }
1252 
1253     /**
1254      * Get the view for the user who wants to cancel its appointment
1255      * 
1256      * @param request
1257      * @return the view
1258      */
1259     @View( VIEW_GET_VIEW_CANCEL_APPOINTMENT )
1260     public synchronized XPage getViewCancelAppointment( HttpServletRequest request )
1261     {
1262         String refAppointment = request.getParameter( PARAMETER_REF_APPOINTMENT );
1263         Appointment appointment = null;
1264         if ( StringUtils.isNotEmpty( refAppointment ) )
1265         {
1266             appointment = AppointmentService.findAppointmentByReference( refAppointment );
1267         }
1268         Map<String, Object> model = new HashMap<>( );
1269         model.put( PARAMETER_REF_APPOINTMENT, refAppointment );
1270         if ( appointment != null )
1271         {
1272 
1273             if ( appointment.getIsCancelled( ) )
1274             {
1275                 model.put( MARK_APPOINTMENT_ALREADY_CANCELLED, Boolean.TRUE );
1276             }
1277 
1278             int nIdAppointment = appointment.getIdAppointment( );
1279             AppointmentDTO appointmentDto = AppointmentService.buildAppointmentDTOFromIdAppointment( nIdAppointment );
1280             // Check if the appointment is passed
1281             if ( appointmentDto.getStartingDateTime( ).isBefore( LocalDateTime.now( ) ) )
1282             {
1283                 model.put( MARK_APPOINTMENT_PASSED, Boolean.TRUE );
1284             }
1285             model.put( MARK_DATE_APPOINTMENT, appointmentDto.getDateOfTheAppointment( ) );
1286             model.put( MARK_STARTING_TIME_APPOINTMENT, appointmentDto.getStartingTime( ) );
1287             model.put( MARK_ENDING_TIME_APPOINTMENT, appointmentDto.getEndingTime( ) );
1288             model.put( MARK_PLACES, appointment.getNbPlaces( ) );
1289             model.put( MARK_FORM, FormService.buildAppointmentForm( appointmentDto.getIdForm( ), 0 ) );
1290             model.put( MARK_FORM_MESSAGES, FormMessageService.findFormMessageByIdForm( appointmentDto.getIdForm( ) ) );
1291             AppointmentDTO appointmentDTO = AppointmentService.buildAppointmentDTOFromIdAppointment( nIdAppointment );
1292             appointmentDTO.setListResponse( AppointmentResponseService.findAndBuildListResponse( nIdAppointment, request ) );
1293             appointmentDTO.setMapResponsesByIdEntry( AppointmentResponseService.buildMapFromListResponse( appointmentDTO.getListResponse( ) ) );
1294             model.put( MARK_LIST_RESPONSE_RECAP_DTO, AppointmentUtilities.buildListResponse( appointmentDTO, request, getLocale( request ) ) );
1295             model.put( MARK_USER, UserService.findUserById( appointment.getIdUser( ) ) );
1296 
1297         }
1298         else
1299         {
1300             model.put( MARK_NO_APPOINTMENT_WITH_THIS_REFERENCE, Boolean.TRUE );
1301         }
1302         Locale locale = getLocale( request );
1303         XPage xpage = getXPage( TEMPLATE_CANCEL_APPOINTMENT, locale, model );
1304         xpage.setTitle( I18nService.getLocalizedString( MESSAGE_CANCEL_APPOINTMENT_PAGE_TITLE, locale ) );
1305         return xpage;
1306 
1307     }
1308 
1309     /**
1310      * Cancel an appointment
1311      * 
1312      * @param request
1313      * @return the confirmation view of the appointment cancelled
1314      */
1315     @Action( ACTION_DO_CANCEL_APPOINTMENT )
1316     public synchronized XPage doCancelAppointment( HttpServletRequest request )
1317     {
1318         String strRef = request.getParameter( PARAMETER_REF_APPOINTMENT );
1319         if ( StringUtils.isNotEmpty( strRef ) )
1320         {
1321             Appointment appointment = AppointmentService.findAppointmentByReference( strRef );
1322             AppointmentDTO appointmentDto = AppointmentService.buildAppointmentDTOFromIdAppointment( appointment.getIdAppointment( ) );
1323 
1324             // Accept only one cancel !!!
1325             if ( !appointment.getIsCancelled( ) && !appointmentDto.getStartingDateTime( ).isBefore( LocalDateTime.now( ) ) )
1326             {
1327                 // Check if the appointment is passed
1328                 if ( appointment.getIdActionCancelled( ) > 0 )
1329                 {
1330                     try
1331                     {
1332                         WorkflowService.getInstance( ).doProcessAction( appointment.getIdAppointment( ), Appointment.APPOINTMENT_RESOURCE_TYPE,
1333                                 appointment.getIdActionCancelled( ), appointmentDto.getIdForm( ), request, request.getLocale( ), true, null );
1334                         AppointmentListenerManager.notifyAppointmentWFActionTriggered( appointment.getIdAppointment( ), appointment.getIdActionCancelled( ) );
1335                     }
1336                     catch( Exception e )
1337                     {
1338                         AppLogService.error( "Error Workflow", e );
1339                     }
1340                 }
1341                 else
1342                 {
1343                     appointment.setIsCancelled( Boolean.TRUE );
1344                     AppointmentService.updateAppointment( appointment );
1345                     AppLogService.info( LogUtilities.buildLog( ACTION_DO_CANCEL_APPOINTMENT, Integer.toString( appointment.getIdAppointment( ) ), null ) );
1346                 }
1347                 Map<String, String> mapParameters = new HashMap<>( );
1348                 if ( StringUtils.isNotEmpty( request.getParameter( PARAMETER_FROM_MY_APPOINTMENTS ) ) )
1349                 {
1350                     String strReferer = request.getHeader( PARAMETER_REFERER );
1351                     if ( StringUtils.isNotEmpty( strReferer ) )
1352                     {
1353                         mapParameters.put( MARK_FROM_URL, strReferer );
1354                     }
1355                     mapParameters.put( PARAMETER_FROM_MY_APPOINTMENTS, request.getParameter( PARAMETER_FROM_MY_APPOINTMENTS ) );
1356                 }
1357                 mapParameters.put( PARAMETER_ID_FORM, Integer.toString( appointmentDto.getIdForm( ) ) );
1358                 return redirect( request, VIEW_APPOINTMENT_CANCELED, mapParameters );
1359             }
1360         }
1361         return redirectView( request, VIEW_APPOINTMENT_FORM_LIST );
1362     }
1363 
1364     /**
1365      * Get the page to confirm that the appointment has been canceled
1366      * 
1367      * @param request
1368      *            The request
1369      * @return The XPage to display
1370      */
1371     @View( VIEW_APPOINTMENT_CANCELED )
1372     public synchronized XPage getAppointmentCanceled( HttpServletRequest request )
1373     {
1374         String strIdForm = request.getParameter( PARAMETER_ID_FORM );
1375         if ( StringUtils.isNotEmpty( strIdForm ) && StringUtils.isNumeric( strIdForm ) )
1376         {
1377             int nIdForm = Integer.parseInt( strIdForm );
1378             Map<String, Object> model = new HashMap<>( );
1379             model.put( MARK_FORM_MESSAGES, FormMessageService.findFormMessageByIdForm( nIdForm ) );
1380             if ( Boolean.parseBoolean( request.getParameter( PARAMETER_FROM_MY_APPOINTMENTS ) ) )
1381             {
1382                 String strFromUrl = request.getParameter( MARK_FROM_URL );
1383                 model.put( MARK_BACK_URL, StringUtils.isNotEmpty( strFromUrl ) ? strFromUrl : getViewUrl( VIEW_GET_MY_APPOINTMENTS ) );
1384             }
1385             return getXPage( TEMPLATE_APPOINTMENT_CANCELED, getLocale( request ), model );
1386         }
1387         return redirectView( request, VIEW_APPOINTMENT_FORM_LIST );
1388     }
1389 
1390     /**
1391      * Get the page to view the appointments of a user
1392      * 
1393      * @param request
1394      *            The request
1395      * @return The XPage to display
1396      * @throws UserNotSignedException
1397      *             If the authentication is enabled and the user has not signed in
1398      */
1399     @View( VIEW_GET_MY_APPOINTMENTS )
1400     public synchronized XPage getMyAppointments( HttpServletRequest request ) throws UserNotSignedException
1401     {
1402         if ( !SecurityService.isAuthenticationEnable( ) )
1403         {
1404             return redirectView( request, VIEW_APPOINTMENT_FORM_LIST );
1405         }
1406         Locale locale = getLocale( request );
1407         XPage xPage = getXPage( );
1408         xPage.setContent( getMyAppointmentsHtml( request, locale, getModel( ) ) );
1409         xPage.setTitle( I18nService.getLocalizedString( MESSAGE_MY_APPOINTMENTS_PAGE_TITLE, locale ) );
1410         return xPage;
1411     }
1412 
1413     /**
1414      * Get the HTML content of the my appointment page of a user
1415      * 
1416      * @param request
1417      *            The request
1418      * @param locale
1419      *            The locale
1420      * @return The HTML content, or null if the
1421      * @throws UserNotSignedException
1422      *             If the user has not signed in
1423      * @deprecated use {@link #getMyAppointmentsHtml(HttpServletRequest, Locale, Map)}
1424      */
1425     @Deprecated
1426     public static String getMyAppointmentsXPage( HttpServletRequest request, Locale locale, Map<String, Object> model ) throws UserNotSignedException
1427     {
1428         if ( !SecurityService.isAuthenticationEnable( ) )
1429         {
1430             return null;
1431         }
1432         LuteceUser luteceUser = SecurityService.getInstance( ).getRegisteredUser( request );
1433         if ( luteceUser == null )
1434         {
1435             throw new UserNotSignedException( );
1436         }
1437         AppointmentFilterDTOointmentFilterDTO.html#AppointmentFilterDTO">AppointmentFilterDTO appointmentFilter = new AppointmentFilterDTO( );
1438         appointmentFilter.setGuid( luteceUser.getName( ) );
1439         List<AppointmentDTO> listAppointmentDTO = AppointmentService.findListAppointmentsDTOByFilter( appointmentFilter );
1440         for ( AppointmentDTO apptDto : listAppointmentDTO )
1441         {
1442             Form form = FormService.findFormLightByPrimaryKey( apptDto.getIdForm( ) );
1443             if ( form.getIdWorkflow( ) > 0 && WorkflowService.getInstance( ).isAvailable( ) )
1444             {
1445 
1446                 apptDto.setListWorkflowActions( WorkflowService.getInstance( ).getActions( apptDto.getIdAppointment( ), Appointment.APPOINTMENT_RESOURCE_TYPE,
1447                         form.getIdWorkflow( ), luteceUser ) );
1448 
1449             }
1450         }
1451 
1452         model = ( model == null ) ? new HashMap<>( ) : model;
1453         model.put( MARK_LIST_APPOINTMENTS, listAppointmentDTO );
1454         model.put( MARK_FORM_LIST, FormService.findAllInReferenceList( ) );
1455         model.put( MARK_LOCALE_DATE_TIME, LocalDateTime.now( ) );
1456         HtmlTemplate template = AppTemplateService.getTemplate( TEMPLATE_MY_APPOINTMENTS, locale, model );
1457         return template.getHtml( );
1458     }
1459 
1460     /**
1461      * Get the HTML content of a user's "My appointments" page
1462      * 
1463      * @param request
1464      *            The request
1465      * @param locale
1466      *            The locale
1467      * @return The HTML content, or null if the user is not logged in
1468      * @throws UserNotSignedException
1469      *             If the user has not signed in
1470      */
1471     public static String getMyAppointmentsHtml( HttpServletRequest request, Locale locale, Map<String, Object> model ) throws UserNotSignedException
1472     {
1473         if ( !SecurityService.isAuthenticationEnable( ) )
1474         {
1475             return null;
1476         }
1477         LuteceUser luteceUser = SecurityService.getInstance( ).getRegisteredUser( request );
1478         if ( luteceUser != null )
1479         {
1480             throw new UserNotSignedException( );
1481         }
1482         AppointmentFilterDTOointmentFilterDTO.html#AppointmentFilterDTO">AppointmentFilterDTO appointmentFilter = new AppointmentFilterDTO( );
1483         appointmentFilter.setGuid( luteceUser.getName( ) );
1484         List<AppointmentDTO> listAppointmentDTO = AppointmentService.findListAppointmentsDTOByFilter( appointmentFilter );
1485         for ( AppointmentDTO apptDto : listAppointmentDTO )
1486         {
1487             Form form = FormService.findFormLightByPrimaryKey( apptDto.getIdForm( ) );
1488             if ( form.getIdWorkflow( ) > 0 && WorkflowService.getInstance( ).isAvailable( ) )
1489             {
1490                 apptDto.setListWorkflowActions( WorkflowService.getInstance( ).getActions( apptDto.getIdAppointment( ), Appointment.APPOINTMENT_RESOURCE_TYPE,
1491                         form.getIdWorkflow( ), luteceUser ) );
1492             }
1493         }
1494 
1495         model = ( model == null ) ? new HashMap<>( ) : model;
1496         model.put( MARK_LIST_APPOINTMENTS, listAppointmentDTO );
1497         model.put( MARK_FORM_LIST, FormService.findAllInReferenceList( ) );
1498         model.put( MARK_LOCALE_DATE_TIME, LocalDateTime.now( ) );
1499         HtmlTemplate template = AppTemplateService.getTemplate( TEMPLATE_MY_APPOINTMENTS, locale, model );
1500         return template.getHtml( );
1501     }
1502 
1503     /**
1504      * Get the html content of the list of forms
1505      * 
1506      * @param appointmentFormService
1507      *            The service to use
1508      * @param strTitle
1509      *            The title to display, or null to display the default title.
1510      * @param locale
1511      *            The locale
1512      * @return The HTML content to display
1513      */
1514     public static String getFormListHtml( Locale locale, Map<String, Object> model )
1515     {
1516         model = ( model == null ) ? new HashMap<>( ) : model;
1517         List<AppointmentFormDTO> listAppointmentForm = FormService.buildAllActiveAndDisplayedOnPortletAppointmentForm( );
1518         // We keep only the active
1519         if ( CollectionUtils.isNotEmpty( listAppointmentForm ) )
1520         {
1521             listAppointmentForm = listAppointmentForm.stream( )
1522                     .filter( a -> ( a.getDateStartValidity( ) != null ) && ( a.getDateStartValidity( ).toLocalDate( ).isBefore( LocalDate.now( ) )
1523                             || a.getDateStartValidity( ).toLocalDate( ).equals( LocalDate.now( ) ) ) )
1524                     .sorted( ( a1, a2 ) -> a1.getTitle( ).compareTo( a2.getTitle( ) ) ).collect( Collectors.toList( ) );
1525         }
1526         List<String> icons = new ArrayList<>( );
1527         for ( AppointmentFormDTO form : listAppointmentForm )
1528         {
1529             ImageResource img = form.getIcon( );
1530             if ( img == null || img.getImage( ) == null || StringUtils.isEmpty( img.getMimeType( ) )
1531                     || StringUtils.equals( img.getMimeType( ), MARK_ICON_NULL ) )
1532             {
1533                 icons.add( MARK_ICON_NULL );
1534             }
1535             else
1536             {
1537                 byte [ ] imgBytesAsBase64 = Base64.encodeBase64( img.getImage( ) );
1538                 String imgDataAsBase64 = new String( imgBytesAsBase64 );
1539                 String strMimeType = img.getMimeType( );
1540                 String imgAsBase64 = MARK_DATA + MARK_COLON + strMimeType + MARK_SEMI_COLON + MARK_BASE_64 + MARK_COMMA + imgDataAsBase64;
1541                 icons.add( imgAsBase64 );
1542             }
1543         }
1544         model.put( MARK_ICONS, icons );
1545         model.put( MARK_FORM_LIST, listAppointmentForm );
1546         HtmlTemplate template = AppTemplateService.getTemplate( TEMPLATE_APPOINTMENT_FORM_LIST, locale, model );
1547         return template.getHtml( );
1548     }
1549 
1550     /**
1551      * 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
1552      * processing page.
1553      * 
1554      * @param request
1555      *            The request
1556      * @return The HTML content to display, or the next URL to redirect the user to
1557      */
1558     @View( VIEW_WORKFLOW_ACTION_FORM )
1559     public synchronized XPage getWorkflowActionForm( HttpServletRequest request ) throws UserNotSignedException
1560     {
1561         String strIdAction = request.getParameter( PARAMETER_ID_ACTION );
1562         String refAppointment = request.getParameter( PARAMETER_REF_APPOINTMENT );
1563         if ( StringUtils.isNotEmpty( strIdAction ) && StringUtils.isNumeric( strIdAction ) && StringUtils.isNotEmpty( refAppointment ) )
1564         {
1565             int nIdAction = Integer.parseInt( strIdAction );
1566             if ( WorkflowService.getInstance( ).isDisplayTasksForm( nIdAction, getLocale( request ) ) )
1567             {
1568                 AppointmentDTO appointment = AppointmentService.buildAppointmentDTOFromRefAppointment( refAppointment );
1569                 // Check if an appointment Object was found and built with the given reference
1570                 if( appointment == null )
1571                 {
1572                 	// When the appointment Object doesn't exist, we display an error message to the user
1573                 	return getXPage( TEMPLATE_ERROR_APPOINTMENT_REFERENCE, getLocale( request ), null );
1574                 }
1575                 ITaskService taskService = SpringContextService.getBean( TaskService.BEAN_SERVICE );
1576                 List<ITask> listActionTasks = taskService.getListTaskByIdAction( nIdAction, getLocale( request ) );
1577                 if ( listActionTasks.stream( ).anyMatch( task -> task.getTaskType( ).getKey( ).equals( "taskReportAppointment" ) ) )
1578                 {
1579                     Map<String, String> additionalParameters = new HashMap<>( );
1580                     additionalParameters.put( PARAMETER_REF_APPOINTMENT, appointment.getReference( ) );
1581                     additionalParameters.put( PARAMETER_NB_PLACE_TO_TAKE, String.valueOf( appointment.getNbBookedSeats( ) ) );
1582                     additionalParameters.put( PARAMETER_ID_FORM, String.valueOf( appointment.getIdForm( ) ) );
1583                     return redirect( request, VIEW_APPOINTMENT_CALENDAR, additionalParameters );
1584                 }
1585                 String strHtmlTasksForm = WorkflowService.getInstance( ).getDisplayTasksForm( appointment.getIdAppointment( ),
1586                         Appointment.APPOINTMENT_RESOURCE_TYPE, nIdAction, request, getLocale( request ), null );
1587                 Map<String, Object> model = getModel( );
1588                 model.put( MARK_TASKS_FORM, strHtmlTasksForm );
1589                 model.put( PARAMETER_ID_ACTION, nIdAction );
1590                 model.put( PARAMETER_REF_APPOINTMENT, refAppointment );
1591 
1592                 return getXPage( TEMPLATE_TASKS_FORM_WORKFLOW, getLocale( request ), model );
1593             }
1594 
1595             return doProcessWorkflowAction( request );
1596         }
1597         return getMyAppointments( request );
1598     }
1599 
1600     /**
1601      * Do process a workflow action over an appointment
1602      * 
1603      * @param request
1604      *            The request
1605      * @return The next URL to redirect to
1606      */
1607     @Action( ACTION_DO_PROCESS_WORKFLOW_ACTION )
1608     public synchronized XPage doProcessWorkflowAction( HttpServletRequest request ) throws UserNotSignedException
1609     {
1610         LuteceUser luteceUser = SecurityService.getInstance( ).getRegisteredUser( request );
1611         String strIdAction = request.getParameter( PARAMETER_ID_ACTION );
1612         String refAppointment = request.getParameter( PARAMETER_REF_APPOINTMENT );
1613         if ( StringUtils.isNotEmpty( strIdAction ) && StringUtils.isNumeric( strIdAction ) && StringUtils.isNotEmpty( refAppointment ) )
1614         {
1615             int nIdAction = Integer.parseInt( strIdAction );
1616             Appointment appointment = AppointmentService.findAppointmentByReference( refAppointment );
1617             // Check if an appointment Object was found and built with the given reference
1618             if( appointment == null )
1619             {
1620             	// When the appointment Object doesn't exist, we display an error message to the user
1621             	return getXPage( TEMPLATE_ERROR_APPOINTMENT_REFERENCE, getLocale( request ), null );
1622             }
1623             int nIdAppointment = appointment.getIdAppointment( );
1624 
1625             List<AppointmentSlot> listApptSlot = appointment.getListAppointmentSlot( );
1626             Slot slot = SlotService.findSlotById( listApptSlot.get( 0 ).getIdSlot( ) );
1627 
1628             if ( request.getParameter( PARAMETER_BACK ) == null )
1629             {
1630                 try
1631                 {
1632                     if ( WorkflowService.getInstance( ).isDisplayTasksForm( nIdAction, getLocale( request ) ) )
1633                     {
1634                         if ( WorkflowService.getInstance( ).canProcessAction( nIdAppointment, Appointment.APPOINTMENT_RESOURCE_TYPE, nIdAction,
1635                                 slot.getIdForm( ), request, false, luteceUser ) )
1636                         {
1637 
1638                             String strError = WorkflowService.getInstance( ).doSaveTasksForm( nIdAppointment, Appointment.APPOINTMENT_RESOURCE_TYPE, nIdAction,
1639                                     slot.getIdForm( ), request, getLocale( request ), luteceUser );
1640                             if ( strError != null )
1641                             {
1642                                 AppLogService.error( "Error Workflow:" + strError );
1643                                 addError( strError );
1644                                 return getWorkflowActionForm( request );
1645                             }
1646                         }
1647                         else
1648                         {
1649 
1650                             AppLogService.error( "Error Workflow can not process Action" );
1651                             addError( "Error Workflow can not process Action" );
1652                             return getMyAppointments( request );
1653                         }
1654                     }
1655                     else
1656                     {
1657                         ITaskService taskService = SpringContextService.getBean( TaskService.BEAN_SERVICE );
1658                         List<ITask> listActionTasks = taskService.getListTaskByIdAction( nIdAction, getLocale( request ) );
1659                         for ( ITask task : listActionTasks )
1660                         {
1661                             if ( task.getTaskType( ).getKey( ).equals( "taskChangeAppointmentStatus" ) && ( appointment.getIsCancelled( ) ) )
1662                             {
1663                                 for ( AppointmentSlot apptSlt : listApptSlot )
1664                                 {
1665 
1666                                     Slot slt = SlotService.findSlotById( apptSlt.getIdSlot( ) );
1667 
1668                                     if ( apptSlt.getNbPlaces( ) > slt.getNbRemainingPlaces( ) )
1669                                     {
1670                                         AppLogService.error( "Error Workflow:" + ERROR_MESSAGE_SLOT_FULL );
1671                                         addError( ERROR_MESSAGE_SLOT_FULL, getLocale( request ) );
1672                                         return getMyAppointments( request );
1673 
1674                                     }
1675                                 }
1676                             }
1677                         }
1678 
1679                         WorkflowService.getInstance( ).doProcessAction( nIdAppointment, Appointment.APPOINTMENT_RESOURCE_TYPE, nIdAction, slot.getIdForm( ),
1680                                 request, getLocale( request ), false, luteceUser );
1681                         AppointmentListenerManager.notifyAppointmentWFActionTriggered( nIdAppointment, nIdAction );
1682 
1683                     }
1684                     addInfo( MESSAGE_WF_ACTION_SUCESS, getLocale( request ) );
1685                 }
1686                 catch( Exception e )
1687                 {
1688                     AppLogService.error( "Error Workflow", e );
1689                 }
1690 
1691             }
1692         }
1693         return getMyAppointments( request );
1694     }
1695     
1696     /**
1697      * Get the captcha security service
1698      * 
1699      * @return The captcha security service
1700      */
1701     private CaptchaSecurityService getCaptchaService( )
1702     {
1703         if ( _captchaSecurityService == null )
1704         {
1705             _captchaSecurityService = new CaptchaSecurityService( );
1706         }
1707 
1708         return _captchaSecurityService;
1709     }
1710 
1711     /**
1712      * Get the URL
1713      * 
1714      * @param request
1715      *            Get the URL to cancel an appointment in FO
1716      * @param appointment
1717      *            The appointment
1718      * @return The URL to cancel the appointment
1719      */
1720     public static String getCancelAppointmentUrl( HttpServletRequest request, Appointment appointment )
1721     {
1722         UrlItem urlItem = new UrlItem( AppPathService.getProdUrl( request ) + AppPathService.getPortalUrl( ) );
1723         urlItem.addParameter( MVCUtils.PARAMETER_PAGE, XPAGE_NAME );
1724         urlItem.addParameter( MVCUtils.PARAMETER_VIEW, VIEW_GET_VIEW_CANCEL_APPOINTMENT );
1725         urlItem.addParameter( PARAMETER_REF_APPOINTMENT, appointment.getReference( ) );
1726         return urlItem.getUrl( );
1727     }
1728 
1729     /**
1730      * Get the URL
1731      * 
1732      * @param request
1733      *            Get the URL to cancel an appointment in FO
1734      * @param appointment
1735      *            The appointment
1736      * @return The URL to cancel the appointment
1737      */
1738     public static String getCancelAppointmentUrl( Appointment appointment )
1739     {
1740         UrlItem urlItem = new UrlItem( AppPathService.getProdUrl( StringUtils.EMPTY ) + AppPathService.getPortalUrl( ) );
1741         urlItem.addParameter( MVCUtils.PARAMETER_PAGE, XPAGE_NAME );
1742         urlItem.addParameter( MVCUtils.PARAMETER_VIEW, VIEW_GET_VIEW_CANCEL_APPOINTMENT );
1743         urlItem.addParameter( PARAMETER_REF_APPOINTMENT, appointment.getReference( ) );
1744         return urlItem.getUrl( );
1745     }
1746 
1747     /**
1748      * Get the URL
1749      * 
1750      * @param request
1751      *            Get the URL to report an appointment in FO
1752      * @param appointment
1753      *            The appointment
1754      * @return The URL to report the appointment
1755      */
1756     public static String getReportAppointmentUrl( AppointmentDTO appointment )
1757     {
1758         UrlItem urlItem = new UrlItem( AppPathService.getProdUrl( StringUtils.EMPTY ) + AppPathService.getPortalUrl( ) );
1759         urlItem.addParameter( MVCUtils.PARAMETER_PAGE, XPAGE_NAME );
1760         urlItem.addParameter( MVCUtils.PARAMETER_VIEW, VIEW_APPOINTMENT_CALENDAR );
1761         urlItem.addParameter( PARAMETER_ID_FORM, appointment.getIdForm( ) );
1762         urlItem.addParameter( PARAMETER_NB_PLACE_TO_TAKE, appointment.getNbBookedSeats( ) );
1763         urlItem.addParameter( PARAMETER_REF_APPOINTMENT, appointment.getReference( ) );
1764         return urlItem.getUrl( );
1765     }
1766 
1767     /**
1768      * Set the user infos to the appointment DTO
1769      * 
1770      * @param request
1771      *            the request
1772      * @param appointment
1773      *            the appointment DTO
1774      */
1775     public void setUserInfo( HttpServletRequest request, AppointmentDTO appointment )
1776     {
1777         if ( SecurityService.isAuthenticationEnable( ) && ( appointment != null ) )
1778         {
1779             LuteceUser user = SecurityService.getInstance( ).getRegisteredUser( request );
1780 
1781             if ( user != null )
1782             {
1783                 appointment.setGuid( user.getName( ) );
1784                 appointment.setFirstName( user.getUserInfo( AppPropertiesService.getProperty( PROPERTY_USER_ATTRIBUTE_FIRST_NAME, StringUtils.EMPTY ) ) );
1785                 appointment.setEmail( user.getUserInfo( AppPropertiesService.getProperty( PROPERTY_USER_ATTRIBUTE_EMAIL, StringUtils.EMPTY ) ) );
1786                 String lastName = user.getUserInfo( AppPropertiesService.getProperty( PROPERTY_USER_ATTRIBUTE_PREFERED_NAME, StringUtils.EMPTY ) );
1787                 if ( ( lastName == null ) || lastName.isEmpty( ) )
1788                 {
1789                     lastName = user.getUserInfo( AppPropertiesService.getProperty( PROPERTY_USER_ATTRIBUTE_LAST_NAME, StringUtils.EMPTY ) );
1790                 }
1791                 appointment.setLastName( lastName );
1792             }
1793         }
1794     }
1795 
1796     /**
1797      * check if authentication
1798      * 
1799      * @param form
1800      *            Form
1801      * @param request
1802      *            HttpServletRequest
1803      * @throws UserNotSignedException
1804      *             exception if the form requires an authentication and the user is not logged
1805      */
1806     private void checkMyLuteceAuthentication( AppointmentFormDTO form, HttpServletRequest request ) throws UserNotSignedException, AccessDeniedException
1807     {
1808         if ( !SecurityService.isAuthenticationEnable( ) )
1809         {
1810             return;
1811         }
1812 
1813         // Try to register the user in case of external authentication
1814         SecurityService securityService = SecurityService.getInstance( );
1815         if ( securityService.isExternalAuthentication( ) )
1816         {
1817             if ( form.getActiveAuthentication( ) )
1818             {
1819                 // The authentication is external
1820                 // Should register the user if it's not already done
1821                 if ( securityService.getRegisteredUser( request ) == null && securityService.getRemoteUser( request ) == null )
1822                 {
1823                     // Authentication is required to access to the portal
1824                     throw new UserNotSignedException( );
1825                 }
1826 
1827                 if ( !Form.ROLE_NONE.equals( form.getRole( ) ) && !securityService.isUserInRole( request, form.getRole( ) ) )
1828                 {
1829                     // User must have the right role
1830                     throw new AccessDeniedException( "Unauthorized" );
1831                 }
1832             }
1833         }
1834         else
1835         {
1836             // If portal authentication is enabled and user is null and the
1837             // requested URL
1838             // is not the login URL, user cannot access to Portal
1839             if ( form.getActiveAuthentication( ) && securityService.getRegisteredUser( request ) == null && !securityService.isLoginUrl( request ) )
1840             {
1841                 // Authentication is required to access to the portal
1842                 throw new UserNotSignedException( );
1843             }
1844         }
1845     }
1846 
1847     /**
1848      * Check if the date are in the validity date display range of the form
1849      * 
1850      * @param date
1851      *            the starting date of slot
1852      * @param locale
1853      *            the locale
1854      * @return true if the starting date of slot is displayed on FO
1855      */
1856     private boolean isAuthorizedDate( LocalDateTime date, Locale locale )
1857     {
1858         // Get the min time from now before a user can take an appointment (in hours)
1859         LocalDateTime startingDateOfDisplay = LocalDateTime.now( ).plusHours( _appointmentForm.getMinTimeBeforeAppointment( ) );
1860         // validity date range of the form
1861         LocalDateTime startingValidityDate = _appointmentForm.getDateStartValidity( ).toLocalDate( ).atStartOfDay( );
1862         if ( startingValidityDate.isAfter( startingDateOfDisplay ) )
1863         {
1864             startingDateOfDisplay = startingValidityDate;
1865         }
1866         // Calculate the ending date of display with the nb weeks to display since today
1867         // We calculate the number of weeks including the current week, so it and will end to the (n) next sunday
1868         LocalDate endingDateOfDisplay = startingDateOfDisplay.toLocalDate( ).with( WeekFields.of( locale ).dayOfWeek( ), DayOfWeek.SUNDAY.getValue( ) )
1869                 .plusWeeks( (long) _appointmentForm.getNbWeeksToDisplay( ) - 1 );
1870         return !( date.toLocalDate( ).isAfter( endingDateOfDisplay ) || date.isBefore( startingDateOfDisplay ) );
1871 
1872     }
1873 }