View Javadoc
1   /*
2    * Copyright (c) 2002-2022, City of Paris
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met:
8    *
9    *  1. Redistributions of source code must retain the above copyright notice
10   *     and the following disclaimer.
11   *
12   *  2. Redistributions in binary form must reproduce the above copyright notice
13   *     and the following disclaimer in the documentation and/or other materials
14   *     provided with the distribution.
15   *
16   *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
17   *     contributors may be used to endorse or promote products derived from
18   *     this software without specific prior written permission.
19   *
20   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
24   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30   * POSSIBILITY OF SUCH DAMAGE.
31   *
32   * License 1.0
33   */
34  package fr.paris.lutece.plugins.genericattributes.service.entrytype;
35  
36  import java.awt.image.BufferedImage;
37  import java.io.ByteArrayInputStream;
38  import java.io.IOException;
39  import java.util.ArrayList;
40  import java.util.Arrays;
41  import java.util.List;
42  import java.util.Locale;
43  
44  import javax.imageio.ImageIO;
45  import javax.servlet.http.HttpServletRequest;
46  
47  import org.apache.commons.fileupload.FileItem;
48  import org.apache.commons.lang3.StringUtils;
49  import org.apache.commons.lang3.math.NumberUtils;
50  
51  import fr.paris.lutece.plugins.asynchronousupload.service.IAsyncUploadHandler;
52  import fr.paris.lutece.plugins.genericattributes.business.Entry;
53  import fr.paris.lutece.plugins.genericattributes.business.Field;
54  import fr.paris.lutece.plugins.genericattributes.business.FieldHome;
55  import fr.paris.lutece.plugins.genericattributes.business.GenericAttributeError;
56  import fr.paris.lutece.plugins.genericattributes.business.Response;
57  import fr.paris.lutece.plugins.genericattributes.service.file.GenericAttributeFileService;
58  import fr.paris.lutece.plugins.genericattributes.util.FileAttributesUtils;
59  import fr.paris.lutece.portal.business.file.File;
60  import fr.paris.lutece.portal.business.regularexpression.RegularExpression;
61  import fr.paris.lutece.portal.service.fileupload.FileUploadService;
62  import fr.paris.lutece.portal.service.i18n.I18nService;
63  import fr.paris.lutece.portal.service.plugin.Plugin;
64  import fr.paris.lutece.portal.service.regularexpression.RegularExpressionService;
65  import fr.paris.lutece.portal.service.util.AppLogService;
66  import fr.paris.lutece.portal.service.util.AppPathService;
67  import fr.paris.lutece.util.ReferenceList;
68  import fr.paris.lutece.util.url.UrlItem;
69  
70  /**
71   * Abstract entry type for uploads
72   */
73  public abstract class AbstractEntryTypeUpload extends EntryTypeService
74  {
75      // PARAMETERS
76      protected static final String PARAMETER_ID_RESPONSE = "id_response";
77      protected static final String PARAMETER_MAX_FILES = "max_files";
78      protected static final String PARAMETER_FILE_MAX_SIZE = "file_max_size";
79      protected static final String PARAMETER_EXPORT_BINARY = "export_binary";
80      protected static final String PARAMETER_FILE_TYPE = "file_type";
81  
82      // CONSTANTS
83      protected static final String ALL = "*";
84      protected static final String COMMA = ",";
85  
86      // Private parameters
87      protected static final String PARAMETER_RESOURCE_TYPE = "resource_type";
88      protected static final String PARAMETER_ID = "id";
89      protected static final String URL_IMAGE_SERVLET = "image";
90  
91      // MESSAGES
92      protected static final String MESSAGE_ERROR_NOT_AN_IMAGE = "genericattributes.message.notAnImage";
93  
94      /**
95       * Get the asynchronous upload handler to use for entries of this type
96       * 
97       * @return The asynchronous upload handler to use for entries of this type
98       */
99      public abstract IAsyncUploadHandler getAsynchronousUploadHandler( );
100 
101     /**
102      * Get the URL to download the file of a response
103      * 
104      * @param nResponseId
105      *            The id of the response to download the file of
106      * @param strBaseUrl
107      *            The base URL
108      * @return The URL to redirect the user to download the file
109      */
110     public abstract String getUrlDownloadFile( int nResponseId, String strBaseUrl );
111 
112     /**
113      * Check whether this entry type allows only images or every file type
114      * 
115      * @return True if this entry type allows only images, false if it allow every file type
116      */
117     protected abstract boolean checkForImages( );
118 
119     /**
120      * Get the URL to download a file of a response throw the image servlet.
121      * 
122      * @param nResponseId
123      *            The id of the response
124      * @param strBaseUrl
125      *            The base URL
126      * @return The URL of to download the image
127      */
128     protected String getUrlDownloadImage( int nResponseId, String strBaseUrl )
129     {
130         UrlItem url = new UrlItem( strBaseUrl + URL_IMAGE_SERVLET );
131         url.addParameter( PARAMETER_RESOURCE_TYPE, Response.RESOURCE_TYPE );
132         url.addParameter( PARAMETER_ID, nResponseId );
133 
134         return url.getUrl( );
135     }
136 
137     /**
138      * {@inheritDoc}
139      */
140     @Override
141     public GenericAttributeError canUploadFiles( Entry entry, List<FileItem> listUploadedFileItems, List<FileItem> listFileItemsToUpload, Locale locale )
142     {
143         /** 1) Check max files */
144         GenericAttributeError error = FileAttributesUtils.checkNumberFiles( entry, listUploadedFileItems, listFileItemsToUpload, locale );
145         if ( error != null )
146         {
147             return error;
148         }
149 
150         /** 2) Check files size */
151         error = FileAttributesUtils.checkFileSize( entry, listUploadedFileItems, listFileItemsToUpload, locale );
152         if ( error != null )
153         {
154             return error;
155         }
156 
157         if ( listFileItemsToUpload != null )
158         {
159             for ( FileItem fileItem : listFileItemsToUpload )
160             {
161                 if ( checkForImages( ) )
162                 {
163                     error = doCheckforImages( fileItem, entry, locale );
164 
165                     if ( error != null )
166                     {
167                         return error;
168                     }
169                 }
170             }
171         }
172 
173         return null;
174     }
175 
176     /**
177      * {@inheritDoc}
178      */
179     @Override
180     public String getResponseValueForExport( Entry entry, HttpServletRequest request, Response response, Locale locale )
181     {
182         // Check whether the binaries must be exported or just displaying an URL to download the file
183         if ( entry.getFields( ) == null )
184         {
185             entry.setFields( FieldHome.getFieldListByIdEntry( entry.getIdEntry( ) ) );
186         }
187 
188         Field field = entry.getFieldByCode( FIELD_FILE_BINARY );
189 
190         if ( ( field != null ) && StringUtils.isNotBlank( field.getValue( ) ) && Boolean.TRUE.equals( Boolean.valueOf( field.getValue( ) ) ) )
191         {
192             if ( response.getFile( ) != null )
193             {
194                 File file = GenericAttributeFileService.getInstance().load( response.getFile( ).getFileKey( ) );
195 
196                 if ( ( file != null ) && ( file.getPhysicalFile( ) != null ) && ( file.getPhysicalFile( ).getValue( ) != null ) )
197                 {
198                     String strPhysicalFile = Arrays.toString( file.getPhysicalFile( ).getValue( ) );
199 
200                     if ( StringUtils.isNotBlank( strPhysicalFile ) )
201                     {
202                         // Removing the square brackets ("[]") that "Arrays.toString" added
203                         return strPhysicalFile.substring( 1, strPhysicalFile.length( ) - 1 );
204                     }
205                 }
206             }
207 
208             return StringUtils.EMPTY;
209         }
210 
211         String strBaseUrl = AppPathService.getBaseUrl( request );
212 
213         return getUrlDownloadFile( response.getIdResponse( ), strBaseUrl );
214     }
215 
216     // CHECKS
217 
218     /**
219      * Check the record field data
220      * 
221      * @param entry
222      *            The entry
223      * @param listFilesSource
224      *            the list of source files to upload
225      * @param locale
226      *            the locale
227      * @param request
228      *            the HTTP request
229      * @return The error if there is any
230      */
231     protected GenericAttributeError checkResponseData( Entry entry, List<FileItem> listFilesSource, Locale locale, HttpServletRequest request )
232     {
233         // Check if the user can upload the file. The File is already uploaded in the asynchronous uploaded files map
234         // Thus the list of files to upload is in the list of uploaded files
235         GenericAttributeError error = canUploadFiles( entry, listFilesSource, new ArrayList<FileItem>( ), locale );
236 
237         if ( error != null )
238         {
239             return error;
240         }
241         return FileAttributesUtils.checkResponseData( entry, listFilesSource, locale );
242     }
243 
244     // FINDERS
245 
246     /**
247      * Get the file source from the session
248      * 
249      * @param request
250      *            the HttpServletRequest
251      * @param strAttributeName
252      *            the attribute name
253      * @return the file item
254      */
255     protected List<FileItem> getFileSources( HttpServletRequest request, String strAttributeName )
256     {
257         if ( request != null )
258         {
259             // Files are only removed if a given flag is in the request
260             getAsynchronousUploadHandler( ).doRemoveFile( request, strAttributeName );
261 
262             // Files are only added if a given flag is in the request
263             getAsynchronousUploadHandler( ).addFilesUploadedSynchronously( request, strAttributeName );
264 
265             return getAsynchronousUploadHandler( ).getListUploadedFiles( strAttributeName, request.getSession( ) );
266         }
267 
268         return new ArrayList<>( );
269     }
270 
271     /**
272      * Gives the attribute name
273      * 
274      * @param entry
275      *            the entry
276      * @param request
277      *            the request
278      * @return the attribute name
279      */
280     protected String getAttributeName( Entry entry, HttpServletRequest request )
281     {
282         String strAttributePrefix = IEntryTypeService.PREFIX_ATTRIBUTE + Integer.toString( entry.getIdEntry( ) );
283         int nIterationNumber = getResponseIterationValue( request );
284 
285         if ( nIterationNumber != NumberUtils.INTEGER_MINUS_ONE )
286         {
287             strAttributePrefix = IEntryTypeService.PREFIX_ITERATION_ATTRIBUTE + nIterationNumber + "_" + strAttributePrefix;
288         }
289 
290         return strAttributePrefix;
291     }
292 
293     // PRIVATE METHODS
294 
295     /**
296      * {@inheritDoc}
297      */
298     @Override
299     public String getRequestData( Entry entry, HttpServletRequest request, Locale locale )
300     {
301         initCommonRequestData( entry, request );
302         String strTitle = request.getParameter( PARAMETER_TITLE );
303         String strHelpMessage = ( request.getParameter( PARAMETER_HELP_MESSAGE ) != null ) ? request.getParameter( PARAMETER_HELP_MESSAGE ).trim( ) : null;
304         String strComment = request.getParameter( PARAMETER_COMMENT );
305         String strMandatory = request.getParameter( PARAMETER_MANDATORY );
306         String strCSSClass = request.getParameter( PARAMETER_CSS_CLASS );
307         String strCode = request.getParameter( PARAMETER_ENTRY_CODE );
308         String strOnlyDisplayInBack = request.getParameter( PARAMETER_ONLY_DISPLAY_IN_BACK );
309         String strIndexed = request.getParameter( PARAMETER_INDEXED );
310 
311         String strError = FileAttributesUtils.checkEntryData( request, locale );
312 
313         if ( StringUtils.isNotBlank( strError ) )
314         {
315             return strError;
316         }
317 
318         entry.setTitle( strTitle );
319         entry.setCode( strCode );
320         entry.setHelpMessage( strHelpMessage );
321         entry.setComment( strComment );
322         entry.setCSSClass( strCSSClass );
323         entry.setIndexed( strIndexed != null );
324 
325         FileAttributesUtils.createOrUpdateFileFields( entry, request );
326 
327         entry.setMandatory( strMandatory != null );
328         entry.setOnlyDisplayInBack( strOnlyDisplayInBack != null );
329         return null;
330     }
331 
332     /**
333      * {@inheritDoc}
334      */
335     @Override
336     public ReferenceList getReferenceListRegularExpression( Entry entry, Plugin plugin )
337     {
338         ReferenceList refListRegularExpression = null;
339 
340         if ( RegularExpressionService.getInstance( ).isAvailable( ) )
341         {
342             refListRegularExpression = new ReferenceList( );
343 
344             List<RegularExpression> listRegularExpression = RegularExpressionService.getInstance( ).getAllRegularExpression( );
345 
346             for ( RegularExpression regularExpression : listRegularExpression )
347             {
348                 if ( !entry.getFields( ).get( 0 ).getRegularExpressionList( ).contains( regularExpression ) )
349                 {
350                     refListRegularExpression.addItem( regularExpression.getIdExpression( ), regularExpression.getTitle( ) );
351                 }
352             }
353         }
354 
355         return refListRegularExpression;
356     }
357 
358     /**
359      * toStringValue should stay <code>null</code>.
360      * 
361      * @param entry
362      *            The entry
363      * @param response
364      *            The response
365      * @param locale
366      *            the locale - will use a default one if not specified
367      */
368     @Override
369     public void setResponseToStringValue( Entry entry, Response response, Locale locale )
370     {
371         // nothing - null is default
372     }
373 
374     /**
375      * {@inheritDoc}
376      */
377     @Override
378     public String getResponseValueForRecap( Entry entry, HttpServletRequest request, Response response, Locale locale )
379     {
380         if ( ( response.getFile( ) != null ) && StringUtils.isNotBlank( response.getFile( ).getTitle( ) ) )
381         {
382             return response.getFile( ).getTitle( );
383         }
384 
385         return StringUtils.EMPTY;
386     }
387 
388     /**
389      * Do check that an uploaded file is an image
390      * 
391      * @param fileItem
392      *            The file item
393      * @param entry
394      *            the entry
395      * @param locale
396      *            The locale
397      * @return The error if any, or null if the file is a valid image
398      */
399     public GenericAttributeError doCheckforImages( FileItem fileItem, Entry entry, Locale locale )
400     {
401         String strFilename = FileUploadService.getFileNameOnly( fileItem );
402         BufferedImage image = null;
403 
404         try
405         {
406             if ( fileItem.get( ) != null )
407             {
408                 image = ImageIO.read( new ByteArrayInputStream( fileItem.get( ) ) );
409             }
410         }
411         catch( IOException e )
412         {
413             AppLogService.error( e );
414         }
415 
416         if ( ( image == null ) && StringUtils.isNotBlank( strFilename ) )
417         {
418             GenericAttributeErrores/business/GenericAttributeError.html#GenericAttributeError">GenericAttributeError genAttError = new GenericAttributeError( );
419             genAttError.setMandatoryError( false );
420 
421             Object [ ] args = {
422                     fileItem.getName( )
423             };
424             genAttError.setErrorMessage( I18nService.getLocalizedString( MESSAGE_ERROR_NOT_AN_IMAGE, args, locale ) );
425             genAttError.setTitleQuestion( entry.getTitle( ) );
426 
427             return genAttError;
428         }
429 
430         return null;
431     }
432 }