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.stock.modules.billetterie.web;
35  
36  import fr.paris.lutece.plugins.stock.business.product.Product;
37  import fr.paris.lutece.plugins.stock.business.purchase.PurchaseFilter;
38  import fr.paris.lutece.plugins.stock.business.purchase.exception.PurchaseUnavailable;
39  import fr.paris.lutece.plugins.stock.commons.exception.BusinessException;
40  import fr.paris.lutece.plugins.stock.commons.exception.FunctionnalException;
41  import fr.paris.lutece.plugins.stock.commons.exception.TechnicalException;
42  import fr.paris.lutece.plugins.stock.modules.billetterie.utils.constants.BilletterieConstants;
43  import fr.paris.lutece.plugins.stock.modules.tickets.business.NotificationDTO;
44  import fr.paris.lutece.plugins.stock.modules.tickets.business.ReservationDTO;
45  import fr.paris.lutece.plugins.stock.modules.tickets.business.SeanceDTO;
46  import fr.paris.lutece.plugins.stock.modules.tickets.business.UnauthentifiedPurchaserDTO;
47  import fr.paris.lutece.plugins.stock.modules.tickets.service.INotificationService;
48  import fr.paris.lutece.plugins.stock.modules.tickets.service.IPurchaseService;
49  import fr.paris.lutece.plugins.stock.modules.tickets.service.ISeanceService;
50  import fr.paris.lutece.plugins.stock.modules.tickets.service.PurchaseService;
51  import fr.paris.lutece.plugins.stock.modules.tickets.utils.constants.TicketsConstants;
52  import fr.paris.lutece.plugins.stock.service.IPurchaseSessionManager;
53  import fr.paris.lutece.plugins.stock.utils.DateUtils;
54  import fr.paris.lutece.portal.business.user.AdminUser;
55  import fr.paris.lutece.portal.business.user.AdminUserHome;
56  import fr.paris.lutece.portal.service.i18n.I18nService;
57  import fr.paris.lutece.portal.service.message.SiteMessage;
58  import fr.paris.lutece.portal.service.message.SiteMessageException;
59  import fr.paris.lutece.portal.service.message.SiteMessageService;
60  import fr.paris.lutece.portal.service.plugin.Plugin;
61  import fr.paris.lutece.portal.service.security.LuteceUser;
62  import fr.paris.lutece.portal.service.security.UserNotSignedException;
63  import fr.paris.lutece.portal.service.spring.SpringContextService;
64  import fr.paris.lutece.portal.service.template.AppTemplateService;
65  import fr.paris.lutece.portal.service.util.AppPathService;
66  import fr.paris.lutece.portal.service.util.AppPropertiesService;
67  import fr.paris.lutece.portal.web.xpages.XPage;
68  import fr.paris.lutece.portal.web.xpages.XPageApplication;
69  import fr.paris.lutece.util.html.HtmlTemplate;
70  import fr.paris.lutece.util.html.Paginator;
71  import fr.paris.lutece.util.url.UrlItem;
72  
73  import org.apache.commons.collections.CollectionUtils;
74  import org.apache.commons.lang.StringUtils;
75  import org.apache.log4j.Logger;
76  
77  import java.io.UnsupportedEncodingException;
78  import java.net.URLEncoder;
79  import java.sql.Timestamp;
80  import java.util.ArrayList;
81  import java.util.Collection;
82  import java.util.Date;
83  import java.util.HashMap;
84  import java.util.List;
85  import java.util.Locale;
86  import java.util.Map;
87  import java.util.UUID;
88  
89  import javax.servlet.http.HttpServletRequest;
90  import javax.servlet.http.HttpServletResponse;
91  
92  /**
93   * Pages for billetterie front
94   *
95   */
96  public class StockBilletterieReservationApp extends AbstractXPageApp implements XPageApplication
97  {
98      public static final Logger LOGGER = Logger.getLogger( StockBilletterieReservationApp.class );
99      private static final long serialVersionUID = -3160877399020602862L;
100     private static final String PAGE_BOOKING = "reservation";
101     private static final String PAGE_TICKETING = "billetterie";
102     private static final String JSP_DO_DELETE_RESERVATION = "jsp/site/plugins/stock/modules/billetterie/DoDeleteReservation.jsp";
103     private static final String JSP_PORTAL = "jsp/site/Portal.jsp";
104     private static final String PROPERTY_NOTIFICATION_REQUEST_RECIPIENT = "stock-billetterie.mail.senderEmail";
105     private static final String PROPERTY_DISABLE_ACTION_MODIFY_BOOKING = "stock-billetterie.disable.modify.booking";
106 
107     // I18n
108     private static final String TITLE_MY_BOOKINGS = "module.stock.billetterie.my_bookings.title";
109     private static final String TITLE_CONFIRM_BOOKING = "module.stock.billetterie.confirm_booking.title";
110     private static final String MESSAGE_NB_PLACES_INVALID = "module.stock.billetterie.message.error.invalid_nb_places";
111     private static final String MESSAGE_INSUFFICIENT_PLACE_REMAINING = "module.stock.billetterie.message.error.insufficient_place_remaining";
112     private static final String MESSAGE_CONFIRM_DELETE_PURCHASE_TITLE = "module.stock.billetterie.message.confirm.delete.purchase.title";
113     private static final String MESSAGE_CONFIRM_DELETE_PURCHASE = "module.stock.billetterie.message.confirm.delete.purchase";
114     private static final String MESSAGE_NOTIFICATION_BOOKING_SUBJECT = "module.stock.billetterie.notification.booking.subject";
115     private static final String MESSAGE_NOTIFICATION_REQUEST_SUBJECT = "module.stock.billetterie.notification.request.subject";
116     private static final String MESSAGE_CAUTION_TIME_PURCHASE = "module.stock.billetterie.message.caution.time.max";
117     private static final String MESSAGE_CAUTION_TIME_PURCHASE_PLURAL = "module.stock.billetterie.message.caution.time.max.plural";
118     private static final String MESSAGE_NOTIFICATION_ADMIN_OFFER_QUANTITY_SUBJECT = "module.stock.billetterie.notification.admin.offer.quantity.subject";
119 
120     // Parameters
121     private static final String PARAMETER_SEANCE_DATE = "seance_date";
122     private static final String PARAMETER_SHOW_NAME = "show_name";
123     private static final String PARAMETER_SHOW_ID = "show_id";
124     private static final String PARAMETER_NEXT_BOOKING_LIST = "nextBookingList";
125     private static final String PARAMETER_BOOKING_LIST = "bookingList";
126     private static final String PARAMETER_PAST_BOOKING_LIST = "pastBookingList";
127     private static final String PARAMETER_NB_PLACES = "nb_places";
128     private static final String PARAMETER_SEANCE_TYPE_NAME = "seance_type_name";
129     private static final String PARAMETER_SEANCE_ID = "seance_id";
130     private static final String PARAMETER_PURCHASE_ID = "purchase_id";
131     private static final String PARAMETER_ACTION = "action";
132     private static final String PARAMETER_PRODUCT_ID = "product_id";
133     private static final String PARAMETER_DATE_SEANCE = "date_seance";
134     private static final String PARAMETER_BOOKING_CHECK = "booking_check";
135     private static final String PARAMETER_AUTHENTIFIED_USER = "authentified_user";
136     private static final String PARAMETER_DATE_SCEANCE = "date_sceance";
137     private static final String PARAMETER_PAGE = "page";
138     private static final String PARAMETER_PAGE_INDEX = "page_index";
139     private static final String PARAMETER_TIME_MAX = AppPropertiesService.getProperty( "daemon.lock.session.time.expiration" );
140     private static final String PARAMETER_SUBSCRIBE = "subscribe";
141 
142     private static final String PARAMETER_TAB_ACTIVE = "tab_active";
143     private static final String CONSTANT_RESERVATION_TAB_ACTIVE = "reservation";
144     private static final String CONSTANT_SUBSCRIPTION_TAB_ACTIVE = "subscription";
145     private static final String MARK_LIST_PRODUCT_SUBSCRIBED = "list_product_subscribed";
146 
147     // Actions
148     private static final String ACTION_MY_BOOKINGS = "mes-reservations";
149     private static final String ACTION_BOOK = "reserver";
150     private static final String ACTION_DELETE_BOOKING = "delete-purchase";
151     private static final String ACTION_MODIFY_BOOKING = "modify-purchase";
152     private static final String ACTION_SHOW_DETAILS = "fiche-spectacle";
153     private static final String ACTION_DELETE_SUBSCRIPTION = "delete-subscription";
154 
155     // Marks
156     private static final String MARK_BASE_URL = "base_url";
157     private static final String MARK_USER = "user";
158     private static final String MARK_PURCHASER = "purchaser";
159     private static final String MARK_PAGINATOR = "paginator";
160     private static final String MARK_CAUTION_TIME_PURCHASE = "cautionTimePurchase";
161     private static final String MARK_SEANCE = "seance";
162     private static final String MARK_DISABLE_ACTION_MODIFY_BOOKING = "disable_modify_booking";
163 
164     // Templates
165     private static final String TEMPLATE_DIR = "skin/plugins/stock/modules/billetterie/";
166     private static final String TEMPLATE_NOTIFICATION_BOOKING = "notification_booking.html";
167     private static final String TEMPLATE_MY_BOOKINGS = "my_bookings.html";
168     private static final String TEMPLATE_MODIFY_BOOK = "modify_book.html";
169     private static final String TEMPLATE_CONFIRM_BOOKING = "confirm_booking.html";
170     private static final String TEMPLATE_NOTIFICATION_REQUEST = "notification_request.html";
171     private static final String TEMPLATE_NOTIFICATION_ADMIN_OFFER_QUANTITY = "notification_admin_offer_quantity.html";
172 
173     private static final String ENCODING_UTF_8 = "utf-8";
174     private final ISeanceService _offerService = SpringContextService.getContext( ).getBean( ISeanceService.class );
175     private final IPurchaseService _purchaseService = SpringContextService.getContext( ).getBean( IPurchaseService.class );
176     private final INotificationService _notificationService = SpringContextService.getContext( ).getBean( INotificationService.class );
177     private final IPurchaseSessionManager _purchaseSessionManager = SpringContextService.getContext( ).getBean( IPurchaseSessionManager.class );
178 
179     /**
180      * Return page with action specified.
181      *
182      * @param request
183      *            the request
184      * @param nMode
185      *            the n mode
186      * @param plugin
187      *            the plugin
188      * @return the page
189      * @throws UserNotSignedException
190      *             the user not signed exception
191      * @throws SiteMessageException
192      *             the site message exception
193      */
194     public XPage getPage( HttpServletRequest request, int nMode, Plugin plugin ) throws UserNotSignedException, SiteMessageException
195     {
196         XPage page = new XPage( );
197         Locale locale = request.getLocale( );
198 
199         String strAction = request.getParameter( PARAMETER_ACTION );
200 
201         if ( ACTION_BOOK.equals( strAction ) )
202         {
203             page = getConfirmBooking( page, request, locale );
204         }
205         else
206             if ( ACTION_MY_BOOKINGS.equals( strAction ) )
207             {
208                 request.setAttribute( PARAMETER_TAB_ACTIVE, CONSTANT_RESERVATION_TAB_ACTIVE );
209                 String strContent = getMyBookings( request, locale );
210                 page.setContent( strContent );
211 
212                 String pageTitle = getMessage( TITLE_MY_BOOKINGS, request );
213                 page.setPathLabel( pageTitle );
214                 page.setTitle( pageTitle );
215 
216             }
217             else
218                 if ( ACTION_DELETE_BOOKING.equals( strAction ) )
219                 {
220                     page = getDeleteBooking( page, request, locale );
221                 }
222                 else
223                     if ( ACTION_DELETE_SUBSCRIPTION.equals( strAction ) )
224                     {
225                         deleteSubscription( page, request, locale );
226 
227                         String strContent = getMyBookings( request, locale );
228                         page.setContent( strContent );
229 
230                         String pageTitle = getMessage( TITLE_MY_BOOKINGS, request );
231                         page.setPathLabel( pageTitle );
232                         page.setTitle( pageTitle );
233                     }
234                     else
235                         if ( ACTION_MODIFY_BOOKING.equals( strAction ) )
236                         {
237 
238                         }
239 
240         return page;
241     }
242 
243     /**
244      * Returns xpage for confirm booking.
245      *
246      * @param page
247      *            xpage
248      * @param request
249      *            http request
250      * @param locale
251      *            locale
252      * @return xpage
253      * @throws SiteMessageException
254      *             Confirm booking page cannot be shown
255      */
256     private XPage getConfirmBooking( XPage page, HttpServletRequest request, Locale locale ) throws SiteMessageException
257     {
258         String [ ] seanceIdList = request.getParameterValues( PARAMETER_SEANCE_ID );
259         String [ ] seanceTypeNameList = request.getParameterValues( PARAMETER_SEANCE_TYPE_NAME );
260         String [ ] numberPlacesList = request.getParameterValues( PARAMETER_NB_PLACES );
261         String showId = request.getParameter( PARAMETER_SHOW_ID );
262 
263         Map<String, Object> model = new HashMap<String, Object>( );
264         model.put( PARAMETER_SHOW_ID, showId );
265 
266         FunctionnalException fe = getErrorOnce( request );
267 
268         List<ReservationDTO> bookingList = (List<ReservationDTO>) request.getSession( ).getAttribute( PARAMETER_BOOKING_LIST );
269 
270         String bookingCheck;
271         boolean bAuthentified = false;
272         UnauthentifiedPurchaserDTO purchaser = null;
273 
274         if ( fe == null )
275         {
276             // If user has already a booking in waiting state, remove it
277             if ( bookingList != null )
278             {
279                 for ( ReservationDTO reservation : bookingList )
280                 {
281                     _purchaseSessionManager.release( request.getSession( ).getId( ), reservation );
282                 }
283             }
284 
285             // Set user informations
286             LuteceUser user = this.getUser( request );
287 
288             // Create booking list
289             boolean bPlacesInvalid = true;
290             bookingList = new ArrayList<ReservationDTO>( );
291             // Avoid mixing purchase session (with two opened tabs for example)
292             bookingCheck = UUID.randomUUID( ).toString( );
293 
294             try
295             {
296                 int i = 0;
297                 ReservationDTO booking;
298                 int nbPlaces;
299 
300                 if ( seanceIdList != null )
301                 {
302                     for ( String seanceId : seanceIdList )
303                     {
304                         // Check validity of parameter
305                         if ( StringUtils.isNumeric( numberPlacesList [i] ) && ( Integer.valueOf( numberPlacesList [i] ) > 0 ) )
306                         {
307                             bPlacesInvalid = false;
308 
309                             // Create booking object
310                             nbPlaces = Integer.valueOf( numberPlacesList [i] );
311 
312                             if ( nbPlaces > 0 )
313                             {
314                                 booking = new ReservationDTO( );
315                                 booking.getOffer( ).setId( Integer.valueOf( seanceId ) );
316                                 booking.getOffer( ).setTypeName( seanceTypeNameList [i] );
317                                 booking.setQuantity( nbPlaces );
318                                 booking.setDate( DateUtils.getCurrentDateString( ) );
319                                 booking.setHeure( DateUtils.getHourFr( DateUtils.getCurrentDate( ) ) );
320 
321                                 if ( user != null )
322                                 {
323                                     String strEmail = !user.getUserInfo( LuteceUser.HOME_INFO_ONLINE_EMAIL ).equals( "" ) ? user
324                                             .getUserInfo( LuteceUser.HOME_INFO_ONLINE_EMAIL ) : user.getUserInfo( LuteceUser.BUSINESS_INFO_ONLINE_EMAIL );// BUSINESS_INFO_ONLINE_EMAIL
325                                     booking.setUserName( user.getName( ) );
326                                     // booking.setUserName( strEmail );
327                                     booking.setEmailAgent( strEmail );
328                                     booking.setFirstNameAgent( user.getUserInfo( LuteceUser.NAME_GIVEN ) );
329                                     booking.setNameAgent( user.getUserInfo( LuteceUser.NAME_FAMILY ) );
330 
331                                     bAuthentified = true;
332 
333                                     // Reserve tickets
334                                     try
335                                     {
336                                         _purchaseSessionManager.reserve( request.getSession( ).getId( ), booking );
337                                     }
338                                     catch( PurchaseUnavailable e )
339                                     {
340                                         throw new BusinessException( null, MESSAGE_INSUFFICIENT_PLACE_REMAINING );
341                                     }
342                                 }
343 
344                                 bookingList.add( booking );
345                             }
346                         }
347 
348                         i++;
349                     }
350 
351                     if ( bPlacesInvalid )
352                     {
353                         throw new BusinessException( null, MESSAGE_NB_PLACES_INVALID );
354                     }
355                 }
356 
357                 // Save booking into session
358                 request.getSession( ).setAttribute( PARAMETER_BOOKING_LIST, bookingList );
359                 request.getSession( ).setAttribute( PARAMETER_BOOKING_CHECK, bookingCheck );
360             }
361             catch( BusinessException e )
362             {
363                 String htmlError = getHtmlError( e, request );
364                 model.put( BilletterieConstants.ERROR, htmlError );
365 
366                 UrlItem targetUrl = new UrlItem( JSP_PORTAL );
367                 targetUrl.addParameter( PARAMETER_PAGE, PAGE_TICKETING );
368                 targetUrl.addParameter( PARAMETER_ACTION, ACTION_SHOW_DETAILS );
369                 targetUrl.addParameter( PARAMETER_PRODUCT_ID, showId );
370                 SiteMessageService.setMessage( request, e.getCode( ), SiteMessage.TYPE_STOP, targetUrl.getUrl( ) );
371             }
372         }
373 
374         // Manage errors
375         else
376         {
377             model.put( TicketsConstants.PARAMETER_ERROR, getHtmlError( fe, request ) );
378             bookingCheck = (String) request.getSession( ).getAttribute( PARAMETER_BOOKING_CHECK );
379             bAuthentified = StringUtils.isNotBlank( bookingList.get( 0 ).getEmailAgent( ) );
380 
381             // try to retrieve DTO if unauthentied user
382             Object errorBean = fe.getBean( );
383 
384             if ( !bAuthentified && ( errorBean != null ) && errorBean instanceof UnauthentifiedPurchaserDTO )
385             {
386                 purchaser = (UnauthentifiedPurchaserDTO) errorBean;
387             }
388         }
389 
390         // Generates template
391         String showName = request.getParameter( PARAMETER_SHOW_NAME );
392 
393         model.put( PARAMETER_BOOKING_LIST, bookingList );
394         model.put( PARAMETER_SEANCE_DATE, request.getParameter( PARAMETER_SEANCE_DATE ) );
395         model.put( PARAMETER_SHOW_NAME, showName );
396         model.put( PARAMETER_BOOKING_CHECK, bookingCheck );
397         model.put( PARAMETER_AUTHENTIFIED_USER, bAuthentified );
398 
399         int timeMax = 0;
400 
401         try
402         {
403             timeMax = Integer.parseInt( PARAMETER_TIME_MAX );
404         }
405         catch( NumberFormatException e )
406         {
407             LOGGER.error( "Erreur de temps maximum avant suppression de la reservation en session : " + e );
408         }
409 
410         String localizedString = I18nService.getLocalizedString( ( timeMax >= 2 ) ? MESSAGE_CAUTION_TIME_PURCHASE_PLURAL : MESSAGE_CAUTION_TIME_PURCHASE,
411                 new String [ ] {
412                     PARAMETER_TIME_MAX
413                 }, locale );
414         model.put( MARK_CAUTION_TIME_PURCHASE, localizedString );
415 
416         // Add DTO when unauthentified
417         if ( !bAuthentified )
418         {
419             model.put( MARK_PURCHASER, ( purchaser != null ) ? purchaser : new UnauthentifiedPurchaserDTO( ) );
420         }
421 
422         HtmlTemplate template = AppTemplateService.getTemplate( TEMPLATE_DIR + TEMPLATE_CONFIRM_BOOKING, locale, model );
423 
424         page.setContent( template.getHtml( ) );
425 
426         String pageTitle = getMessage( TITLE_CONFIRM_BOOKING, request, showName );
427         page.setPathLabel( pageTitle );
428         page.setTitle( pageTitle );
429 
430         return page;
431     }
432 
433     /**
434      * Action for saving booking. Called by JSP.
435      *
436      * @param request
437      *            http request
438      * @param response
439      *            http response
440      * @return url to go
441      * @throws SiteMessageException
442      *             the site message exception
443      */
444     public String doSaveReservation( HttpServletRequest request, HttpServletResponse response ) throws SiteMessageException
445     {
446         List<String> strPurchaseId = (List<String>) request.getSession( ).getAttribute( PARAMETER_PURCHASE_ID );
447         if ( CollectionUtils.isNotEmpty( strPurchaseId ) )
448         {
449             strPurchaseId.stream( ).map( Integer::valueOf ).forEach( _purchaseService::doDeletePurchase );
450         }
451 
452         String returnUrl = null;
453 
454         // Check mixing booking (with two tabs and two booking opened
455         if ( ( request.getParameter( PARAMETER_BOOKING_CHECK ) == null )
456                 || !request.getParameter( PARAMETER_BOOKING_CHECK ).equals( request.getSession( ).getAttribute( PARAMETER_BOOKING_CHECK ) ) )
457         {
458             SiteMessageService.setMessage( request, PurchaseService.MESSAGE_ERROR_PURCHASE_SESSION_EXPIRED, SiteMessage.TYPE_ERROR, JSP_PORTAL );
459         }
460         else
461         {
462             List<ReservationDTO> bookingList = (List<ReservationDTO>) request.getSession( ).getAttribute( PARAMETER_BOOKING_LIST );
463 
464             // Check booked quantity is available
465             for ( ReservationDTO booking : bookingList )
466             {
467                 SeanceDTO seance = _offerService.findSeanceById( bookingList.get( 0 ).getOffer( ).getId( ) );
468 
469                 if ( booking.getQuantity( ) > seance.getQuantity( ) )
470                 {
471                     UrlItem targetUrl = new UrlItem( AppPathService.getBaseUrl( request ) + JSP_PORTAL );
472                     targetUrl.addParameter( PARAMETER_PAGE, PAGE_TICKETING );
473                     targetUrl.addParameter( PARAMETER_ACTION, ACTION_SHOW_DETAILS );
474                     targetUrl.addParameter( PARAMETER_PRODUCT_ID, seance.getProduct( ).getId( ) );
475 
476                     SiteMessageService.setMessage( request, PurchaseService.MESSAGE_ERROR_PURCHASE_QUANTITY_OFFER, SiteMessage.TYPE_ERROR, JSP_PORTAL );
477 
478                     return targetUrl.getUrl( );
479                 }
480             }
481 
482             if ( Boolean.valueOf( request.getParameter( PARAMETER_AUTHENTIFIED_USER ) ) )
483             {
484                 try
485                 {
486                     bookingList = _purchaseService.doSavePurchaseList( bookingList, request.getSession( ).getId( ) );
487                     sendBookingNotification( bookingList, request );
488 
489                     for ( ReservationDTO booking : bookingList )
490                     {
491                         sendNotificationToAdmins( request, booking );
492                     }
493 
494                     // Go to page "mes reservations"
495                     UrlItem url = new UrlItem( AppPathService.getBaseUrl( request ) + JSP_PORTAL );
496                     url.addParameter( PARAMETER_PAGE, PAGE_BOOKING );
497                     url.addParameter( PARAMETER_ACTION, ACTION_MY_BOOKINGS );
498                     returnUrl = url.getUrl( );
499                 }
500                 catch( FunctionnalException e )
501                 {
502                     if ( bookingList != null )
503                     {
504                         // If error we display show page
505                         SeanceDTO seance = _offerService.findSeanceById( bookingList.get( 0 ).getOffer( ).getId( ) );
506                         UrlItem targetUrl = new UrlItem( AppPathService.getBaseUrl( request ) + JSP_PORTAL );
507                         targetUrl.addParameter( PARAMETER_PAGE, PAGE_TICKETING );
508                         targetUrl.addParameter( PARAMETER_ACTION, ACTION_SHOW_DETAILS );
509                         targetUrl.addParameter( PARAMETER_PRODUCT_ID, seance.getProduct( ).getId( ) );
510 
511                         return manageFunctionnalException( request, e, targetUrl.getUrl( ) );
512                     }
513 
514                     SiteMessageService.setMessage( request, PurchaseService.MESSAGE_ERROR_PURCHASE_SESSION_EXPIRED, SiteMessage.TYPE_ERROR, JSP_PORTAL );
515                 }
516             }
517             else
518             {
519                 UnauthentifiedPurchaserDTO purchaser = new UnauthentifiedPurchaserDTO( );
520                 populate( purchaser, request );
521 
522                 try
523                 {
524                     validate( purchaser );
525                     sendRequestNotification( bookingList, purchaser, request );
526 
527                     // Go to portal
528                     UrlItem url = new UrlItem( AppPathService.getBaseUrl( request ) + JSP_PORTAL );
529                     returnUrl = url.getUrl( );
530                 }
531                 catch( FunctionnalException e )
532                 {
533                     UrlItem targetUrl = new UrlItem( AppPathService.getBaseUrl( request ) + JSP_PORTAL );
534                     targetUrl.addParameter( PARAMETER_PAGE, PAGE_BOOKING );
535                     targetUrl.addParameter( PARAMETER_ACTION, ACTION_BOOK );
536                     targetUrl.addParameter( PARAMETER_SHOW_ID, request.getParameter( PARAMETER_SHOW_ID ) );
537                     targetUrl.addParameter( PARAMETER_SHOW_NAME, request.getParameter( PARAMETER_SHOW_NAME ) );
538                     targetUrl.addParameter( PARAMETER_SEANCE_DATE, request.getParameter( PARAMETER_SEANCE_DATE ) );
539 
540                     return manageFunctionnalException( request, e, targetUrl.getUrl( ) );
541                 }
542             }
543         }
544 
545         return returnUrl;
546     }
547 
548     /**
549      * Send a notification to all the admins when all the tickets of an offer are booked
550      * 
551      * @param request
552      *            The HTTP request
553      * @param purchase
554      */
555     private void sendNotificationToAdmins( HttpServletRequest request, ReservationDTO purchase )
556     {
557         SeanceDTO seance = this._offerService.findSeanceById( purchase.getOffer( ).getId( ) );
558 
559         if ( seance != null && seance.getQuantity( ) < seance.getMinTickets( ) )
560         {
561             // Generate mail content
562             Map<String, Object> model = new HashMap<String, Object>( );
563             model.put( MARK_SEANCE, seance );
564             model.put( MARK_BASE_URL, AppPathService.getBaseUrl( request ) );
565 
566             HtmlTemplate template = AppTemplateService.getTemplate( TEMPLATE_NOTIFICATION_ADMIN_OFFER_QUANTITY, request.getLocale( ), model );
567 
568             Collection<AdminUser> listUsers = (List<AdminUser>) AdminUserHome.findUserList( );
569 
570             for ( AdminUser adminUser : listUsers )
571             {
572                 // Create mail object
573                 NotificationDTO notificationDTO = new NotificationDTO( );
574                 notificationDTO.setRecipientsTo( adminUser.getEmail( ) );
575 
576                 String [ ] args = new String [ ] {
577                         String.valueOf( seance.getId( ) ), seance.getProduct( ).getName( ), seance.getDate( ) + " " + seance.getHour( )
578                 };
579                 notificationDTO.setSubject( I18nService.getLocalizedString( MESSAGE_NOTIFICATION_ADMIN_OFFER_QUANTITY_SUBJECT, args, request.getLocale( ) ) );
580                 notificationDTO.setMessage( template.getHtml( ) );
581 
582                 // Send it
583                 _notificationService.send( notificationDTO );
584             }
585         }
586     }
587 
588     /**
589      * Action for cancel saving booking. Called by JSP.
590      *
591      * @param request
592      *            http request
593      * @param response
594      *            http response
595      * @return url to go
596      * @throws UnsupportedEncodingException
597      *             the unsupported encoding exception
598      */
599     public String doCancelSaveReservation( HttpServletRequest request, HttpServletResponse response ) throws UnsupportedEncodingException
600     {
601         List<ReservationDTO> bookingList = (List<ReservationDTO>) request.getSession( ).getAttribute( PARAMETER_BOOKING_LIST );
602         String seanceDate = request.getParameter( PARAMETER_DATE_SEANCE );
603         String showId = request.getParameter( PARAMETER_PRODUCT_ID );
604         Integer nShowId;
605 
606         try
607         {
608             nShowId = Integer.valueOf( showId );
609         }
610         catch( NumberFormatException e )
611         {
612             return AppPathService.getBaseUrl( request ) + JSP_PORTAL;
613         }
614 
615         UrlItem targetUrl = new UrlItem( AppPathService.getBaseUrl( request ) + JSP_PORTAL );
616         targetUrl.addParameter( PARAMETER_PAGE, PAGE_TICKETING );
617         targetUrl.addParameter( PARAMETER_ACTION, ACTION_SHOW_DETAILS );
618         targetUrl.addParameter( PARAMETER_PRODUCT_ID, nShowId );
619         targetUrl.addParameter( PARAMETER_DATE_SCEANCE, URLEncoder.encode( seanceDate, ENCODING_UTF_8 ) );
620 
621         if ( ( bookingList != null ) && !bookingList.isEmpty( ) )
622         {
623             try
624             {
625                 _purchaseService.doCancelPurchaseList( bookingList, request.getSession( ).getId( ) );
626             }
627             catch( FunctionnalException e )
628             {
629                 // If error we display show page
630                 return manageFunctionnalException( request, e, targetUrl.getUrl( ) );
631             }
632         }
633 
634         return targetUrl.getUrl( );
635     }
636 
637     /**
638      * Returns page for managing user bookings.
639      * 
640      * @param request
641      *            http request
642      * @param user
643      *            The user
644      * @param locale
645      *            local
646      * @return The content to display
647      * @throws UserNotSignedException
648      *             If the user has not signed in
649      */
650     public static String getMyBookings( HttpServletRequest request,  Locale locale ) throws SiteMessageException
651     {
652 
653         LuteceUser user = getLuteceUserAuthentication( request );
654 
655         // Get user bookings
656         Date today = new Date( );
657         PurchaseFilter purchaseFilterUserName = new PurchaseFilter( );
658         PurchaseFilter purchaseFilterEmail = new PurchaseFilter( );
659 
660         String strEmail = !user.getUserInfo( LuteceUser.HOME_INFO_ONLINE_EMAIL ).equals( "" ) ? user.getUserInfo( LuteceUser.HOME_INFO_ONLINE_EMAIL ) : user
661                 .getUserInfo( LuteceUser.BUSINESS_INFO_ONLINE_EMAIL );// BUSINESS_INFO_ONLINE_EMAIL
662         purchaseFilterEmail.setUserName( strEmail );
663         purchaseFilterEmail.setDateBeginOffer( new Timestamp( today.getTime( ) ) );
664 
665         purchaseFilterUserName.setUserName( user.getName( ) );
666         purchaseFilterUserName.setDateBeginOffer( new Timestamp( today.getTime( ) ) );
667 
668         IPurchaseService purchaseService = SpringContextService.getContext( ).getBean( IPurchaseService.class );
669 
670         // Dispatch booking list into two differents lists (old and to come)
671         List<ReservationDTO> toComeBookingList = purchaseService.findByFilter( purchaseFilterUserName );
672         List<ReservationDTO> toComeBookingListEmail = purchaseService.findByFilter( purchaseFilterEmail );
673         for ( ReservationDTO reservationDTO : toComeBookingListEmail )
674         {
675             if ( !toComeBookingList.contains( reservationDTO ) )
676             {
677                 toComeBookingList.add( reservationDTO );
678             }
679         }
680 
681         SubscriptionProductJspBean jspBean = new SubscriptionProductJspBean( );
682         List<Product> listProduct = jspBean.getProductsSubscribedByUser( user );
683 
684         purchaseFilterUserName.setDateBeginOffer( null );
685         purchaseFilterUserName.setDateEndOffer( new Timestamp( today.getTime( ) ) );
686 
687         List<ReservationDTO> oldBookingList = purchaseService.findByFilter( purchaseFilterUserName, getPaginationProperties( request ) );
688 
689         // Generates template
690         Map<String, Object> model = new HashMap<String, Object>( );
691         model.put( PARAMETER_NEXT_BOOKING_LIST, toComeBookingList );
692         model.put( PARAMETER_PAST_BOOKING_LIST, oldBookingList );
693 
694         model.put( MARK_USER, user );
695 
696         model.put( MARK_LIST_PRODUCT_SUBSCRIBED, listProduct );
697         model.put( PARAMETER_TAB_ACTIVE, (String) request.getAttribute( PARAMETER_TAB_ACTIVE ) );
698 
699         String strNbItemPerPage = request.getParameter( PARAMETER_NB_ITEMS_PER_PAGE );
700         String strDefaultNbItemPerPage = DEFAULT_RESULTS_PER_PAGE;
701         strNbItemPerPage = ( strNbItemPerPage != null ) ? strNbItemPerPage : strDefaultNbItemPerPage;
702 
703         int nNbItemsPerPage = Integer.parseInt( strNbItemPerPage );
704         String strCurrentPageIndex = request.getParameter( PARAMETER_PAGE_INDEX );
705         strCurrentPageIndex = ( strCurrentPageIndex != null ) ? strCurrentPageIndex : DEFAULT_PAGE_INDEX;
706 
707         UrlItem url = new UrlItem( "jsp/site/Portal.jsp?page=reservation&action=mes-reservations" );
708 
709         Paginator<ReservationDTO> paginator = new Paginator<ReservationDTO>( oldBookingList, nNbItemsPerPage, url.getUrl( ), PARAMETER_PAGE_INDEX,
710                 strCurrentPageIndex );
711 
712         model.put( MARK_PAGINATOR, paginator );
713         model.put( MARK_DISABLE_ACTION_MODIFY_BOOKING, AppPropertiesService.getProperty( PROPERTY_DISABLE_ACTION_MODIFY_BOOKING ) );
714 
715         HtmlTemplate template = AppTemplateService.getTemplate( TEMPLATE_DIR + TEMPLATE_MY_BOOKINGS, locale, model );
716 
717         return template.getHtml( );
718     }
719 
720     /**
721      * Returns page for deleting user booking.
722      *
723      * @param page
724      *            xpage
725      * @param request
726      *            http request
727      * @param locale
728      *            local
729      * @return xpage
730      * @throws SiteMessageException
731      *             the site message exception
732      */
733     public XPage getDeleteBooking( XPage page, HttpServletRequest request, Locale locale ) throws SiteMessageException
734     {
735         String purchaseId = request.getParameter( PARAMETER_PURCHASE_ID );
736 
737         if ( StringUtils.isEmpty( purchaseId ) || !StringUtils.isNumeric( purchaseId ) )
738         {
739             throw new TechnicalException( "Paramètre " + PARAMETER_SEANCE_ID + " invalide   : " + purchaseId );
740         }
741 
742         Map<String, Object> model = new HashMap<String, Object>( );
743         model.put( PARAMETER_PURCHASE_ID, purchaseId );
744 
745         SiteMessageService.setMessage( request, MESSAGE_CONFIRM_DELETE_PURCHASE, null, MESSAGE_CONFIRM_DELETE_PURCHASE_TITLE, JSP_DO_DELETE_RESERVATION, null,
746                 SiteMessage.TYPE_CONFIRMATION, model );
747 
748         return null;
749     }
750 
751     /**
752      * Action for deleting booking. Called by JSP.
753      * 
754      * @param request
755      *            http request
756      * @param response
757      *            http response
758      * @return url to go
759      */
760     public String doDeleteReservation( HttpServletRequest request, HttpServletResponse response )
761     {
762         String purchaseId = request.getParameter( PARAMETER_PURCHASE_ID );
763 
764         if ( StringUtils.isEmpty( purchaseId ) || !StringUtils.isNumeric( purchaseId ) )
765         {
766             throw new TechnicalException( "Paramètre " + PARAMETER_SEANCE_ID + " invalide : " + purchaseId );
767         }
768 
769         _purchaseService.doDeletePurchase( Integer.valueOf( purchaseId ) );
770 
771         UrlItem returnUrl = new UrlItem( AppPathService.getBaseUrl( request ) + JSP_PORTAL );
772         returnUrl.addParameter( PARAMETER_PAGE, PAGE_BOOKING );
773         returnUrl.addParameter( PARAMETER_ACTION, ACTION_MY_BOOKINGS );
774 
775         return returnUrl.getUrl( );
776     }
777 
778     /**
779      * Send booking notification.
780      *
781      * @param bookingList
782      *            the booking list
783      * @param request
784      *            the request
785      * @return the notification dto
786      */
787     private NotificationDTO sendBookingNotification( List<ReservationDTO> bookingList, HttpServletRequest request )
788     {
789         // Generate mail content
790         Map<String, Object> model = new HashMap<String, Object>( );
791         model.put( PARAMETER_BOOKING_LIST, bookingList );
792         model.put( MARK_BASE_URL, AppPathService.getBaseUrl( request ) );
793 
794         HtmlTemplate template = AppTemplateService.getTemplate( TEMPLATE_DIR + TEMPLATE_NOTIFICATION_BOOKING, request.getLocale( ), model );
795 
796         // Create mail object
797         NotificationDTO notificationDTO = new NotificationDTO( );
798         ReservationDTO reservation = bookingList.get( 0 );
799         notificationDTO.setRecipientsTo( reservation.getEmailAgent( ) );
800         notificationDTO.setSubject( getMessage( MESSAGE_NOTIFICATION_BOOKING_SUBJECT, request, reservation.getOffer( ).getName( ) ) );
801         notificationDTO.setMessage( template.getHtml( ) );
802 
803         // Send it
804         _notificationService.send( notificationDTO );
805 
806         return notificationDTO;
807     }
808 
809     /**
810      * Send request notification
811      *
812      * @param bookingList
813      *            the booking list
814      * @param purchaser
815      *            the purchaser
816      * @param request
817      *            the request
818      * @return the notification dto
819      */
820     private NotificationDTO sendRequestNotification( List<ReservationDTO> bookingList, UnauthentifiedPurchaserDTO purchaser, HttpServletRequest request )
821     {
822         // Generate mail content
823         Map<String, Object> model = new HashMap<String, Object>( );
824         model.put( PARAMETER_BOOKING_LIST, bookingList );
825         model.put( MARK_PURCHASER, purchaser );
826         model.put( MARK_BASE_URL, AppPathService.getBaseUrl( request ) );
827         model.put( PARAMETER_SEANCE_DATE, request.getParameter( PARAMETER_SEANCE_DATE ) );
828 
829         String showName = request.getParameter( PARAMETER_SHOW_NAME );
830         model.put( PARAMETER_SHOW_NAME, showName );
831 
832         HtmlTemplate template = AppTemplateService.getTemplate( TEMPLATE_DIR + TEMPLATE_NOTIFICATION_REQUEST, request.getLocale( ), model );
833 
834         // Create mail object
835         NotificationDTO notificationDTO = new NotificationDTO( );
836         notificationDTO.setRecipientsTo( AppPropertiesService.getProperty( PROPERTY_NOTIFICATION_REQUEST_RECIPIENT ) );
837         notificationDTO.setSubject( getMessage( MESSAGE_NOTIFICATION_REQUEST_SUBJECT, request, showName ) );
838         notificationDTO.setMessage( template.getHtml( ) );
839 
840         // Send it
841         _notificationService.send( notificationDTO );
842 
843         return notificationDTO;
844     }
845 
846     /**
847      * Returns page for deleting user booking.
848      *
849      * @param page
850      *            xpage
851      * @param request
852      *            http request
853      * @param locale
854      *            local
855      * @return xpage
856      * @throws SiteMessageException
857      *             the site message exception
858      */
859     public void deleteSubscription( XPage page, HttpServletRequest request, Locale locale ) throws SiteMessageException
860     {
861         String strSubscribe = request.getParameter( PARAMETER_SUBSCRIBE );
862         request.setAttribute( PARAMETER_TAB_ACTIVE, CONSTANT_SUBSCRIPTION_TAB_ACTIVE );
863 
864         // Get the user
865         LuteceUser currentUser = getUser( request );
866 
867         SubscriptionProductJspBean jspBean = new SubscriptionProductJspBean( );
868 
869         if ( strSubscribe != null )
870         {
871             if ( strSubscribe.equals( "false" ) )
872             {
873                 jspBean.doUnsubscribeToProduct( request, currentUser );
874             }
875         }
876 
877     }
878 }