View Javadoc
1   /*
2    * Copyright (c) 2002-2018, Mairie de Paris
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met:
8    *
9    *  1. Redistributions of source code must retain the above copyright notice
10   *     and the following disclaimer.
11   *
12   *  2. Redistributions in binary form must reproduce the above copyright notice
13   *     and the following disclaimer in the documentation and/or other materials
14   *     provided with the distribution.
15   *
16   *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
17   *     contributors may be used to endorse or promote products derived from
18   *     this software without specific prior written permission.
19   *
20   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
24   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30   * POSSIBILITY OF SUCH DAMAGE.
31   *
32   * License 1.0
33   */
34  package fr.paris.lutece.plugins.appointment.web;
35  
36  import java.io.UnsupportedEncodingException;
37  import java.net.URLEncoder;
38  import java.text.ParseException;
39  import java.text.SimpleDateFormat;
40  import java.time.DayOfWeek;
41  import java.time.LocalDate;
42  import java.time.LocalDateTime;
43  import java.time.LocalTime;
44  import java.time.temporal.TemporalField;
45  import java.time.temporal.WeekFields;
46  import java.util.*;
47  import java.util.concurrent.TimeUnit;
48  import java.util.stream.Collectors;
49  
50  import javax.servlet.http.HttpServletRequest;
51  
52  import fr.paris.lutece.plugins.appointment.business.AppointmentFillDTO;
53  import fr.paris.lutece.plugins.genericattributes.business.Response;
54  import org.apache.commons.codec.binary.Base64;
55  import org.apache.commons.collections.CollectionUtils;
56  import org.apache.commons.lang.StringEscapeUtils;
57  import org.apache.commons.lang.StringUtils;
58  import org.apache.commons.lang3.CharEncoding;
59  import org.apache.commons.lang3.math.NumberUtils;
60  import org.bouncycastle.util.Strings;
61  
62  import fr.paris.lutece.plugins.appointment.business.appointment.Appointment;
63  import fr.paris.lutece.plugins.appointment.business.calendar.CalendarTemplate;
64  import fr.paris.lutece.plugins.appointment.business.calendar.CalendarTemplateHome;
65  import fr.paris.lutece.plugins.appointment.business.display.Display;
66  import fr.paris.lutece.plugins.appointment.business.form.Form;
67  import fr.paris.lutece.plugins.appointment.business.message.FormMessage;
68  import fr.paris.lutece.plugins.appointment.business.planning.WeekDefinition;
69  import fr.paris.lutece.plugins.appointment.business.rule.FormRule;
70  import fr.paris.lutece.plugins.appointment.business.rule.ReservationRule;
71  import fr.paris.lutece.plugins.appointment.business.slot.Period;
72  import fr.paris.lutece.plugins.appointment.business.slot.Slot;
73  import fr.paris.lutece.plugins.appointment.log.LogUtilities;
74  import fr.paris.lutece.plugins.appointment.service.AppointmentResponseService;
75  import fr.paris.lutece.plugins.appointment.service.AppointmentService;
76  import fr.paris.lutece.plugins.appointment.service.AppointmentUtilities;
77  import fr.paris.lutece.plugins.appointment.service.DisplayService;
78  import fr.paris.lutece.plugins.appointment.service.EntryService;
79  import fr.paris.lutece.plugins.appointment.service.FormMessageService;
80  import fr.paris.lutece.plugins.appointment.service.FormRuleService;
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.SlotService;
84  import fr.paris.lutece.plugins.appointment.service.UserService;
85  import fr.paris.lutece.plugins.appointment.service.Utilities;
86  import fr.paris.lutece.plugins.appointment.service.WeekDefinitionService;
87  import fr.paris.lutece.plugins.appointment.service.upload.AppointmentAsynchronousUploadHandler;
88  import fr.paris.lutece.plugins.appointment.web.dto.AppointmentDTO;
89  import fr.paris.lutece.plugins.appointment.web.dto.AppointmentFormDTO;
90  import fr.paris.lutece.plugins.genericattributes.business.Entry;
91  import fr.paris.lutece.plugins.genericattributes.business.GenericAttributeError;
92  import fr.paris.lutece.portal.service.admin.AdminUserService;
93  import fr.paris.lutece.portal.service.captcha.CaptchaSecurityService;
94  import fr.paris.lutece.portal.service.i18n.I18nService;
95  import fr.paris.lutece.portal.service.image.ImageResource;
96  import fr.paris.lutece.portal.service.message.SiteMessageException;
97  import fr.paris.lutece.portal.service.security.LuteceUser;
98  import fr.paris.lutece.portal.service.security.SecurityService;
99  import fr.paris.lutece.portal.service.security.UserNotSignedException;
100 import fr.paris.lutece.portal.service.template.AppTemplateService;
101 import fr.paris.lutece.portal.service.util.AppLogService;
102 import fr.paris.lutece.portal.service.util.AppPathService;
103 import fr.paris.lutece.portal.service.util.AppPropertiesService;
104 import fr.paris.lutece.portal.service.workflow.WorkflowService;
105 import fr.paris.lutece.portal.util.mvc.commons.annotations.Action;
106 import fr.paris.lutece.portal.util.mvc.commons.annotations.View;
107 import fr.paris.lutece.portal.util.mvc.utils.MVCMessage;
108 import fr.paris.lutece.portal.util.mvc.utils.MVCUtils;
109 import fr.paris.lutece.portal.util.mvc.xpage.MVCApplication;
110 import fr.paris.lutece.portal.util.mvc.xpage.annotations.Controller;
111 import fr.paris.lutece.portal.web.xpages.XPage;
112 import fr.paris.lutece.util.ErrorMessage;
113 import fr.paris.lutece.util.html.HtmlTemplate;
114 import fr.paris.lutece.util.url.UrlItem;
115 
116 /**
117  * This class provides a simple implementation of an Appointment XPage (On Front Office)
118  * 
119  * @author Laurent Payen
120  *
121  */
122 @Controller( xpageName = AppointmentApp.XPAGE_NAME, pageTitleI18nKey = AppointmentApp.MESSAGE_DEFAULT_PAGE_TITLE, pagePathI18nKey = AppointmentApp.MESSAGE_DEFAULT_PATH )
123 public class AppointmentApp extends MVCApplication
124 {
125 
126     /**
127      * Default page of XPages of this app
128      */
129     public static final String MESSAGE_DEFAULT_PATH = "appointment.appointment.name";
130 
131     /**
132      * Default page title of XPages of this app
133      */
134     public static final String MESSAGE_DEFAULT_PAGE_TITLE = "appointment.appointmentApp.defaultTitle";
135 
136     /**
137      * The name of the XPage
138      */
139     protected static final String XPAGE_NAME = "appointment";
140 
141     /**
142      * Generated serial version UID
143      */
144     private static final long serialVersionUID = 5741361182728887387L;
145 
146     // Templates
147     private static final String TEMPLATE_APPOINTMENT_FORM_LIST = "/skin/plugins/appointment/appointment_form_list.html";
148     private static final String TEMPLATE_APPOINTMENT_FORM = "/skin/plugins/appointment/appointment_form.html";
149     private static final String TEMPLATE_APPOINTMENT_FORM_RECAP = "/skin/plugins/appointment/appointment_form_recap.html";
150     private static final String TEMPLATE_APPOINTMENT_CREATED = "skin/plugins/appointment/appointment_created.html";
151     private static final String TEMPLATE_CANCEL_APPOINTMENT = "skin/plugins/appointment/cancel_appointment.html";
152     private static final String TEMPLATE_APPOINTMENT_CANCELED = "skin/plugins/appointment/appointment_canceled.html";
153     private static final String TEMPLATE_MY_APPOINTMENTS = "skin/plugins/appointment/my_appointments.html";
154     private static final String TEMPLATE_HTML_CODE_FORM = "skin/plugins/appointment/html_code_form.html";
155     private static final String TEMPLATE_ERRROR_PAGE = "skin/plugins/appointment/error_page.html";
156 
157     // Views
158     public static final String VIEW_APPOINTMENT_FORM = "getViewAppointmentForm";
159     public static final String VIEW_APPOINTMENT_CALENDAR = "getViewAppointmentCalendar";
160     private static final String VIEW_APPOINTMENT_FORM_LIST = "getViewFormList";
161     private static final String VIEW_DISPLAY_RECAP_APPOINTMENT = "displayRecapAppointment";
162     private static final String VIEW_GET_APPOINTMENT_CREATED = "getAppointmentCreated";
163     private static final String VIEW_APPOINTMENT_CANCELED = "getAppointmentCanceled";
164     private static final String VIEW_GET_MY_APPOINTMENTS = "getMyAppointments";
165     private static final String VIEW_GET_VIEW_CANCEL_APPOINTMENT = "getViewCancelAppointment";
166     private static final String VIEW_GET_APPOINTMENT_ERROR = "getViewAppointmenterror";
167 
168     // Actions
169     private static final String ACTION_DO_VALIDATE_FORM = "doValidateForm";
170     private static final String ACTION_DO_MAKE_APPOINTMENT = "doMakeAppointment";
171     private static final String ACTION_DO_CANCEL_APPOINTMENT = "doCancelAppointment";
172     private static final String ACTION_DO_FILL_APPOINTMENT = "doFillAppointment";
173 
174     // Parameters
175     private static final String PARAMETER_STARTING_DATE_TIME = "starting_date_time";
176     private static final String PARAMETER_ENDING_DATE_TIME = "ending_date_time";
177     private static final String PARAMETER_IS_OPEN = "is_open";
178     private static final String PARAMETER_IS_SPECIFIC = "is_specific";
179     private static final String PARAMETER_MAX_CAPACITY = "max_capacity";
180     private static final String PARAMETER_ENDING_DATE_OF_DISPLAY = "ending_date_of_display";
181     private static final String PARAMETER_STR_ENDING_DATE_OF_DISPLAY = "str_ending_date_of_display";
182     private static final String PARAMETER_DATE_OF_DISPLAY = "date_of_display";
183     private static final String PARAMETER_DAY_OF_WEEK = "dow";
184     private static final String PARAMETER_HIDDEN_DAYS = "hidden_days";
185     private static final String PARAMETER_ID_FORM = "id_form";
186     private static final String PARAMETER_EVENTS = "events";
187     private static final String PARAMETER_MIN_DURATION = "min_duration";
188     private static final String PARAMETER_MIN_TIME = "min_time";
189     private static final String PARAMETER_MAX_TIME = "max_time";
190     private static final String PARAMETER_EMAIL = "email";
191     private static final String PARAMETER_EMAIL_CONFIRMATION = "emailConfirm";
192     private static final String PARAMETER_FIRST_NAME = "firstname";
193     private static final String PARAMETER_LAST_NAME = "lastname";
194     private static final String PARAMETER_NUMBER_OF_BOOKED_SEATS = "nbBookedSeats";
195     private static final String PARAMETER_ID_SLOT = "id_slot";
196     private static final String PARAMETER_ID_APPOINTMENT = "id_appointment";
197     private static final String PARAMETER_BACK = "back";
198     private static final String PARAMETER_REF_APPOINTMENT = "refAppointment";
199     private static final String PARAMETER_FROM_MY_APPOINTMENTS = "fromMyappointments";
200     private static final String PARAMETER_REFERER = "referer";
201     private static final String PARAMETER_WEEK_VIEW = "week_view";
202     private static final String PARAMETER_DAY_VIEW = "day_view";
203     private static final String PARAMETER_ANCHOR = "anchor";
204     private static final String PARAMETER_MODIFICATION_FORM = "mod";
205     private static final String PARAMETER_MIN_DATE_OF_OPEN_DAY = "min_date_of_open_day";
206     private static final String PARAMETER_MAX_DATE_OF_OPEN_DAY = "max_date_of_open_day";
207     // Mark
208     private static final String MARK_INFOS = "infos";
209     private static final String MARK_LOCALE = "locale";
210     private static final String MARK_FORM = "form";
211     private static final String MARK_USER = "user";
212     private static final String MARK_FORM_MESSAGES = "formMessages";
213     private static final String MARK_STR_ENTRY = "str_entry";
214     private static final String MARK_APPOINTMENT = "appointment";
215     private static final String MARK_LIST_ERRORS = "listAllErrors";
216     private static final String MARK_PLACES = "nbplaces";
217     private static final String MARK_DATE_APPOINTMENT = "dateAppointment";
218     private static final String MARK_STARTING_TIME_APPOINTMENT = "startingTimeAppointment";
219     private static final String MARK_ENDING_TIME_APPOINTMENT = "endingTimeAppointment";
220     private static final String MARK_FORM_LIST = "form_list";
221     private static final String MARK_FORM_HTML = "form_html";
222     private static final String MARK_FORM_ERRORS = "form_errors";
223     private static final String MARK_FORM_CALENDAR_ERRORS = "formCalendarErrors";
224     private static final String MARK_CAPTCHA = "captcha";
225     private static final String MARK_REF = "%%REF%%";
226     private static final String MARK_DATE_APP = "%%DATE%%";
227     private static final String MARK_TIME_BEGIN = "%%HEURE_DEBUT%%";
228     private static final String MARK_TIME_END = "%%HEURE_FIN%%";
229     private static final String MARK_LIST_APPOINTMENTS = "list_appointments";
230     private static final String MARK_BACK_URL = "backUrl";
231     private static final String MARK_FROM_URL = "fromUrl";
232     private static final String MARK_LIST_RESPONSE_RECAP_DTO = "listResponseRecapDTO";
233     private static final String MARK_DATA = "data";
234     private static final String MARK_BASE_64 = "base64";
235     private static final String MARK_SEMI_COLON = ";";
236     private static final String MARK_COMMA = ",";
237     private static final String MARK_COLON = ":";
238     private static final String MARK_ICONS = "icons";
239     private static final String MARK_ICON_NULL = "NULL";
240     private static final String MARK_ANCHOR = "#";
241     private static final String MARK_APPOINTMENT_ALREADY_CANCELLED = "alreadyCancelled";
242     private static final String MARK_NO_APPOINTMENT_WITH_THIS_REFERENCE = "noAppointmentWithThisReference";
243     private static final String MARK_APPOINTMENT_PASSED = "appointmentPassed";
244     private static final String MARK_ERROR = "error";
245 
246     // Errors
247     private static final String ERROR_MESSAGE_SLOT_FULL = "appointment.message.error.slotFull";
248     private static final String ERROR_MESSAGE_CAPTCHA = "portal.admin.message.wrongCaptcha";
249     private static final String ERROR_MESSAGE_NB_MIN_DAYS_BETWEEN_TWO_APPOINTMENTS = "appointment.validation.appointment.NbMinDaysBetweenTwoAppointments.error";
250     private static final String ERROR_MESSAGE_NB_MAX_APPOINTMENTS_ON_A_PERIOD = "appointment.validation.appointment.NbMaxAppointmentsOnAPeriod.error";
251     private static final String ERROR_MESSAGE_FORM_NOT_ACTIVE = "appointment.validation.appointment.formNotActive";
252     private static final String ERROR_MESSAGE_NO_STARTING_VALIDITY_DATE = "appointment.validation.appointment.noStartingValidityDate";
253     private static final String ERROR_MESSAGE_FORM_NO_MORE_VALID = "appointment.validation.appointment.formNoMoreValid";
254     private static final String ERROR_MESSAGE_NO_AVAILABLE_SLOT = "appointment.validation.appointment.noAvailableSlot";
255     private static final String ERROR_MESSAGE_NB_CRENEAUX_RESTANTS = "appointment.validation.appointment.NbCreneauxRestants.error";
256     private static final String ERROR_MESSAGE_CLE_INCORRECTE = "appointment.message.error.securite";
257     private static final String ERROR_MESSAGE_AMPLITUDE = "appointment.message.error.amplitude";
258     private static final String ERROR_MESSAGE_HORAIRE_FO = "appointment.message.error.horaireFO";
259 
260     // Session keys
261     private static final String SESSION_APPOINTMENT_FORM_ERRORS = "appointment.session.formErrors";
262     private static final String SESSION_NOT_VALIDATED_APPOINTMENT = "appointment.appointmentFormService.notValidatedAppointment";
263     private static final String SESSION_VALIDATED_APPOINTMENT = "appointment.appointmentFormService.validatedAppointment";
264     private static final String SESSION_ATTRIBUTE_APPOINTMENT_FORM = "appointment.session.appointmentForm";
265 
266     // Constants
267     private static final String ATTRIBUTE = "&attribute";
268     private static final String EMAIL = "&email";
269     private static final String EQUALS = "=";
270     private static final String URL_FILL_APPOINTMENT = "Portal.jsp?page=appointmentfilling&id_form=";
271 
272     // Messages
273     private static final String MESSAGE_CANCEL_APPOINTMENT_PAGE_TITLE = "appointment.cancelAppointment.pageTitle";
274     private static final String MESSAGE_MY_APPOINTMENTS_PAGE_TITLE = "appointment.myAppointments.name";
275 
276     // Local variables
277     private transient CaptchaSecurityService _captchaSecurityService;
278 
279     private static final String APPOINTMENT_FILL_DTO = "appointmentFillDto";
280     private static final String APPOINTMENT_ID_SLOT = "appointmentIdSlot";
281 
282     private static final String PROPERTY_ID_KIOSQUE = "appointment.id.kiosque";
283     private static final String PROPERTY_ID_CLE_SECURITE = "appointment.id.cleSecurite";
284     private static final String PROPERTY_ID_DATES = "appointment.id.dates";
285     private static final String PROPERTY_ID_NUMERO = "appointment.id.numero";
286     private static final String PROPERTY_ID_EMAIL = "appointment.id.email";
287     private static final String PROPERTY_AMPLITUDE = "appointment.amplitude";
288     private static final String PROPERTY_AMPLITUDE_ANIMATIONS = "appointment.amplitude.animations";
289     private static final String PROPERTY_HORAIRE_MAX_AM = "appointment.horaireLimiteMaxAM";
290     private static final String PROPERTY_HORAIRE_MIN_PM = "appointment.horaireLimiteMinPM";
291 
292     private int idEntryKiosque = Integer.valueOf(AppPropertiesService.getProperty( PROPERTY_ID_KIOSQUE ));
293     private int idEntryCleSecurite = Integer.valueOf(AppPropertiesService.getProperty( PROPERTY_ID_CLE_SECURITE ));
294     private int idEntryDates = Integer.valueOf(AppPropertiesService.getProperty( PROPERTY_ID_DATES ));
295     private int idEntryNumero = Integer.valueOf(AppPropertiesService.getProperty( PROPERTY_ID_NUMERO ));
296     private int idEntryEmail = Integer.valueOf(AppPropertiesService.getProperty( PROPERTY_ID_EMAIL ));
297     private int amplitude = Integer.valueOf(AppPropertiesService.getProperty( PROPERTY_AMPLITUDE ));
298     private String[] animations = StringUtils.split( AppPropertiesService.getProperty( PROPERTY_AMPLITUDE_ANIMATIONS ), MARK_COMMA );
299     private String horaireMaxAM = AppPropertiesService.getProperty( PROPERTY_HORAIRE_MAX_AM );
300     private String horaireMinPM = AppPropertiesService.getProperty( PROPERTY_HORAIRE_MIN_PM );
301 
302     // Properties
303     private static final String PROPERTY_USER_ATTRIBUTE_FIRST_NAME = "appointment.userAttribute.firstName";
304     private static final String PROPERTY_USER_ATTRIBUTE_LAST_NAME = "appointment.userAttribute.lastName";
305     private static final String PROPERTY_USER_ATTRIBUTE_PREFERED_NAME = "appointment.userAttribute.preferred_username";
306     private static final String PROPERTY_USER_ATTRIBUTE_EMAIL = "appointment.userAttribute.email";
307     private static final String PROPERTY_USER_ATTRIBUTE_GUID = "appointment.userAttribute.guid";
308 
309     private static final String AGENDA_WEEK = "agendaWeek";
310     private static final String BASIC_WEEK = "basicWeek";
311     private static final String AGENDA_DAY = "agendaDay";
312     private static final String BASIC_DAY = "basicDay";
313 
314     private static final String STEP_3 = "step3";
315 
316     private static final String LABEL_MONTAGE = "début du montage";
317     private static final String LABEL_DEMONTAGE = "démontage";
318 
319     /**
320      * Get the calendar view
321      * 
322      * @param request
323      * @return the Xpage
324      */
325     @SuppressWarnings( "unchecked" )
326     @View( VIEW_APPOINTMENT_CALENDAR )
327     public XPage getViewAppointmentCalendar( HttpServletRequest request )
328     {
329 
330         AppointmentFillDTO dto = getAppointmentFillDTO( request );
331 
332         if((null == dto || null == dto.getIdForm( ) || null == dto.getIdFormUser( ) || null == dto.getSecurityKey( )) || !AppointmentService.checkSecurityKey( dto.getIdForm( ), dto.getIdFormUser( ), dto.getSecurityKey( ), idEntryKiosque, idEntryCleSecurite )){
333             return getAppointmentErrorView(request, ERROR_MESSAGE_CLE_INCORRECTE);
334         }
335 
336         Locale locale = getLocale( request );
337         int nIdForm = Integer.parseInt( request.getParameter( PARAMETER_ID_FORM ) );
338         Form form = FormService.findFormLightByPrimaryKey( nIdForm );
339         AppointmentFormDTO appointmentForm = FormService.buildAppointmentForm( nIdForm, 0, 0 );
340         boolean bError = false;
341         if ( !form.getIsActive( ) )
342         {
343             addError( ERROR_MESSAGE_FORM_NOT_ACTIVE, locale );
344             bError = true;
345         }
346         FormMessage formMessages = FormMessageService.findFormMessageByIdForm( nIdForm );
347         // Check if the date of display and the endDateOfDisplay are in the
348         // validity date range of the form
349         LocalDate startingValidityDate = form.getStartingValidityDate( );
350         if ( startingValidityDate == null )
351         {
352             addError( ERROR_MESSAGE_NO_STARTING_VALIDITY_DATE, locale );
353             bError = true;
354         }
355         LocalDate startingDateOfDisplay = LocalDate.now( );
356         if ( startingValidityDate != null && startingValidityDate.isAfter( startingDateOfDisplay ) )
357         {
358             startingDateOfDisplay = startingValidityDate;
359         }
360         Display display = DisplayService.findDisplayWithFormId( nIdForm );
361         // Get the nb weeks to display
362         int nNbWeeksToDisplay = display.getNbWeeksToDisplay( );
363         // Calculate the ending date of display with the nb weeks to display
364         // since today
365         // We calculate the number of weeks including the current week, so it
366         // will end to the (n) next sunday
367         TemporalField fieldISO = WeekFields.of( locale ).dayOfWeek( );
368         LocalDate dateOfSunday = startingDateOfDisplay.with( fieldISO, DayOfWeek.SUNDAY.getValue( ) );
369         LocalDate endingDateOfDisplay = dateOfSunday.plusWeeks( nNbWeeksToDisplay - 1 );
370         // if the ending date of display is after the ending validity date of
371         // the form
372         // assign the ending date of display with the ending validity date of
373         // the form
374         LocalDate endingValidityDate = form.getEndingValidityDate( );
375         if ( endingValidityDate != null )
376         {
377             if ( endingDateOfDisplay.isAfter( endingValidityDate ) )
378             {
379                 endingDateOfDisplay = endingValidityDate;
380             }
381             if ( startingDateOfDisplay.isAfter( endingDateOfDisplay ) )
382             {
383                 addError( ERROR_MESSAGE_FORM_NO_MORE_VALID, locale );
384                 bError = true;
385             }
386         }
387         // Get the current date of display of the calendar, if it exists
388         String strDateOfDisplay = request.getParameter( PARAMETER_DATE_OF_DISPLAY );
389         LocalDate dateOfDisplay = startingDateOfDisplay;
390         if ( StringUtils.isNotEmpty( strDateOfDisplay ) )
391         {
392             dateOfDisplay = LocalDate.parse( strDateOfDisplay );
393         }
394         // Get all the week definitions
395         HashMap<LocalDate, WeekDefinition> mapWeekDefinition = WeekDefinitionService.findAllWeekDefinition( nIdForm );
396         List<WeekDefinition> listWeekDefinition = new ArrayList<>( mapWeekDefinition.values( ) );
397         // Filter on the list of weekdefinition on the starting date and the
398         // ending date of display
399         if ( listWeekDefinition.size( ) > 1 )
400         {
401             WeekDefinition weekDefinitionClosest = WeekDefinitionService.findWeekDefinitionByIdFormAndClosestToDateOfApply( nIdForm, startingDateOfDisplay );
402             LocalDate dateOfClosestWeekDefinition = weekDefinitionClosest.getDateOfApply( );
403             LocalDate maxEndingDateOfWeekDefinition = endingDateOfDisplay;
404             listWeekDefinition = listWeekDefinition
405                     .stream( )
406                     .filter(
407                             w -> ( w.getDateOfApply( ).isEqual( dateOfClosestWeekDefinition ) || w.getDateOfApply( ).isAfter( dateOfClosestWeekDefinition ) )
408                                     && ( w.getDateOfApply( ).isBefore( maxEndingDateOfWeekDefinition ) || w.getDateOfApply( ).isEqual(
409                                             maxEndingDateOfWeekDefinition ) ) ).collect( Collectors.toList( ) );
410         }
411         // Get the min time of all the week definitions
412         LocalTime minStartingTime = WeekDefinitionService.getMinStartingTimeOfAListOfWeekDefinition( listWeekDefinition );
413         // Get the max time of all the week definitions
414         LocalTime maxEndingTime = WeekDefinitionService.getMaxEndingTimeOfAListOfWeekDefinition( listWeekDefinition );
415         // Get all the working days of all the week definitions
416         List<String> listStrBase0OpenDaysOfWeek = new ArrayList<>(
417                 WeekDefinitionService.getSetDaysOfWeekOfAListOfWeekDefinitionForFullCalendar( listWeekDefinition ) );
418         // Build the slots if no errors
419         List<Slot> listSlots = new ArrayList<>( );
420         if ( !bError )
421         {
422             listSlots = SlotService.buildListSlot( nIdForm, mapWeekDefinition, startingDateOfDisplay, endingDateOfDisplay );
423             // Get the min time from now before a user can take an appointment
424             // (in hours)
425             FormRule formRule = FormRuleService.findFormRuleWithFormId( nIdForm );
426             int minTimeBeforeAppointment = formRule.getMinTimeBeforeAppointment( );
427             LocalDateTime dateTimeBeforeAppointment = LocalDateTime.now( ).plusHours( minTimeBeforeAppointment );
428             // Filter the list of slots
429             if ( CollectionUtils.isNotEmpty( listSlots ) )
430             {
431                 listSlots = listSlots.stream( ).filter( s -> s.getStartingDateTime( ).isAfter( dateTimeBeforeAppointment ) ).collect( Collectors.toList( ) );
432             }
433             LocalDate firstDateOfFreeOpenSlot = null;
434             if ( CollectionUtils.isNotEmpty( listSlots ) )
435             {
436                 // Need to find the first available slot from now (with time)
437                 List<Slot> listAvailableSlots = listSlots.stream( ).filter( s -> ( s.getNbPotentialRemainingPlaces( ) > 0 && s.getIsOpen( ) == Boolean.TRUE ) )
438                         .collect( Collectors.toList( ) );
439                 if ( CollectionUtils.isNotEmpty( listAvailableSlots ) )
440                 {
441                     firstDateOfFreeOpenSlot = listAvailableSlots.stream( ).min( ( s1, s2 ) -> s1.getStartingDateTime( ).compareTo( s2.getStartingDateTime( ) ) )
442                             .get( ).getDate( );
443                 }
444             }
445             if ( firstDateOfFreeOpenSlot == null )
446             {
447                 addError( ERROR_MESSAGE_NO_AVAILABLE_SLOT, locale );
448                 bError = true;
449             }
450             // Display the week with the first available slot
451             if ( firstDateOfFreeOpenSlot != null && firstDateOfFreeOpenSlot.isAfter( dateOfDisplay ) )
452             {
453                 dateOfDisplay = firstDateOfFreeOpenSlot;
454             }
455         }
456         Map<String, Object> model = getModel( );
457         if ( bError )
458         {
459             model.put( MARK_FORM_CALENDAR_ERRORS, bError );
460         }
461         if ( formMessages != null && StringUtils.isNotEmpty( formMessages.getCalendarDescription( ) ) )
462         {
463             List<ErrorMessage> listInfos = (List<ErrorMessage>) model.get( MARK_INFOS );
464             if ( listInfos == null )
465             {
466                 listInfos = new ArrayList<ErrorMessage>( );
467                 model.put( MARK_INFOS, listInfos );
468             }
469             MVCMessage message = new MVCMessage( formMessages.getCalendarDescription( ) );
470             listInfos.add( message );
471         }
472 
473         // Get the min and max date of the open days (for the week navigation on
474         // open days calendar templates)
475         HashSet<Integer> setOpenDays = WeekDefinitionService.getOpenDaysOfWeek( listWeekDefinition );
476         LocalDate minDateOfOpenDay = LocalDate.now( ).with( DayOfWeek.of( setOpenDays.stream( ).min( Comparator.naturalOrder( ) ).get( ) ) );
477         LocalDate maxDateOfOpenDay = endingDateOfDisplay.with( DayOfWeek.of( setOpenDays.stream( ).max( Comparator.naturalOrder( ) ).get( ) ) );
478         model.put( PARAMETER_MIN_DATE_OF_OPEN_DAY, minDateOfOpenDay );
479         model.put( PARAMETER_MAX_DATE_OF_OPEN_DAY, maxDateOfOpenDay );
480         model.put( MARK_FORM, appointmentForm );
481         model.put( PARAMETER_ID_FORM, nIdForm );
482         model.put( MARK_FORM_MESSAGES, formMessages );
483         model.put( PARAMETER_ENDING_DATE_OF_DISPLAY, endingDateOfDisplay );
484         model.put( PARAMETER_STR_ENDING_DATE_OF_DISPLAY, endingDateOfDisplay.format( Utilities.getFormatter( ) ) );
485         model.put( PARAMETER_DATE_OF_DISPLAY, dateOfDisplay );
486         model.put( PARAMETER_DAY_OF_WEEK, listStrBase0OpenDaysOfWeek );
487         model.put( PARAMETER_MIN_TIME, AppointmentUtilities.getMinTimeToDisplay( minStartingTime ) );
488         model.put( PARAMETER_MAX_TIME, AppointmentUtilities.getMaxTimeToDisplay( maxEndingTime ) );
489         model.put( PARAMETER_MIN_DURATION, LocalTime.MIN.plusMinutes( AppointmentUtilities.THIRTY_MINUTES ) );
490         CalendarTemplate calendarTemplate = CalendarTemplateHome.findByPrimaryKey( display.getIdCalendarTemplate( ) );
491         List<String> listHiddenDays = new ArrayList<>( );
492         String dayView = AGENDA_DAY;
493         String weekView = AGENDA_WEEK;
494         for ( int i = 0; i < 7; i++ )
495         {
496             listHiddenDays.add( Integer.toString( i ) );
497         }
498         /**
499          * Calculate the hidden days and set the view (Day and week) with the type of calendar
500          */
501         switch( calendarTemplate.getTitle( ) )
502         {
503             case CalendarTemplate.CALENDAR:
504                 listHiddenDays.clear( );
505                 dayView = AGENDA_DAY;
506                 weekView = AGENDA_WEEK;
507                 break;
508             case CalendarTemplate.CALENDAR_OPEN_DAYS:
509                 // update the list of the days to hide
510                 listHiddenDays.removeAll( listStrBase0OpenDaysOfWeek );
511                 dayView = AGENDA_DAY;
512                 weekView = AGENDA_WEEK;
513                 break;
514             case CalendarTemplate.FREE_SLOTS:
515                 // Keep only the available slots
516                 listSlots = listSlots.stream( ).filter( s -> ( ( s.getNbRemainingPlaces( ) > 0 ) && ( s.getIsOpen( ) ) ) ).collect( Collectors.toList( ) );
517                 listHiddenDays.clear( );
518                 dayView = BASIC_DAY;
519                 weekView = BASIC_WEEK;
520                 break;
521             case CalendarTemplate.FREE_SLOTS_ON_OPEN_DAYS:
522                 // Keep only the available slots
523                 listSlots = listSlots.stream( ).filter( s -> ( ( s.getNbRemainingPlaces( ) > 0 ) && ( s.getIsOpen( ) ) ) ).collect( Collectors.toList( ) );
524                 // update the list of the days to hide
525                 listHiddenDays.removeAll( listStrBase0OpenDaysOfWeek );
526                 dayView = BASIC_DAY;
527                 weekView = BASIC_WEEK;
528                 break;
529             default:
530                 listHiddenDays.clear( );
531                 dayView = AGENDA_DAY;
532                 weekView = AGENDA_WEEK;
533                 break;
534         }
535         model.put( PARAMETER_EVENTS, listSlots );
536         model.put( PARAMETER_HIDDEN_DAYS, listHiddenDays );
537         model.put( PARAMETER_DAY_VIEW, dayView );
538         model.put( PARAMETER_WEEK_VIEW, weekView );
539         HtmlTemplate template = AppTemplateService.getTemplate( calendarTemplate.getTemplatePath( ), locale, model );
540         XPage xpage = new XPage( );
541         xpage.setContent( template.getHtml( ) );
542         xpage.setPathLabel(
543 
544         getDefaultPagePath( locale ) );
545         xpage.setTitle( getDefaultPageTitle( locale ) );
546         return xpage;
547     }
548 
549     /**
550      * Get the form appointment view (front office)
551      * 
552      * @param request
553      *            the request
554      * @return the xpage
555      * @throws UserNotSignedException
556      */
557     @SuppressWarnings( "unchecked" )
558     @View( VIEW_APPOINTMENT_FORM )
559     public XPage getViewAppointmentForm( HttpServletRequest request ) throws UserNotSignedException
560     {
561         AppointmentFillDTO dto = getAppointmentFillDTO( request );
562 
563         if((null == dto || null == dto.getIdForm( ) || null == dto.getIdFormUser( ) || null == dto.getSecurityKey( )) || !AppointmentService.checkSecurityKey( dto.getIdForm( ), dto.getIdFormUser( ), dto.getSecurityKey( ), idEntryKiosque, idEntryCleSecurite )){
564             return getAppointmentErrorView(request, ERROR_MESSAGE_CLE_INCORRECTE);
565         }
566 
567         // Vérification du nombre de créneaux restants pour le projet
568         if(!AppointmentService.checkNbCreneauxRestants(dto.getIdForm( ), dto.getIdFormUser( ), idEntryDates, idEntryNumero)){
569             return getAppointmentErrorView(request, ERROR_MESSAGE_NB_CRENEAUX_RESTANTS);
570         }
571 
572         AppointmentFormDTO form = (AppointmentFormDTO) request.getSession( ).getAttribute( SESSION_ATTRIBUTE_APPOINTMENT_FORM );
573         String strIdForm = request.getParameter( PARAMETER_ID_FORM );
574         int nIdForm = Integer.parseInt( strIdForm );
575         if ( form == null )
576         {
577             form = FormService.buildAppointmentForm( nIdForm, 0, 0 );
578         }
579         checkMyLuteceAuthentication( form, request );
580         // Patch needed for authentication after being on the form
581         String secondAttempt = request.getParameter( "secondAttempt" );
582         boolean bTestSecondAttempt = Boolean.FALSE;
583         if ( StringUtils.isNotEmpty( secondAttempt ) )
584         {
585             bTestSecondAttempt = Boolean.TRUE;
586         }
587         // Need to manage the anchor
588         String anchor = request.getParameter( PARAMETER_ANCHOR );
589         if ( StringUtils.isNotEmpty( anchor ) )
590         {
591             LinkedHashMap<String, String> additionalParameters = new LinkedHashMap<String, String>( );
592             additionalParameters.put( PARAMETER_ID_FORM, strIdForm );
593             additionalParameters.put( PARAMETER_ID_SLOT, request.getParameter( PARAMETER_ID_SLOT ) );
594             additionalParameters.put( PARAMETER_STARTING_DATE_TIME, request.getParameter( PARAMETER_STARTING_DATE_TIME ) );
595             additionalParameters.put( PARAMETER_ENDING_DATE_TIME, request.getParameter( PARAMETER_ENDING_DATE_TIME ) );
596             additionalParameters.put( PARAMETER_IS_OPEN, request.getParameter( PARAMETER_IS_OPEN ) );
597             additionalParameters.put( PARAMETER_IS_SPECIFIC, request.getParameter( PARAMETER_IS_SPECIFIC ) );
598             additionalParameters.put( PARAMETER_MAX_CAPACITY, request.getParameter( PARAMETER_MAX_CAPACITY ) );
599             additionalParameters.put( PARAMETER_ANCHOR, MARK_ANCHOR + anchor );
600             return redirect( request, VIEW_APPOINTMENT_FORM, additionalParameters );
601 
602         }
603         String strIdSlot = request.getParameter( PARAMETER_ID_SLOT );
604         boolean bModificationForm = false;
605         if ( strIdSlot == null )
606         {
607             bModificationForm = true;
608         }
609         Integer nIdSlot = null;
610         if ( StringUtils.isNumeric( strIdSlot ) )
611         {
612             nIdSlot = Integer.parseInt( strIdSlot );
613         }
614         AppointmentDTO oldAppointmentDTO = null;
615         // Get the not validated appointment in session if it exists
616         AppointmentDTO appointmentDTO = (AppointmentDTO) request.getSession( ).getAttribute( SESSION_NOT_VALIDATED_APPOINTMENT );
617         if ( appointmentDTO == null )
618         {
619             // Try to get the validated appointment in session
620             // (in case the user click on back button in the recap view (or
621             // modification)
622             appointmentDTO = (AppointmentDTO) request.getSession( ).getAttribute( SESSION_VALIDATED_APPOINTMENT );
623             if ( appointmentDTO != null )
624             {
625                 request.getSession( ).removeAttribute( SESSION_VALIDATED_APPOINTMENT );
626                 request.getSession( ).setAttribute( SESSION_NOT_VALIDATED_APPOINTMENT, appointmentDTO );
627                 if ( nIdSlot != null && appointmentDTO.getIdSlot( ) != nIdSlot )
628                 {
629                     oldAppointmentDTO = appointmentDTO;
630                 }
631             }
632         }
633         else
634         {
635             // Appointment DTO not validated in session
636             // Need to verify if the slot has not changed
637             if ( nIdSlot != null && appointmentDTO.getIdSlot( ) != nIdSlot )
638             {
639                 oldAppointmentDTO = appointmentDTO;
640             }
641         }
642         if ( appointmentDTO == null || oldAppointmentDTO != null )
643         {
644             // Need to get back the informations the user has entered
645             appointmentDTO = new AppointmentDTO( );
646             if ( oldAppointmentDTO != null )
647             {
648                 appointmentDTO.setFirstName( oldAppointmentDTO.getFirstName( ) );
649                 appointmentDTO.setLastName( oldAppointmentDTO.getLastName( ) );
650                 appointmentDTO.setEmail( oldAppointmentDTO.getEmail( ) );
651                 appointmentDTO.setPhoneNumber( oldAppointmentDTO.getPhoneNumber( ) );
652                 appointmentDTO.setNbBookedSeats( oldAppointmentDTO.getNbBookedSeats( ) );
653                 appointmentDTO.setListResponse( oldAppointmentDTO.getListResponse( ) );
654                 appointmentDTO.setMapResponsesByIdEntry( oldAppointmentDTO.getMapResponsesByIdEntry( ) );
655             }
656         }
657         if ( !bModificationForm )
658         {
659             Slot slot = null;
660             // If nIdSlot == 0, the slot has not been created yet
661             if ( nIdSlot == 0 )
662             {
663                 // Need to get all the informations to create the slot
664                 LocalDateTime startingDateTime = LocalDateTime.parse( request.getParameter( PARAMETER_STARTING_DATE_TIME ) );
665                 LocalDateTime endingDateTime = LocalDateTime.parse( request.getParameter( PARAMETER_ENDING_DATE_TIME ) );
666                 // Need to check if the slot has not been already created
667                 HashMap<LocalDateTime, Slot> slotInDbMap = SlotService.buildMapSlotsByIdFormAndDateRangeWithDateForKey( nIdForm, startingDateTime,
668                         endingDateTime );
669                 if ( !slotInDbMap.isEmpty( ) )
670                 {
671                     slot = slotInDbMap.get( startingDateTime );
672                 }
673                 else
674                 {
675                     boolean bIsOpen = Boolean.parseBoolean( request.getParameter( PARAMETER_IS_OPEN ) );
676                     boolean bIsSpecific = Boolean.parseBoolean( request.getParameter( PARAMETER_IS_SPECIFIC ) );
677                     int nMaxCapacity = Integer.parseInt( request.getParameter( PARAMETER_MAX_CAPACITY ) );
678                     slot = SlotService.buildSlot( nIdForm, new Period( startingDateTime, endingDateTime ), nMaxCapacity, nMaxCapacity, nMaxCapacity, 0,
679                             bIsOpen, bIsSpecific );
680                     slot = SlotService.saveSlot( slot );
681                 }
682             }
683             else
684             {
685                 slot = SlotService.findSlotById( nIdSlot );
686             }
687             ReservationRule reservationRule = ReservationRuleService.findReservationRuleByIdFormAndClosestToDateOfApply( nIdForm, slot.getDate( ) );
688             WeekDefinition weekDefinition = WeekDefinitionService.findWeekDefinitionByIdFormAndClosestToDateOfApply( nIdForm, slot.getDate( ) );
689             form = FormService.buildAppointmentForm( nIdForm, reservationRule.getIdReservationRule( ), weekDefinition.getIdWeekDefinition( ) );
690             // Need to check competitive access
691             // May be the slot is already taken at the same time
692             if ( !bTestSecondAttempt && slot.getNbPotentialRemainingPlaces( ) == 0 )
693             {
694                 addError( ERROR_MESSAGE_SLOT_FULL, getLocale( request ) );
695                 return redirect( request, VIEW_APPOINTMENT_CALENDAR, PARAMETER_ID_FORM, nIdForm );
696             }
697             appointmentDTO.setSlot( slot );
698             appointmentDTO.setIdSlot( slot.getIdSlot( ) );
699             appointmentDTO.setDateOfTheAppointment( slot.getDate( ).format( Utilities.getFormatter( ) ) );
700             appointmentDTO.setIdForm( nIdForm );
701             LuteceUser user = SecurityService.getInstance( ).getRegisteredUser( request );
702             if ( user != null )
703             {
704                 setUserInfo( request, appointmentDTO );
705             }
706             request.getSession( ).setAttribute( SESSION_NOT_VALIDATED_APPOINTMENT, appointmentDTO );
707             request.getSession( ).setAttribute( SESSION_ATTRIBUTE_APPOINTMENT_FORM, form );
708             AppointmentUtilities.putTimerInSession( request, slot, appointmentDTO, form.getMaxPeoplePerAppointment( ) );
709         }
710         else
711         {
712             // Modification of the Form only
713             String strModificationForm = request.getParameter( PARAMETER_MODIFICATION_FORM );
714             // Need to redirect for the anchor
715             if ( StringUtils.isEmpty( strModificationForm ) )
716             {
717                 LinkedHashMap<String, String> additionalParameters = new LinkedHashMap<String, String>( );
718                 additionalParameters.put( PARAMETER_ID_FORM, strIdForm );
719                 additionalParameters.put( PARAMETER_MODIFICATION_FORM, String.valueOf( Boolean.TRUE ) );
720                 additionalParameters.put( PARAMETER_ANCHOR, MARK_ANCHOR + STEP_3 );
721                 return redirect( request, VIEW_APPOINTMENT_FORM, additionalParameters );
722             }
723         }
724         Map<String, Object> model = getModel( );
725 
726         FormMessage formMessages = FormMessageService.findFormMessageByIdForm( nIdForm );
727         List<GenericAttributeError> listErrors = (List<GenericAttributeError>) request.getSession( ).getAttribute( SESSION_APPOINTMENT_FORM_ERRORS );
728         if ( listErrors != null )
729         {
730             model.put( MARK_FORM_ERRORS, listErrors );
731             request.getSession( ).removeAttribute( SESSION_APPOINTMENT_FORM_ERRORS );
732         }
733 
734         StringBuffer strBuffer = new StringBuffer( );
735         Locale locale = getLocale( request );
736         List<Entry> listEntryFirstLevel = EntryService.getFilter( form.getIdForm( ), true );
737         for ( Entry entry : listEntryFirstLevel )
738         {
739             EntryService.getHtmlEntry( model, entry.getIdEntry( ), strBuffer, locale, true, request );
740             // manage error messages for debut montage and fin demontage
741             if ( CollectionUtils.isNotEmpty( listErrors ) && ( StringUtils.contains( entry.getTitle( ), LABEL_MONTAGE ) || StringUtils.contains( entry.getTitle( ), LABEL_DEMONTAGE ) ) )
742             {
743                 for ( GenericAttributeError error : listErrors )
744                 {
745                     if ( !error.isMandatoryError( ) && entry.getTitle( ).equals( error.getTitleQuestion( ) ) )
746                     {
747                         strBuffer.append("<div class=\"alert alert-error alert-danger\"><p>" + error.getErrorMessage( ) + "</p></div>");
748                     }
749                 }
750             }
751         }
752 
753         model.put( MARK_APPOINTMENT, appointmentDTO );
754         model.put( PARAMETER_DATE_OF_DISPLAY, appointmentDTO.getSlot( ).getDate( ) );
755         model.put( MARK_FORM, form );
756         model.put( MARK_FORM_MESSAGES, formMessages );
757         model.put( MARK_STR_ENTRY, strBuffer.toString( ) );
758         model.put( MARK_LOCALE, locale );
759         model.put( MARK_PLACES, appointmentDTO.getNbMaxPotentialBookedSeats( ) );
760         model.put( MARK_FORM_ERRORS, listErrors );
761         model.put( MARK_LIST_ERRORS, AppointmentDTO.getAllErrors( locale ) );
762         HtmlTemplate templateForm = AppTemplateService.getTemplate( TEMPLATE_HTML_CODE_FORM, locale, model );
763         model.put( MARK_FORM_HTML, templateForm.getHtml( ) );
764         HtmlTemplate template = AppTemplateService.getTemplate( TEMPLATE_APPOINTMENT_FORM, getLocale( request ), model );
765         XPage xPage = new XPage( );
766         xPage.setContent( template.getHtml( ) );
767         xPage.setPathLabel( getDefaultPagePath( getLocale( request ) ) );
768         if ( form.getDisplayTitleFo( ) )
769         {
770             xPage.setTitle( form.getTitle( ) );
771         }
772         return xPage;
773     }
774 
775     /**
776      * Do validate data entered by a user to fill a form
777      * 
778      * @param request
779      *            The request
780      * @return The next URL to redirect to
781      * @throws SiteMessageException
782      * @throws UserNotSignedException
783      */
784     @Action( ACTION_DO_VALIDATE_FORM )
785     public XPage doValidateForm( HttpServletRequest request ) throws SiteMessageException, UserNotSignedException
786     {
787         AppointmentFormDTO form = (AppointmentFormDTO) request.getSession( ).getAttribute( SESSION_ATTRIBUTE_APPOINTMENT_FORM );
788         checkMyLuteceAuthentication( form, request );
789         String strIdForm = request.getParameter( PARAMETER_ID_FORM );
790         AppointmentDTO appointmentDTO = (AppointmentDTO) request.getSession( ).getAttribute( SESSION_NOT_VALIDATED_APPOINTMENT );
791         List<GenericAttributeError> listFormErrors = new ArrayList<GenericAttributeError>( );
792         Locale locale = request.getLocale( );
793         String strEmail = request.getParameter( PARAMETER_EMAIL );
794         String strFirstName = request.getParameter( PARAMETER_FIRST_NAME );
795         String strLastName = request.getParameter( PARAMETER_LAST_NAME );
796         AppointmentUtilities.checkDateOfTheAppointmentIsNotBeforeNow( appointmentDTO, locale, listFormErrors );
797         AppointmentUtilities.checkEmail( strEmail, request.getParameter( PARAMETER_EMAIL_CONFIRMATION ), form, locale, listFormErrors );
798         int nbBookedSeats = AppointmentUtilities.checkAndReturnNbBookedSeats( request.getParameter( PARAMETER_NUMBER_OF_BOOKED_SEATS ), form, appointmentDTO,
799                 locale, listFormErrors );
800         AppointmentUtilities.fillAppointmentDTO( appointmentDTO, nbBookedSeats, strEmail, strFirstName, strLastName );
801         AppointmentUtilities.validateFormAndEntries( appointmentDTO, request, listFormErrors );
802         AppointmentUtilities.fillInListResponseWithMapResponse( appointmentDTO );
803 
804         List<Response> responses = appointmentDTO.getListResponse( );
805 
806         Response responseMontage = null;
807         Response responseDemontage = null;
808         for ( Response response : responses )
809         {
810             Entry entry = response.getEntry( );
811             if ( StringUtils.contains( entry.getTitle( ), LABEL_MONTAGE ) )
812             {
813                 responseMontage = response;
814             }
815             if ( StringUtils.contains( entry.getTitle( ), LABEL_DEMONTAGE ) )
816             {
817                 responseDemontage = response;
818             }
819         }
820 
821         if ( responseMontage != null && responseDemontage != null )
822         {
823             SimpleDateFormat sdf = new SimpleDateFormat( "HHmm" );
824 
825             Date dateMontage = null;
826             Date dateDemontage = null;
827             try
828             {
829                 // montage
830                 String heureMontage = responseMontage.getToStringValueResponse( ).replaceAll("(?i)h", "");
831                 if ( heureMontage != null && heureMontage.length() == 3 )
832                 {
833                     heureMontage = "0" + heureMontage;
834                 }
835                 dateMontage = sdf.parse( heureMontage );
836                 // demontage
837                 String heureDemontage = responseDemontage.getToStringValueResponse( ).replaceAll("(?i)h", "");
838                 if ( heureDemontage != null && heureDemontage.length() == 3 )
839                 {
840                     heureDemontage = "0" + heureDemontage;
841                 }
842                 dateDemontage = sdf.parse( heureDemontage );
843             } catch ( ParseException e )
844             {
845                 AppLogService.error( "Parsing error", e );
846             }
847 
848             if ( dateMontage != null && dateDemontage != null )
849             {
850                 AppointmentFillDTO dto = getAppointmentFillDTO( request );
851                 // Validation de l'amplitude horaire
852                 if ( AppointmentService.checkSelectedAnimation( dto.getIdFormUser( ), animations ) && amplitude != NumberUtils.INTEGER_MINUS_ONE )
853                 {
854                     long minutes = TimeUnit.MILLISECONDS.toMinutes( dateDemontage.getTime( ) - dateMontage.getTime( ) );
855                     if ( minutes > amplitude )
856                     {
857                         GenericAttributeError error = new GenericAttributeError( );
858                         error.setTitleQuestion( responseDemontage.getEntry( ).getTitle( ) );
859                         error.setErrorMessage( I18nService.getLocalizedString( ERROR_MESSAGE_AMPLITUDE, request.getLocale( ) ) );
860                         error.setIsDisplayableError( Boolean.TRUE );
861                         listFormErrors.add( error );
862                     }
863                 }
864                 // Validation de la limite horaire
865                 try
866                 {
867                     if ( AppointmentUtilities.isSlotAM( appointmentDTO.getSlot( ) ) )
868                     {
869                         Date dateMaxAM = sdf.parse( horaireMaxAM );
870                         if ( dateDemontage.after( dateMaxAM ) )
871                         {
872                             GenericAttributeError error = new GenericAttributeError( );
873                             error.setTitleQuestion( responseDemontage.getEntry( ).getTitle( ) );
874                             error.setErrorMessage( I18nService.getLocalizedString( ERROR_MESSAGE_HORAIRE_FO, request.getLocale( ) ) );
875                             error.setIsDisplayableError( Boolean.TRUE );
876                             listFormErrors.add( error );
877                         }
878                     } else
879                     {
880                         Date dateMaxPM = sdf.parse( horaireMinPM );
881                         if ( dateMontage.before( dateMaxPM ) )
882                         {
883                             GenericAttributeError error = new GenericAttributeError( );
884                             error.setTitleQuestion( responseMontage.getEntry( ).getTitle( ) );
885                             error.setErrorMessage( I18nService.getLocalizedString( ERROR_MESSAGE_HORAIRE_FO, request.getLocale( ) ) );
886                             error.setIsDisplayableError( Boolean.TRUE );
887                             listFormErrors.add( error );
888                         }
889                     }
890                 } catch ( ParseException e )
891                 {
892                     AppLogService.error( "Parsing error", e );
893                 }
894             }
895         }
896 
897         boolean bErrors = false;
898         if ( !AppointmentUtilities.checkNbDaysBetweenTwoAppointments( appointmentDTO, strFirstName, strLastName, strEmail, form ) )
899         {
900             addError( ERROR_MESSAGE_NB_MIN_DAYS_BETWEEN_TWO_APPOINTMENTS, locale );
901             bErrors = true;
902         }
903         if ( form.getEnableMandatoryEmail( ) && !AppointmentUtilities.checkNbMaxAppointmentsOnAGivenPeriod( appointmentDTO, strEmail, form ) )
904         {
905             addError( ERROR_MESSAGE_NB_MAX_APPOINTMENTS_ON_A_PERIOD, locale );
906             bErrors = true;
907         }
908         if ( CollectionUtils.isNotEmpty( listFormErrors ) )
909         {
910             request.getSession( ).setAttribute( SESSION_APPOINTMENT_FORM_ERRORS, listFormErrors );
911             bErrors = true;
912         }
913         if ( bErrors )
914         {
915             LinkedHashMap<String, String> additionalParameters = new LinkedHashMap<String, String>( );
916             additionalParameters.put( PARAMETER_ID_FORM, strIdForm );
917             additionalParameters.put( PARAMETER_MODIFICATION_FORM, String.valueOf( Boolean.TRUE ) );
918             additionalParameters.put( PARAMETER_ANCHOR, MARK_ANCHOR + STEP_3 );
919             return redirect( request, VIEW_APPOINTMENT_FORM, additionalParameters );
920         }
921         request.getSession( ).removeAttribute( SESSION_NOT_VALIDATED_APPOINTMENT );
922         request.getSession( ).setAttribute( SESSION_VALIDATED_APPOINTMENT, appointmentDTO );
923         XPage xPage = null;
924         String anchor = request.getParameter( PARAMETER_ANCHOR );
925         if ( StringUtils.isNotEmpty( anchor ) )
926         {
927             Map<String, String> additionalParameters = new HashMap<>( );
928             additionalParameters.put( PARAMETER_ANCHOR, MARK_ANCHOR + anchor );
929             xPage = redirect( request, VIEW_DISPLAY_RECAP_APPOINTMENT, additionalParameters );
930         }
931         else
932         {
933             xPage = redirectView( request, VIEW_DISPLAY_RECAP_APPOINTMENT );
934         }
935         return xPage;
936     }
937 
938     /**
939      * Display the recap before validating an appointment
940      * 
941      * @param request
942      *            The request
943      * @return The HTML content to display or the next URL to redirect to
944      * @throws UserNotSignedException
945      */
946     @View( VIEW_DISPLAY_RECAP_APPOINTMENT )
947     public XPage displayRecapAppointment( HttpServletRequest request ) throws UserNotSignedException
948     {
949         AppointmentFillDTO dto = getAppointmentFillDTO( request );
950 
951         if((null == dto || null == dto.getIdForm( ) || null == dto.getIdFormUser( ) || null == dto.getSecurityKey( )) || !AppointmentService.checkSecurityKey( dto.getIdForm( ), dto.getIdFormUser( ), dto.getSecurityKey( ), idEntryKiosque, idEntryCleSecurite )){
952             return getAppointmentErrorView(request, ERROR_MESSAGE_CLE_INCORRECTE);
953         }
954 
955         AppointmentFormDTO form = (AppointmentFormDTO) request.getSession( ).getAttribute( SESSION_ATTRIBUTE_APPOINTMENT_FORM );
956         checkMyLuteceAuthentication( form, request );
957         String anchor = request.getParameter( PARAMETER_ANCHOR );
958         if ( StringUtils.isNotEmpty( anchor ) )
959         {
960             Map<String, String> additionalParameters = new HashMap<>( );
961             additionalParameters.put( PARAMETER_ANCHOR, MARK_ANCHOR + anchor );
962             return redirect( request, VIEW_DISPLAY_RECAP_APPOINTMENT, additionalParameters );
963         }
964         AppointmentDTO appointment = (AppointmentDTO) request.getSession( ).getAttribute( SESSION_VALIDATED_APPOINTMENT );
965         if ( appointment == null )
966         {
967             return redirectView( request, VIEW_APPOINTMENT_FORM_LIST );
968         }
969         Map<String, Object> model = new HashMap<String, Object>( );
970         if ( form.getEnableCaptcha( ) && getCaptchaService( ).isAvailable( ) )
971         {
972             model.put( MARK_CAPTCHA, getCaptchaService( ).getHtmlCode( ) );
973         }
974         model.put( MARK_FORM_MESSAGES, FormMessageService.findFormMessageByIdForm( appointment.getIdForm( ) ) );
975         fillCommons( model );
976         model.put( MARK_APPOINTMENT, appointment );
977         Locale locale = getLocale( request );
978         model.put( MARK_LIST_RESPONSE_RECAP_DTO, AppointmentUtilities.buildListResponse( appointment, request, locale ) );
979         model.put( MARK_FORM, form );
980         model.put( PARAMETER_DATE_OF_DISPLAY, appointment.getSlot( ).getDate( ) );
981         XPage xPage = new XPage( );
982         HtmlTemplate t = AppTemplateService.getTemplate( TEMPLATE_APPOINTMENT_FORM_RECAP, locale, model );
983         xPage.setContent( t.getHtml( ) );
984         xPage.setTitle( getDefaultPageTitle( locale ) );
985         xPage.setPathLabel( getDefaultPagePath( locale ) );
986         return xPage;
987     }
988 
989     /**
990      * Do save an appointment into the database if it is valid
991      * 
992      * @param request
993      *            The request
994      * @return The XPage to display
995      * @throws UserNotSignedException
996      */
997     @Action( ACTION_DO_MAKE_APPOINTMENT )
998     public XPage doMakeAppointment( HttpServletRequest request ) throws UserNotSignedException
999     {
1000         AppointmentFormDTO form = (AppointmentFormDTO) request.getSession( ).getAttribute( SESSION_ATTRIBUTE_APPOINTMENT_FORM );
1001         checkMyLuteceAuthentication( form, request );
1002         AppointmentDTO appointment = (AppointmentDTO) request.getSession( ).getAttribute( SESSION_VALIDATED_APPOINTMENT );
1003         if ( StringUtils.isNotEmpty( request.getParameter( PARAMETER_BACK ) ) )
1004         {
1005             return getAppointmentErrorView(request, ERROR_MESSAGE_CLE_INCORRECTE);
1006         }
1007         if ( form.getEnableCaptcha( ) && getCaptchaService( ).isAvailable( ) )
1008         {
1009             if ( !getCaptchaService( ).validate( request ) )
1010             {
1011                 addError( ERROR_MESSAGE_CAPTCHA, getLocale( request ) );
1012                 return redirect( request, VIEW_DISPLAY_RECAP_APPOINTMENT, PARAMETER_ID_FORM, appointment.getIdForm( ) );
1013             }
1014         }
1015         Slot slot = null;
1016         // Reload the slot from the database
1017         // The slot could have been taken since the beginning of the entry of
1018         // the form
1019         if ( appointment.getSlot( ).getIdSlot( ) != 0 )
1020         {
1021             slot = SlotService.findSlotById( appointment.getSlot( ).getIdSlot( ) );
1022         }
1023         else
1024         {
1025             HashMap<LocalDateTime, Slot> mapSlot = SlotService.buildMapSlotsByIdFormAndDateRangeWithDateForKey( appointment.getIdForm( ), appointment.getSlot( )
1026                     .getStartingDateTime( ), appointment.getSlot( ).getEndingDateTime( ) );
1027             if ( !mapSlot.isEmpty( ) )
1028             {
1029                 slot = mapSlot.get( appointment.getSlot( ).getStartingDateTime( ) );
1030             }
1031             else
1032             {
1033                 slot = appointment.getSlot( );
1034             }
1035         }
1036         appointment.setSlot( slot );
1037         if ( appointment.getNbBookedSeats( ) > slot.getNbRemainingPlaces( ) )
1038         {
1039             addInfo( ERROR_MESSAGE_SLOT_FULL, getLocale( request ) );
1040             return redirect( request, VIEW_APPOINTMENT_CALENDAR, PARAMETER_ID_FORM, appointment.getIdForm( ) );
1041         }
1042         AppointmentUtilities.killTimer( request );
1043         int nIdAppointment = AppointmentService.saveAppointment( appointment );
1044         AppLogService.info( LogUtilities.buildLog( ACTION_DO_MAKE_APPOINTMENT, Integer.toString( nIdAppointment ), null ) );
1045         request.getSession( ).removeAttribute( SESSION_VALIDATED_APPOINTMENT );
1046         AppointmentAsynchronousUploadHandler.getHandler( ).removeSessionFiles( request.getSession( ).getId( ) );
1047         XPage xPage = null;
1048         String anchor = request.getParameter( PARAMETER_ANCHOR );
1049         if ( StringUtils.isNotEmpty( anchor ) )
1050         {
1051             LinkedHashMap<String, String> additionalParameters = new LinkedHashMap<String, String>( );
1052             additionalParameters.put( PARAMETER_ID_FORM, String.valueOf( appointment.getIdForm( ) ) );
1053             additionalParameters.put( PARAMETER_ID_APPOINTMENT, String.valueOf( nIdAppointment ) );
1054             additionalParameters.put( PARAMETER_ANCHOR, MARK_ANCHOR + anchor );
1055             xPage = redirect( request, VIEW_GET_APPOINTMENT_CREATED, additionalParameters );
1056         }
1057         else
1058         {
1059             xPage = redirect( request, VIEW_GET_APPOINTMENT_CREATED, PARAMETER_ID_FORM, appointment.getIdForm( ), PARAMETER_ID_APPOINTMENT, nIdAppointment );
1060         }
1061         return xPage;
1062     }
1063 
1064     /**
1065      * Get the page to notify the user that the appointment has been created
1066      * 
1067      * @param request
1068      *            The request
1069      * @return The XPage to display
1070      */
1071     @View( VIEW_GET_APPOINTMENT_CREATED )
1072     public XPage getAppointmentCreated( HttpServletRequest request )
1073     {
1074         AppointmentFillDTO dto = getAppointmentFillDTO( request );
1075 
1076         if((null == dto || null == dto.getIdForm( ) || null == dto.getIdFormUser( ) || null == dto.getSecurityKey( )) || !AppointmentService.checkSecurityKey( dto.getIdForm( ), dto.getIdFormUser( ), dto.getSecurityKey( ), idEntryKiosque, idEntryCleSecurite )){
1077             return getAppointmentErrorView(request, ERROR_MESSAGE_CLE_INCORRECTE);
1078         }
1079 
1080         int nIdForm = Integer.parseInt( request.getParameter( PARAMETER_ID_FORM ) );
1081         int nIdAppointment = Integer.parseInt( request.getParameter( PARAMETER_ID_APPOINTMENT ) );
1082         AppLogService.debug( "n Id Appointment :" + nIdAppointment );
1083         Appointment appointment = AppointmentService.findAppointmentById( nIdAppointment );
1084         FormMessage formMessages = FormMessageService.findFormMessageByIdForm( nIdForm );
1085         Slot slot = SlotService.findSlotById( appointment.getIdSlot( ) );
1086         AppointmentFormDTO form = FormService.buildAppointmentForm( nIdForm, 0, 0 );
1087         String strTimeBegin = slot.getStartingDateTime( ).toLocalTime( ).toString( );
1088         String strTimeEnd = slot.getEndingDateTime( ).toLocalTime( ).toString( );
1089         String strReference = StringUtils.EMPTY;
1090         if ( !StringUtils.isEmpty( form.getReference( ) ) )
1091         {
1092             strReference = Strings.toUpperCase( form.getReference( ).trim( ) ) + " - ";
1093         }
1094         strReference += appointment.getReference( );
1095         formMessages.setTextAppointmentCreated( formMessages.getTextAppointmentCreated( ).replaceAll( MARK_REF, strReference )
1096                 .replaceAll( MARK_DATE_APP, slot.getStartingDateTime( ).toLocalDate( ).format( Utilities.getFormatter( ) ) )
1097                 .replaceAll( MARK_TIME_BEGIN, strTimeBegin ).replaceAll( MARK_TIME_END, strTimeEnd ) );
1098         Map<String, Object> model = new HashMap<String, Object>( );
1099         AppointmentDTO appointmentDTO = AppointmentService.buildAppointmentDTOFromIdAppointment( nIdAppointment );
1100         appointmentDTO.setListResponse( AppointmentResponseService.findAndBuildListResponse( nIdAppointment, request ) );
1101         appointmentDTO.setMapResponsesByIdEntry( AppointmentResponseService.buildMapFromListResponse( appointmentDTO.getListResponse( ) ) );
1102         model.put( MARK_LIST_RESPONSE_RECAP_DTO, AppointmentUtilities.buildListResponse( appointmentDTO, request, getLocale( request ) ) );
1103         model.put( MARK_DATE_APPOINTMENT, slot.getDate( ).format( Utilities.getFormatter( ) ) );
1104         model.put( MARK_STARTING_TIME_APPOINTMENT, slot.getStartingTime( ) );
1105         model.put( MARK_ENDING_TIME_APPOINTMENT, slot.getEndingTime( ) );
1106         model.put( MARK_USER, UserService.findUserById( appointment.getIdUser( ) ) );
1107         model.put( MARK_PLACES, appointment.getNbPlaces( ) );
1108         model.put( MARK_FORM, form );
1109         model.put( MARK_FORM_MESSAGES, formMessages );
1110         return getXPage( TEMPLATE_APPOINTMENT_CREATED, getLocale( request ), model );
1111     }
1112 
1113     /**
1114      * Get the view of all the forms on front office side
1115      * 
1116      * @param request
1117      *            the request
1118      * @return the xpage
1119      */
1120     @View( value = VIEW_APPOINTMENT_FORM_LIST, defaultView = true )
1121     public XPage getFormList( HttpServletRequest request )
1122     {
1123         Locale locale = getLocale( request );
1124         String strHtmlContent = getFormListHtml( request, locale );
1125         XPage xpage = new XPage( );
1126         xpage.setContent( strHtmlContent );
1127         xpage.setPathLabel( getDefaultPagePath( locale ) );
1128         xpage.setTitle( getDefaultPageTitle( locale ) );
1129         return xpage;
1130     }
1131 
1132     /**
1133      * Get the view for he user who wants to cancel its appointment
1134      * 
1135      * @param request
1136      * @return the view
1137      * @throws SiteMessageException
1138      */
1139     @View( VIEW_GET_VIEW_CANCEL_APPOINTMENT )
1140     public XPage getViewCancelAppointment( HttpServletRequest request ) throws SiteMessageException
1141     {
1142 
1143         AppointmentFillDTO dto = getAppointmentFillDTO( request );
1144 
1145         if((null == dto || null == dto.getIdForm( ) || null == dto.getIdFormUser( ) || null == dto.getSecurityKey( )) || !AppointmentService.checkSecurityKey( dto.getIdForm( ), dto.getIdFormUser( ), dto.getSecurityKey( ), idEntryKiosque, idEntryCleSecurite )){
1146             return getAppointmentErrorView(request, ERROR_MESSAGE_CLE_INCORRECTE);
1147         }
1148 
1149         String refAppointment = request.getParameter( PARAMETER_REF_APPOINTMENT );
1150         Appointment appointment = null;
1151         if ( StringUtils.isNotEmpty( refAppointment ) )
1152         {
1153             appointment = AppointmentService.findAppointmentByReference( refAppointment );
1154         }
1155         Map<String, Object> model = new HashMap<String, Object>( );
1156         model.put( PARAMETER_REF_APPOINTMENT, refAppointment );
1157         if ( appointment != null )
1158         {
1159 
1160             if ( appointment.getIsCancelled( ) )
1161             {
1162                 model.put( MARK_APPOINTMENT_ALREADY_CANCELLED, Boolean.TRUE );
1163             }
1164             int nIdAppointment = appointment.getIdAppointment( );
1165             Slot slot = SlotService.findSlotById( appointment.getIdSlot( ) );
1166             // Check if the appointment is passed
1167             if ( slot.getStartingDateTime( ).isBefore( LocalDateTime.now( ) ) )
1168             {
1169                 model.put( MARK_APPOINTMENT_PASSED, Boolean.TRUE );
1170             }
1171             model.put( MARK_DATE_APPOINTMENT, slot.getDate( ).format( Utilities.getFormatter( ) ) );
1172             model.put( MARK_STARTING_TIME_APPOINTMENT, slot.getStartingTime( ) );
1173             model.put( MARK_ENDING_TIME_APPOINTMENT, slot.getEndingTime( ) );
1174             model.put( MARK_PLACES, appointment.getNbPlaces( ) );
1175             model.put( MARK_FORM, FormService.buildAppointmentForm( slot.getIdForm( ), 0, 0 ) );
1176             model.put( MARK_FORM_MESSAGES, FormMessageService.findFormMessageByIdForm( slot.getIdForm( ) ) );
1177             AppointmentDTO appointmentDTO = AppointmentService.buildAppointmentDTOFromIdAppointment( nIdAppointment );
1178             appointmentDTO.setListResponse( AppointmentResponseService.findAndBuildListResponse( nIdAppointment, request ) );
1179             appointmentDTO.setMapResponsesByIdEntry( AppointmentResponseService.buildMapFromListResponse( appointmentDTO.getListResponse( ) ) );
1180             model.put( MARK_LIST_RESPONSE_RECAP_DTO, AppointmentUtilities.buildListResponse( appointmentDTO, request, getLocale( request ) ) );
1181             model.put( MARK_USER, UserService.findUserById( appointment.getIdUser( ) ) );
1182 
1183         }
1184         else
1185         {
1186             model.put( MARK_NO_APPOINTMENT_WITH_THIS_REFERENCE, Boolean.TRUE );
1187         }
1188         Locale locale = getLocale( request );
1189         XPage xpage = getXPage( TEMPLATE_CANCEL_APPOINTMENT, locale, model );
1190         xpage.setTitle( I18nService.getLocalizedString( MESSAGE_CANCEL_APPOINTMENT_PAGE_TITLE, locale ) );
1191         return xpage;
1192     }
1193 
1194     /**
1195      * Cancel an appointment
1196      * 
1197      * @param request
1198      * @return the confirmation view of the appointment cancelled
1199      * @throws SiteMessageException
1200      */
1201     @Action( ACTION_DO_CANCEL_APPOINTMENT )
1202     public XPage doCancelAppointment( HttpServletRequest request ) throws SiteMessageException
1203     {
1204         String strRef = request.getParameter( PARAMETER_REF_APPOINTMENT );
1205         if ( StringUtils.isNotEmpty( strRef ) )
1206         {
1207             Appointment appointment = AppointmentService.findAppointmentByReference( strRef );
1208             // Accept only one cancel !!!
1209             if ( appointment != null && !appointment.getIsCancelled( ) )
1210             {
1211                 Slot slot = SlotService.findSlotById( appointment.getIdSlot( ) );
1212                 // Check if the appointment is passed
1213                 if ( !slot.getStartingDateTime( ).isBefore( LocalDateTime.now( ) ) )
1214                 {
1215                     if ( appointment.getIdActionCancelled( ) > 0 )
1216                     {
1217                         boolean automaticUpdate = ( AdminUserService.getAdminUser( request ) == null ) ? true : false;
1218                         try
1219                         {
1220                             WorkflowService.getInstance( ).doProcessAction( appointment.getIdAppointment( ), Appointment.APPOINTMENT_RESOURCE_TYPE,
1221                                     appointment.getIdActionCancelled( ), slot.getIdForm( ), request, request.getLocale( ), automaticUpdate );
1222                         }
1223                         catch( Exception e )
1224                         {
1225                             AppLogService.error( "Error Workflow", e );
1226                         }
1227                     }
1228                     else
1229                     {
1230                         appointment.setIsCancelled( Boolean.TRUE );
1231                         AppointmentService.updateAppointment( appointment );
1232                         AppLogService.info( LogUtilities.buildLog( ACTION_DO_CANCEL_APPOINTMENT, Integer.toString( appointment.getIdAppointment( ) ), null ) );
1233                     }
1234                     Map<String, String> mapParameters = new HashMap<String, String>( );
1235                     if ( StringUtils.isNotEmpty( request.getParameter( PARAMETER_FROM_MY_APPOINTMENTS ) ) )
1236                     {
1237                         String strReferer = request.getHeader( PARAMETER_REFERER );
1238                         if ( StringUtils.isNotEmpty( strReferer ) )
1239                         {
1240                             mapParameters.put( MARK_FROM_URL, strReferer );
1241                         }
1242                         mapParameters.put( PARAMETER_FROM_MY_APPOINTMENTS, request.getParameter( PARAMETER_FROM_MY_APPOINTMENTS ) );
1243                     }
1244                     mapParameters.put( PARAMETER_ID_FORM, Integer.toString( slot.getIdForm( ) ) );
1245                     return redirect( request, VIEW_APPOINTMENT_CANCELED, mapParameters );
1246                 }
1247             }
1248         }
1249         return redirectView( request, VIEW_APPOINTMENT_FORM_LIST );
1250     }
1251 
1252     /**
1253      * Get the page to confirm that the appointment has been canceled
1254      * 
1255      * @param request
1256      *            The request
1257      * @return The XPage to display
1258      */
1259     @View( VIEW_APPOINTMENT_CANCELED )
1260     public XPage getAppointmentCanceled( HttpServletRequest request )
1261     {
1262         AppointmentFillDTO dto = getAppointmentFillDTO( request );
1263 
1264         if((null == dto || null == dto.getIdForm( ) || null == dto.getIdFormUser( ) || null == dto.getSecurityKey( )) || !AppointmentService.checkSecurityKey( dto.getIdForm( ), dto.getIdFormUser( ), dto.getSecurityKey( ), idEntryKiosque, idEntryCleSecurite )){
1265             return getAppointmentErrorView(request, ERROR_MESSAGE_CLE_INCORRECTE);
1266         }
1267 
1268         String strIdForm = request.getParameter( PARAMETER_ID_FORM );
1269         if ( StringUtils.isNotEmpty( strIdForm ) && StringUtils.isNumeric( strIdForm ) )
1270         {
1271             int nIdForm = Integer.parseInt( strIdForm );
1272             Map<String, Object> model = new HashMap<String, Object>( );
1273             model.put( MARK_FORM_MESSAGES, FormMessageService.findFormMessageByIdForm( nIdForm ) );
1274             if ( Boolean.parseBoolean( request.getParameter( PARAMETER_FROM_MY_APPOINTMENTS ) ) )
1275             {
1276                 String strFromUrl = request.getParameter( MARK_FROM_URL );
1277                 model.put( MARK_BACK_URL, StringUtils.isNotEmpty( strFromUrl ) ? strFromUrl : getViewUrl( VIEW_GET_MY_APPOINTMENTS ) );
1278             }
1279             return getXPage( TEMPLATE_APPOINTMENT_CANCELED, getLocale( request ), model );
1280         }
1281         return redirectView( request, VIEW_APPOINTMENT_FORM_LIST );
1282     }
1283 
1284     /**
1285      * Get the page to view the appointments of a user
1286      * 
1287      * @param request
1288      *            The request
1289      * @return The XPage to display
1290      * @throws UserNotSignedException
1291      *             If the authentication is enabled and the user has not signed in
1292      */
1293     @View( VIEW_GET_MY_APPOINTMENTS )
1294     public XPage getMyAppointments( HttpServletRequest request ) throws UserNotSignedException
1295     {
1296         AppointmentFillDTO dto = getAppointmentFillDTO( request );
1297 
1298         if((null == dto || null == dto.getIdForm( ) || null == dto.getIdFormUser( ) || null == dto.getSecurityKey( )) || !AppointmentService.checkSecurityKey( dto.getIdForm( ), dto.getIdFormUser( ), dto.getSecurityKey( ), idEntryKiosque, idEntryCleSecurite )){
1299             return getAppointmentErrorView(request, ERROR_MESSAGE_CLE_INCORRECTE);
1300         }
1301 
1302         if ( !SecurityService.isAuthenticationEnable( ) )
1303         {
1304             return redirectView( request, VIEW_APPOINTMENT_FORM_LIST );
1305         }
1306         XPage xpage = new XPage( );
1307         Locale locale = getLocale( request );
1308         xpage.setContent( getMyAppointmentsXPage( request, locale ) );
1309         xpage.setTitle( I18nService.getLocalizedString( MESSAGE_MY_APPOINTMENTS_PAGE_TITLE, locale ) );
1310         return xpage;
1311     }
1312 
1313     /**
1314      * Get the HTML content of the my appointment page of a user
1315      * 
1316      * @param request
1317      *            The request
1318      * @param locale
1319      *            The locale
1320      * @return The HTML content, or null if the
1321      * @throws UserNotSignedException
1322      *             If the user has not signed in
1323      */
1324     public static String getMyAppointmentsXPage( HttpServletRequest request, Locale locale ) throws UserNotSignedException
1325     {
1326         if ( !SecurityService.isAuthenticationEnable( ) )
1327         {
1328             return null;
1329         }
1330         LuteceUser luteceUser = SecurityService.getInstance( ).getRegisteredUser( request );
1331         if ( luteceUser == null )
1332         {
1333             throw new UserNotSignedException( );
1334         }
1335         List<AppointmentDTO> listAppointmentDTO = new ArrayList<>( );
1336         Map<String, Object> model = new HashMap<String, Object>( );
1337         model.put( MARK_LIST_APPOINTMENTS, listAppointmentDTO );
1338         HtmlTemplate template = AppTemplateService.getTemplate( TEMPLATE_MY_APPOINTMENTS, locale, model );
1339         return template.getHtml( );
1340     }
1341 
1342     /**
1343      * Get the html content of the list of forms
1344      * 
1345      * @param appointmentFormService
1346      *            The service to use
1347      * @param strTitle
1348      *            The title to display, or null to display the default title.
1349      * @param locale
1350      *            The locale
1351      * @return The HTML content to display
1352      */
1353     public static String getFormListHtml( HttpServletRequest request, Locale locale )
1354     {
1355         request.getSession( ).removeAttribute( SESSION_VALIDATED_APPOINTMENT );
1356         request.getSession( ).removeAttribute( SESSION_ATTRIBUTE_APPOINTMENT_FORM );
1357         Map<String, Object> model = new HashMap<String, Object>( );
1358         List<AppointmentFormDTO> listAppointmentForm = FormService.buildAllActiveAndDisplayedOnPortletAppointmentForm( );
1359         // We keep only the active
1360         if ( CollectionUtils.isNotEmpty( listAppointmentForm ) )
1361         {
1362             listAppointmentForm = listAppointmentForm
1363                     .stream( )
1364                     .filter(
1365                             a -> ( a.getDateStartValidity( ) != null )
1366                                     && ( a.getDateStartValidity( ).toLocalDate( ).isBefore( LocalDate.now( ) ) || a.getDateStartValidity( ).toLocalDate( )
1367                                             .equals( LocalDate.now( ) ) ) ).sorted( ( a1, a2 ) -> a1.getTitle( ).compareTo( a2.getTitle( ) ) )
1368                     .collect( Collectors.toList( ) );
1369         }
1370         List<String> icons = new ArrayList<String>( );
1371         for ( AppointmentFormDTO form : listAppointmentForm )
1372         {
1373             ImageResource img = form.getIcon( );
1374             if ( img == null || img.getImage( ) == null || StringUtils.isEmpty( img.getMimeType( ) ) || StringUtils.equals( img.getMimeType( ), MARK_ICON_NULL ) )
1375             {
1376                 icons.add( MARK_ICON_NULL );
1377             }
1378             else
1379             {
1380                 byte [ ] imgBytesAsBase64 = Base64.encodeBase64( img.getImage( ) );
1381                 String imgDataAsBase64 = new String( imgBytesAsBase64 );
1382                 String strMimeType = img.getMimeType( );
1383                 String imgAsBase64 = MARK_DATA + MARK_COLON + strMimeType + MARK_SEMI_COLON + MARK_BASE_64 + MARK_COMMA + imgDataAsBase64;
1384                 icons.add( imgAsBase64 );
1385             }
1386         }
1387         model.put( MARK_ICONS, icons );
1388         model.put( MARK_FORM_LIST, listAppointmentForm );
1389         HtmlTemplate template = AppTemplateService.getTemplate( TEMPLATE_APPOINTMENT_FORM_LIST, locale, model );
1390         return template.getHtml( );
1391     }
1392 
1393     /**
1394      * Get the captcha security service
1395      * 
1396      * @return The captcha security service
1397      */
1398     private CaptchaSecurityService getCaptchaService( )
1399     {
1400         if ( _captchaSecurityService == null )
1401         {
1402             _captchaSecurityService = new CaptchaSecurityService( );
1403         }
1404 
1405         return _captchaSecurityService;
1406     }
1407 
1408     /**
1409      * Get the URL
1410      * 
1411      * @param request
1412      *            Get the URL to cancel an appointment in FO
1413      * @param appointment
1414      *            The appointment
1415      * @return The URL to cancel the appointment
1416      */
1417     public static String getCancelAppointmentUrl( HttpServletRequest request, Appointment appointment )
1418     {
1419         UrlItem urlItem = new UrlItem( AppPathService.getProdUrl( request ) + AppPathService.getPortalUrl( ) );
1420         urlItem.addParameter( MVCUtils.PARAMETER_PAGE, XPAGE_NAME );
1421         urlItem.addParameter( MVCUtils.PARAMETER_VIEW, VIEW_GET_VIEW_CANCEL_APPOINTMENT );
1422         urlItem.addParameter( PARAMETER_REF_APPOINTMENT, appointment.getReference( ) );
1423         return urlItem.getUrl( );
1424     }
1425 
1426     /**
1427      * Get the URL
1428      * 
1429      * @param request
1430      *            Get the URL to cancel an appointment in FO
1431      * @param appointment
1432      *            The appointment
1433      * @return The URL to cancel the appointment
1434      */
1435     public static String getCancelAppointmentUrl( Appointment appointment )
1436     {
1437         UrlItem urlItem = new UrlItem( AppPathService.getProdUrl( StringUtils.EMPTY ) + AppPathService.getPortalUrl( ) );
1438         urlItem.addParameter( MVCUtils.PARAMETER_PAGE, XPAGE_NAME );
1439         urlItem.addParameter( MVCUtils.PARAMETER_VIEW, VIEW_GET_VIEW_CANCEL_APPOINTMENT );
1440         urlItem.addParameter( PARAMETER_REF_APPOINTMENT, appointment.getReference( ) );
1441         return urlItem.getUrl( );
1442     }
1443 
1444     /**
1445      * Set the user infos to the appointment DTO
1446      * 
1447      * @param request
1448      *            the request
1449      * @param appointment
1450      *            the appointment DTO
1451      */
1452     public void setUserInfo( HttpServletRequest request, AppointmentDTO appointment )
1453     {
1454         if ( SecurityService.isAuthenticationEnable( ) && ( appointment != null ) )
1455         {
1456             LuteceUser user = SecurityService.getInstance( ).getRegisteredUser( request );
1457 
1458             if ( user != null )
1459             {
1460                 appointment.setGuid( user.getUserInfo( AppPropertiesService.getProperty( PROPERTY_USER_ATTRIBUTE_GUID, StringUtils.EMPTY ) ) );
1461                 appointment.setFirstName( user.getUserInfo( AppPropertiesService.getProperty( PROPERTY_USER_ATTRIBUTE_FIRST_NAME, StringUtils.EMPTY ) ) );
1462                 appointment.setEmail( user.getUserInfo( AppPropertiesService.getProperty( PROPERTY_USER_ATTRIBUTE_EMAIL, StringUtils.EMPTY ) ) );
1463                 String lastName = user.getUserInfo( AppPropertiesService.getProperty( PROPERTY_USER_ATTRIBUTE_PREFERED_NAME, StringUtils.EMPTY ) );
1464                 if ( ( lastName == null ) || lastName.isEmpty( ) )
1465                 {
1466                     lastName = user.getUserInfo( AppPropertiesService.getProperty( PROPERTY_USER_ATTRIBUTE_LAST_NAME, StringUtils.EMPTY ) );
1467                 }
1468                 appointment.setLastName( lastName );
1469             }
1470         }
1471     }
1472 
1473     /**
1474      * check if authentication
1475      * 
1476      * @param form
1477      *            Form
1478      * @param request
1479      *            HttpServletRequest
1480      * @throws UserNotSignedException
1481      *             exception if the form requires an authentication and the user is not logged
1482      */
1483     private void checkMyLuteceAuthentication( AppointmentFormDTO form, HttpServletRequest request ) throws UserNotSignedException
1484     {
1485         // Try to register the user in case of external authentication
1486         if ( SecurityService.isAuthenticationEnable( ) )
1487         {
1488             if ( SecurityService.getInstance( ).isExternalAuthentication( ) )
1489             {
1490                 // The authentication is external
1491                 // Should register the user if it's not already done
1492                 if ( SecurityService.getInstance( ).getRegisteredUser( request ) == null )
1493                 {
1494                     if ( ( SecurityService.getInstance( ).getRemoteUser( request ) == null ) && ( form.getActiveAuthentication( ) ) )
1495                     {
1496                         // Authentication is required to access to the portal
1497                         throw new UserNotSignedException( );
1498                     }
1499                 }
1500             }
1501             else
1502             {
1503                 // If portal authentication is enabled and user is null and the
1504                 // requested URL
1505                 // is not the login URL, user cannot access to Portal
1506                 if ( ( form.getActiveAuthentication( ) ) && ( SecurityService.getInstance( ).getRegisteredUser( request ) == null )
1507                         && !SecurityService.getInstance( ).isLoginUrl( request ) )
1508                 {
1509                     // Authentication is required to access to the portal
1510                     throw new UserNotSignedException( );
1511                 }
1512             }
1513         }
1514     }
1515 
1516     /**
1517      * Fill the appointment form
1518      *
1519      * @param request
1520      *             HttpServletRequest
1521      * @return XPage
1522      * @throws UserNotSignedException
1523      */
1524     @Action( ACTION_DO_FILL_APPOINTMENT )
1525     public XPage doFillAppointement( HttpServletRequest request ) {
1526 
1527         String idForm = request.getParameter( "id_form" );
1528         String idFormUser = request.getParameter( "id_form_user" );
1529         String securityKey = request.getParameter( "security_key" );
1530 
1531         AppointmentFillDTO dto = new AppointmentFillDTO( idForm, idFormUser, securityKey );
1532 
1533         // Si pas de paramètres ou que la clé de sécurité n'est pas bonne on renvoie vers la page d'erreur
1534         if( StringUtils.isEmpty(dto.getIdForm()) || StringUtils.isEmpty(dto.getIdFormUser()) || StringUtils.isEmpty(dto.getSecurityKey()) ||
1535                 !AppointmentService.checkSecurityKey(  dto.getIdForm(),  dto.getIdFormUser(),  dto.getSecurityKey(), idEntryKiosque, idEntryCleSecurite ))
1536         {
1537             return getAppointmentErrorView(request, ERROR_MESSAGE_CLE_INCORRECTE);
1538         }
1539 
1540         // Récupération des données du porteur de projet à partir de son id_form
1541         HashMap<String,String> parametersMap = AppointmentService.getAppointmentFillParameters(Integer.valueOf( idForm ), idFormUser, idEntryEmail);
1542 
1543         // Si pas de donnée remontée
1544         if(null == parametersMap || parametersMap.isEmpty( )){
1545             return getAppointmentErrorView(request, ERROR_MESSAGE_CLE_INCORRECTE);
1546         }
1547 
1548         // Construction de l'url de redirection
1549         String urlRedirect = URL_FILL_APPOINTMENT +idForm;
1550 
1551         // Récupération de toutes les données avec leur champs associé pour construire l'url
1552         for(java.util.Map.Entry<String, String> entry : parametersMap.entrySet( )) {
1553             String attributeId = entry.getKey();
1554             String value = entry.getValue();
1555 
1556             if(attributeId.equals(String.valueOf(idEntryEmail))){
1557                 urlRedirect =  urlRedirect + EMAIL + EQUALS + value;
1558             } else {
1559                 try
1560                 {
1561                     urlRedirect = urlRedirect + ATTRIBUTE + attributeId + EQUALS + URLEncoder.encode( value , CharEncoding.UTF_8 );
1562                 } catch ( UnsupportedEncodingException e )
1563                 {
1564                     urlRedirect = urlRedirect + ATTRIBUTE + attributeId + EQUALS + value;
1565                 }
1566             }
1567         }
1568 
1569         saveAppointmentFillDTO( request, dto );
1570 
1571         return redirect( request, urlRedirect );
1572 
1573     }
1574 
1575     /**
1576      * Display the recap before validating an appointment
1577      *
1578      * @param request
1579      *            The request
1580      * @param messageKey
1581      *            The message key
1582      * @return The HTML content to display or the next URL to redirect to
1583      */
1584     @View( VIEW_GET_APPOINTMENT_ERROR )
1585     public XPage getAppointmentErrorView( HttpServletRequest request, String messageKey )
1586     {
1587         Locale locale = getLocale( request );
1588 
1589         Map<String, Object> model = new HashMap<String, Object>( );
1590 
1591         model.put( MARK_ERROR,  I18nService.getLocalizedString( messageKey, locale ) );
1592 
1593         fillCommons( model );
1594 
1595         return getXPage( TEMPLATE_ERRROR_PAGE, getLocale( request ), model );
1596     }
1597 
1598     /**
1599      * Return the actual AppointmentFillDTO object
1600      *
1601      * @param request
1602      *              {@link HttpServletRequest}
1603      * @return {@link AppointmentFillDTO}
1604      */
1605     protected AppointmentFillDTO getAppointmentFillDTO( HttpServletRequest request )
1606     {
1607         if ( request.getSession( ).getAttribute( APPOINTMENT_FILL_DTO ) != null )
1608         {
1609             return (AppointmentFillDTO) request.getSession( ).getAttribute( APPOINTMENT_FILL_DTO );
1610         } else
1611         {
1612             return null;
1613         }
1614     }
1615 
1616     /**
1617      * Save the actual AppointmentFillDTO object
1618      *
1619      * @param request
1620      *              {@link HttpServletRequest}
1621      * @param appointmentFillDTO
1622      *              {@link AppointmentFillDTO}
1623      */
1624     protected void saveAppointmentFillDTO( HttpServletRequest request, AppointmentFillDTO appointmentFillDTO )
1625     {
1626         request.getSession( ).removeAttribute( APPOINTMENT_ID_SLOT );
1627         request.getSession( ).setAttribute( APPOINTMENT_FILL_DTO, appointmentFillDTO );
1628     }
1629 
1630 }