View Javadoc
1   /*
2    * Copyright (c) 2002-2021, City of Paris
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met:
8    *
9    *  1. Redistributions of source code must retain the above copyright notice
10   *     and the following disclaimer.
11   *
12   *  2. Redistributions in binary form must reproduce the above copyright notice
13   *     and the following disclaimer in the documentation and/or other materials
14   *     provided with the distribution.
15   *
16   *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
17   *     contributors may be used to endorse or promote products derived from
18   *     this software without specific prior written permission.
19   *
20   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
24   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30   * POSSIBILITY OF SUCH DAMAGE.
31   *
32   * License 1.0
33   */
34  package fr.paris.lutece.plugins.announce.service;
35  
36  import fr.paris.lutece.plugins.announce.business.Announce;
37  import fr.paris.lutece.plugins.announce.business.AnnounceDTO;
38  import fr.paris.lutece.plugins.announce.business.AnnounceHome;
39  import fr.paris.lutece.plugins.announce.business.Category;
40  import fr.paris.lutece.plugins.announce.business.Sector;
41  import fr.paris.lutece.plugins.announce.business.SectorHome;
42  import fr.paris.lutece.plugins.announce.service.upload.AnnounceAsynchronousUploadHandler;
43  import fr.paris.lutece.plugins.genericattributes.business.Entry;
44  import fr.paris.lutece.plugins.genericattributes.business.EntryFilter;
45  import fr.paris.lutece.plugins.genericattributes.business.EntryHome;
46  import fr.paris.lutece.plugins.genericattributes.business.Field;
47  import fr.paris.lutece.plugins.genericattributes.business.FieldHome;
48  import fr.paris.lutece.plugins.genericattributes.business.GenAttFileItem;
49  import fr.paris.lutece.plugins.genericattributes.business.GenericAttributeError;
50  import fr.paris.lutece.plugins.genericattributes.business.Response;
51  import fr.paris.lutece.plugins.genericattributes.service.entrytype.AbstractEntryTypeUpload;
52  import fr.paris.lutece.plugins.genericattributes.service.entrytype.EntryTypeServiceManager;
53  import fr.paris.lutece.plugins.genericattributes.service.entrytype.IEntryTypeService;
54  import fr.paris.lutece.portal.business.file.File;
55  import fr.paris.lutece.portal.business.file.FileHome;
56  import fr.paris.lutece.portal.business.physicalfile.PhysicalFile;
57  import fr.paris.lutece.portal.business.physicalfile.PhysicalFileHome;
58  import fr.paris.lutece.portal.service.content.XPageAppService;
59  import fr.paris.lutece.portal.service.security.LuteceUser;
60  import fr.paris.lutece.portal.service.security.SecurityService;
61  import fr.paris.lutece.portal.service.security.UserNotSignedException;
62  import fr.paris.lutece.portal.service.template.AppTemplateService;
63  import fr.paris.lutece.portal.service.util.AppPathService;
64  import fr.paris.lutece.portal.util.mvc.utils.MVCUtils;
65  import fr.paris.lutece.util.html.HtmlTemplate;
66  import fr.paris.lutece.util.url.UrlItem;
67  
68  import org.apache.commons.collections.CollectionUtils;
69  import org.apache.commons.fileupload.FileItem;
70  
71  import java.io.Serializable;
72  
73  import java.text.DateFormat;
74  import java.text.SimpleDateFormat;
75  
76  import java.util.ArrayList;
77  import java.util.HashMap;
78  import java.util.List;
79  import java.util.Locale;
80  import java.util.Map;
81  
82  import javax.servlet.http.HttpServletRequest;
83  
84  /**
85   * Service for announces
86   */
87  public class AnnounceService implements Serializable
88  {
89      /**
90       * Pattern for dates
91       */
92      public static final String PATTERN_DATE = "dd/MM/yyyy";
93  
94      /**
95       * Name of the bean of the service
96       */
97      public static final String BEAN_NAME = "announce.announceService";
98      private static final long serialVersionUID = 6197939507943704211L;
99      private static final String VIEW_GET_FORM = "viewForm";
100     private static final String PARAMETER_ID_CATEGORY = "id_form";
101     private static final String PREFIX_ATTRIBUTE = "attribute";
102 
103     // marks
104     private static final String MARK_LOCALE = "locale";
105     private static final String MARK_ENTRY = "entry";
106     private static final String MARK_FIELD = "field";
107     private static final String MARK_STR_LIST_CHILDREN = "str_list_entry_children";
108     private static final String MARK_CATEGORY = "category";
109     private static final String MARK_SECTOR = "sector";
110     private static final String MARK_STR_ENTRY = "str_entry";
111     private static final String MARK_USER = "user";
112     private static final String MARK_LIST_RESPONSES = "list_responses";
113     private static final String MARK_UPLOAD_HANDLER = "uploadHandler";
114 
115     // Templates
116     private static final String TEMPLATE_DIV_CONDITIONAL_ENTRY = "skin/plugins/announce/html_code_div_conditional_entry.html";
117     private static final String TEMPLATE_HTML_CODE_FORM = "skin/plugins/announce/html_code_form.html";
118     private static final String TEMPLATE_HTML_CODE_FORM_ADMIN = "admin/plugins/announce/html_code_form.html";
119 
120     /**
121      * Return the HTML code of the form
122      * 
123      * @param announce
124      *            The announce the get the HTML form of, or null to get a default form for the given category. The list of responses of the announce must have
125      *            been set if the announce is not null.
126      * @param category
127      *            the category to display the form of
128      * @param locale
129      *            the locale
130      * @param bDisplayFront
131      *            True if the entry will be displayed in Front Office, false if it will be displayed in Back Office.
132      * @param request
133      *            HttpServletRequest
134      * @return the HTML code of the form
135      */
136     public String getHtmlAnnounceForm( Announce announce, Category category, Locale locale, boolean bDisplayFront, HttpServletRequest request )
137     {
138         Map<String, Object> model = new HashMap<>( );
139         StringBuffer strBuffer = new StringBuffer( );
140         EntryFilter filter = new EntryFilter( );
141         filter.setIdResource( category.getId( ) );
142         filter.setResourceType( Category.RESOURCE_TYPE );
143         filter.setEntryParentNull( EntryFilter.FILTER_TRUE );
144         filter.setFieldDependNull( EntryFilter.FILTER_TRUE );
145 
146         AnnounceDTO announceDTO = null;
147 
148         if ( announce != null )
149         {
150             if ( ( announce.getListResponse( ) == null ) && ( announce.getId( ) > 0 ) )
151             {
152                 announce.setListResponse( AnnounceHome.findListResponse( announce.getId( ), true ) );
153             }
154 
155             announceDTO = new AnnounceDTO( announce );
156 
157             if ( announce.getListResponse( ) != null )
158             {
159                 for ( Response response : announce.getListResponse( ) )
160                 {
161                     if ( ( response.getFile( ) != null ) && ( response.getFile( ).getIdFile( ) > 0 ) )
162                     {
163                         File file = FileHome.findByPrimaryKey( response.getFile( ).getIdFile( ) );
164                         PhysicalFile physicalFile = PhysicalFileHome.findByPrimaryKey( file.getPhysicalFile( ).getIdPhysicalFile( ) );
165                         FileItem fileItem = new GenAttFileItem( physicalFile.getValue( ), file.getTitle( ) );
166                         AnnounceAsynchronousUploadHandler.getHandler( ).addFileItemToUploadedFilesList( fileItem,
167                                 IEntryTypeService.PREFIX_ATTRIBUTE + Integer.toString( response.getEntry( ).getIdEntry( ) ), request );
168                     }
169                 }
170 
171                 Map<Integer, List<Response>> mapResponsesByIdEntry = announceDTO.getMapResponsesByIdEntry( );
172 
173                 for ( Response response : announce.getListResponse( ) )
174                 {
175                     List<Response> listResponse = mapResponsesByIdEntry.get( response.getEntry( ).getIdEntry( ) );
176 
177                     if ( listResponse == null )
178                     {
179                         listResponse = new ArrayList<>( );
180                         mapResponsesByIdEntry.put( response.getEntry( ).getIdEntry( ), listResponse );
181                     }
182 
183                     listResponse.add( response );
184                 }
185             }
186         }
187 
188         List<Entry> listEntryFirstLevel = EntryHome.getEntryList( filter );
189 
190         for ( Entry entry : listEntryFirstLevel )
191         {
192             getHtmlEntry( announceDTO, entry.getIdEntry( ), strBuffer, locale, bDisplayFront, request );
193         }
194 
195         Sector sector = SectorHome.findByPrimaryKey( category.getIdSector( ) );
196 
197         model.put( MARK_CATEGORY, category );
198         model.put( MARK_SECTOR, sector );
199         model.put( MARK_STR_ENTRY, strBuffer.toString( ) );
200         model.put( MARK_LOCALE, locale );
201 
202         HtmlTemplate template = AppTemplateService.getTemplate( bDisplayFront ? TEMPLATE_HTML_CODE_FORM : TEMPLATE_HTML_CODE_FORM_ADMIN, locale, model );
203 
204         return template.getHtml( );
205     }
206 
207     /**
208      * Insert in the string buffer the content of the HTML code of the entry
209      * 
210      * @param announce
211      *            The announce to load current values, or null to use default values
212      * @param nIdEntry
213      *            the key of the entry which HTML code must be insert in the stringBuffer
214      * @param stringBuffer
215      *            the buffer which contains the HTML code
216      * @param locale
217      *            the locale
218      * @param bDisplayFront
219      *            True if the entry will be displayed in Front Office, false if it will be displayed in Back Office.
220      * @param request
221      *            HttpServletRequest
222      */
223     public void getHtmlEntry( AnnounceDTO announce, int nIdEntry, StringBuffer stringBuffer, Locale locale, boolean bDisplayFront, HttpServletRequest request )
224     {
225         Map<String, Object> model = new HashMap<>( );
226         StringBuilder strConditionalQuestionStringBuffer = null;
227         HtmlTemplate template;
228         Entry entry = EntryHome.findByPrimaryKey( nIdEntry );
229 
230         if ( Boolean.TRUE.equals( entry.getEntryType( ).getGroup( ) ) )
231         {
232             StringBuffer strGroupStringBuffer = new StringBuffer( );
233 
234             for ( Entry entryChild : entry.getChildren( ) )
235             {
236                 getHtmlEntry( announce, entryChild.getIdEntry( ), strGroupStringBuffer, locale, bDisplayFront, request );
237             }
238 
239             model.put( MARK_STR_LIST_CHILDREN, strGroupStringBuffer.toString( ) );
240         }
241         else
242         {
243             if ( entry.getNumberConditionalQuestion( ) != 0 )
244             {
245                 for ( Field field : entry.getFields( ) )
246                 {
247                     field.setConditionalQuestions( FieldHome.findByPrimaryKey( field.getIdField( ) ).getConditionalQuestions( ) );
248                 }
249             }
250         }
251 
252         if ( entry.getNumberConditionalQuestion( ) != 0 )
253         {
254             strConditionalQuestionStringBuffer = new StringBuilder( );
255 
256             for ( Field field : entry.getFields( ) )
257             {
258                 if ( CollectionUtils.isNotEmpty( field.getConditionalQuestions( ) ) )
259                 {
260                     StringBuffer strGroupStringBuffer = new StringBuffer( );
261 
262                     for ( Entry entryConditional : field.getConditionalQuestions( ) )
263                     {
264                         getHtmlEntry( announce, entryConditional.getIdEntry( ), strGroupStringBuffer, locale, bDisplayFront, request );
265                     }
266 
267                     model.put( MARK_STR_LIST_CHILDREN, strGroupStringBuffer.toString( ) );
268                     model.put( MARK_FIELD, field );
269                     template = AppTemplateService.getTemplate( TEMPLATE_DIV_CONDITIONAL_ENTRY, locale, model );
270                     strConditionalQuestionStringBuffer.append( template.getHtml( ) );
271                 }
272             }
273 
274             model.put( MARK_STR_LIST_CHILDREN, strConditionalQuestionStringBuffer.toString( ) );
275         }
276 
277         model.put( MARK_ENTRY, entry );
278         model.put( MARK_LOCALE, locale );
279 
280         LuteceUser user = SecurityService.getInstance( ).getRegisteredUser( request );
281 
282         if ( ( user == null ) && SecurityService.isAuthenticationEnable( ) && SecurityService.getInstance( ).isExternalAuthentication( ) )
283         {
284             try
285             {
286                 user = SecurityService.getInstance( ).getRemoteUser( request );
287             }
288             catch( UserNotSignedException e )
289             {
290                 // Nothing to do : lutece user is not mandatory
291             }
292         }
293 
294         model.put( MARK_USER, user );
295 
296         if ( ( announce != null ) && ( announce.getMapResponsesByIdEntry( ) != null ) )
297         {
298             List<Response> listResponses = announce.getMapResponsesByIdEntry( ).get( entry.getIdEntry( ) );
299             if ( listResponses != null )
300             {
301                 for ( Response response : listResponses )
302                 {
303                     for ( Field filed : entry.getFields( ) )
304                     {
305                         if ( response.getField( ) != null && filed.getIdField( ) == response.getField( ).getIdField( ) )
306                         {
307                             response.setField( filed );
308                         }
309                     }
310                 }
311             }
312 
313             model.put( MARK_LIST_RESPONSES, listResponses );
314         }
315 
316         IEntryTypeService entryTypeService = EntryTypeServiceManager.getEntryTypeService( entry );
317 
318         // If the entry type is a file, we add the
319         if ( entryTypeService instanceof AbstractEntryTypeUpload )
320         {
321             model.put( MARK_UPLOAD_HANDLER, ( (AbstractEntryTypeUpload) entryTypeService ).getAsynchronousUploadHandler( ) );
322         }
323 
324         template = AppTemplateService.getTemplate( EntryTypeServiceManager.getEntryTypeService( entry ).getTemplateHtmlForm( entry, bDisplayFront ), locale,
325                 model );
326         stringBuffer.append( template.getHtml( ) );
327     }
328 
329     /**
330      * Get the responses associated with an entry.<br />
331      * Return null if there is no error in the response, or return the list of errors Response created are stored the map of {@link AnnounceDTO}. The key of the
332      * map is this id of the entry, and the value the list of responses
333      * 
334      * @param request
335      *            the request
336      * @param nIdEntry
337      *            the key of the entry
338      * @param locale
339      *            the locale
340      * @param announce
341      *            The announce
342      * @return null if there is no error in the response or the list of errors found
343      */
344     public List<GenericAttributeError> getResponseEntry( HttpServletRequest request, int nIdEntry, Locale locale, AnnounceDTO announce )
345     {
346         List<Response> listResponse = new ArrayList<>( );
347         announce.getMapResponsesByIdEntry( ).put( nIdEntry, listResponse );
348 
349         return getResponseEntry( request, nIdEntry, listResponse, false, locale, announce );
350     }
351 
352     /**
353      * Get the responses associated with an entry.<br />
354      * Return null if there is no error in the response, or return the list of errors
355      * 
356      * @param request
357      *            the request
358      * @param nIdEntry
359      *            the key of the entry
360      * @param listResponse
361      *            The list of response to add responses found in
362      * @param bResponseNull
363      *            true if the response created must be null
364      * @param locale
365      *            the locale
366      * @param announce
367      *            The announce
368      * @return null if there is no error in the response or the list of errors found
369      */
370     private List<GenericAttributeError> getResponseEntry( HttpServletRequest request, int nIdEntry, List<Response> listResponse, boolean bResponseNull,
371             Locale locale, AnnounceDTO announce )
372     {
373         List<GenericAttributeError> listFormErrors = new ArrayList<>( );
374         Entry entry = EntryHome.findByPrimaryKey( nIdEntry );
375 
376         List<Field> listField = new ArrayList<>( );
377 
378         for ( Field field : entry.getFields( ) )
379         {
380             field = FieldHome.findByPrimaryKey( field.getIdField( ) );
381             listField.add( field );
382         }
383 
384         entry.setFields( listField );
385 
386         if ( Boolean.TRUE.equals( entry.getEntryType( ).getGroup( ) ) )
387         {
388             for ( Entry entryChild : entry.getChildren( ) )
389             {
390                 List<Response> listResponseChild = new ArrayList<>( );
391                 announce.getMapResponsesByIdEntry( ).put( entryChild.getIdEntry( ), listResponseChild );
392 
393                 listFormErrors.addAll( getResponseEntry( request, entryChild.getIdEntry( ), listResponseChild, false, locale, announce ) );
394             }
395         }
396         else
397             if ( !Boolean.TRUE.equals( entry.getEntryType( ).getComment( ) ) )
398             {
399                 GenericAttributeError formError = null;
400 
401                 if ( !bResponseNull )
402                 {
403                     formError = EntryTypeServiceManager.getEntryTypeService( entry ).getResponseData( entry, request, listResponse, locale );
404 
405                     if ( formError != null )
406                     {
407                         formError.setUrl( getEntryUrl( entry ) );
408                     }
409                 }
410                 else
411                 {
412                     Response response = new Response( );
413                     response.setEntry( entry );
414                     listResponse.add( response );
415                 }
416 
417                 if ( formError != null )
418                 {
419                     entry.setError( formError );
420                     listFormErrors.add( formError );
421                 }
422 
423                 if ( entry.getNumberConditionalQuestion( ) != 0 )
424                 {
425                     for ( Field field : entry.getFields( ) )
426                     {
427                         boolean bIsFieldInResponseList = isFieldInTheResponseList( field.getIdField( ), listResponse );
428 
429                         for ( Entry conditionalEntry : field.getConditionalQuestions( ) )
430                         {
431                             List<Response> listResponseChild = new ArrayList<>( );
432                             announce.getMapResponsesByIdEntry( ).put( conditionalEntry.getIdEntry( ), listResponseChild );
433 
434                             listFormErrors.addAll(
435                                     getResponseEntry( request, conditionalEntry.getIdEntry( ), listResponseChild, !bIsFieldInResponseList, locale, announce ) );
436                         }
437                     }
438                 }
439             }
440 
441         return listFormErrors;
442     }
443 
444     /**
445      * Check if a field is in a response list
446      * 
447      * @param nIdField
448      *            the id of the field to search
449      * @param listResponse
450      *            the list of responses
451      * @return true if the field is in the response list, false otherwise
452      */
453     public Boolean isFieldInTheResponseList( int nIdField, List<Response> listResponse )
454     {
455         for ( Response response : listResponse )
456         {
457             if ( ( response.getField( ) != null ) && ( response.getField( ).getIdField( ) == nIdField ) )
458             {
459                 return true;
460             }
461         }
462 
463         return false;
464     }
465 
466     /**
467      * Get the URL to modify an entry of the form in front office
468      * 
469      * @param entry
470      *            the entry
471      * @return The URL to modify the entry in front office
472      */
473     public String getEntryUrl( Entry entry )
474     {
475         UrlItem url = new UrlItem( AppPathService.getPortalUrl( ) );
476         url.addParameter( XPageAppService.PARAM_XPAGE_APP, AnnouncePlugin.PLUGIN_NAME );
477         url.addParameter( MVCUtils.PARAMETER_VIEW, VIEW_GET_FORM );
478 
479         if ( ( entry != null ) && ( entry.getIdResource( ) > 0 ) )
480         {
481             url.addParameter( PARAMETER_ID_CATEGORY, entry.getIdResource( ) );
482             url.setAnchor( PREFIX_ATTRIBUTE + entry.getIdEntry( ) );
483         }
484 
485         return url.getUrl( );
486     }
487 
488     /**
489      * Convert an AppointmentDTO to an Appointment by transferring response from the map of class AppointmentDTO to the list of class Appointment.
490      * 
491      * @param announce
492      *            The announce to get the map to convert
493      */
494     public void convertMapResponseToList( AnnounceDTO announce )
495     {
496         List<Response> listResponse = new ArrayList<>( );
497 
498         for ( List<Response> listResponseByEntry : announce.getMapResponsesByIdEntry( ).values( ) )
499         {
500             listResponse.addAll( listResponseByEntry );
501         }
502 
503         announce.setMapResponsesByIdEntry( null );
504         announce.setListResponse( listResponse );
505     }
506 
507     /**
508      * Get the date format to use
509      * 
510      * @return The date format to use
511      */
512     public static DateFormat getDateFormat( )
513     {
514         DateFormat dateFormat = new SimpleDateFormat( PATTERN_DATE, Locale.FRENCH );
515         dateFormat.setLenient( false );
516 
517         return dateFormat;
518     }
519 }