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.asynchronousupload.web;
35  
36  import java.io.IOException;
37  import java.util.ArrayList;
38  import java.util.HashMap;
39  import java.util.List;
40  import java.util.Map;
41  
42  import javax.servlet.http.HttpServletRequest;
43  import javax.servlet.http.HttpServletResponse;
44  
45  import org.apache.commons.lang3.StringUtils;
46  
47  import fr.paris.lutece.plugins.asynchronousupload.service.IAsyncUploadHandler;
48  import fr.paris.lutece.plugins.asynchronousupload.service.UploadCacheService;
49  import fr.paris.lutece.portal.service.spring.SpringContextService;
50  import fr.paris.lutece.portal.service.template.AppTemplateService;
51  import fr.paris.lutece.portal.service.util.AppLogService;
52  import fr.paris.lutece.portal.service.util.AppPathService;
53  import fr.paris.lutece.portal.service.util.AppPropertiesService;
54  import fr.paris.lutece.portal.util.mvc.xpage.MVCApplication;
55  import fr.paris.lutece.util.html.HtmlTemplate;
56  
57  /**
58   * Upload application
59   */
60  public class AsynchronousUploadApp extends MVCApplication
61  {
62      private static final long serialVersionUID = -2287035947644920508L;
63  
64      // Marks
65      private static final String MARK_BASE_URL = "base_url";
66      private static final String MARK_UPLOAD_URL = "upload_url";
67      private static final String MARK_HANDLER_NAME = "handler_name";
68      private static final String MARK_SUBMIT_PREFIX = "submitPrefix";
69      private static final String MARK_DELETE_PREFIX = "deletePrefix";
70      private static final String MARK_CHECKBOX_PREFIX = "checkBoxPrefix";
71      private static final String MARK_SPLIT_FILE = "splitFile";
72      // Parameters
73      private static final String PROPERTY_KEY_PREFIX = "asynchronous.upload.config.";
74      private static final String PARAMETER_HANDLER = "handler";
75      private static final String PARAMETER_FIELD_NAME = "fieldname";
76      private static final String PARAMETER_FIELD_INDEX = "field_index";
77      private static final String PARAMETER_MAX_FILE_SIZE = "maxFileSize";
78      private static final String PARAMETER_IMAGE_MAX_WIDTH = "imageMaxWidth";
79      private static final String PARAMETER_IMAGE_MAX_HEIGHT = "imageMaxHeight";
80      private static final String PARAMETER_PREVIEW_MAX_WIDTH = "previewMaxWidth";
81      private static final String PARAMETER_PREVIEW_MAX_HEIGHT = "previewMaxHeight";
82      private static final String PARAMETER_MAX_CHUNK_SIZE = "maxChunkSize";
83  
84      // Templates
85      private static final String TEMPLATE_MAIN_UPLOAD_JS = "skin/plugins/asynchronousupload/main.js";
86      private static final String TEMPLATE_ADMIN_MAIN_UPLOAD_JS = "admin/plugins/asynchronousupload/main.js";
87  
88      // Urls
89      private static final String URL_UPLOAD_SERVLET = "jsp/site/upload";
90  
91      // Constants
92      private static final String CONSTANT_COMA = ",";
93      // filed name
94      private static final String DEFAULT_FIELD_NAME = StringUtils.EMPTY;
95  
96      /**
97       * Get the main upload JavaScript file. Available HTTP parameters are :
98       * <ul>
99       * <li><b>handler</b> : Name of the handler that will manage the asynchronous upload.</li>
100      * <li><b>maxFileSize</b> : The maximum size (in bytes) of uploaded files. Default value is 2097152</li>
101      * </ul>
102      * 
103      * @param request
104      *            The request
105      * @param bContext
106      *            True if front / False if admin
107      * @return The content of the JavaScript file
108      */
109     public String getMainUploadJs( HttpServletRequest request, Boolean bContext )
110     {
111         String strBaseUrl = AppPathService.getBaseUrl( request );
112 
113         String strTemplate;
114 
115         String strHandlerName = request.getParameter( PARAMETER_HANDLER );
116         String strMaxFileSize = request.getParameter( PARAMETER_MAX_FILE_SIZE );
117         String strImageMaxWidth = request.getParameter( PARAMETER_IMAGE_MAX_WIDTH );
118         String strImageMaxHeight = request.getParameter( PARAMETER_IMAGE_MAX_HEIGHT );
119         String strPreviewMaxWidth = request.getParameter( PARAMETER_PREVIEW_MAX_WIDTH );
120         String strPreviewMaxHeight = request.getParameter( PARAMETER_PREVIEW_MAX_HEIGHT );
121         String strFieldName = request.getParameter( PARAMETER_FIELD_NAME );
122         String strMaxChunkSize = request.getParameter( PARAMETER_MAX_CHUNK_SIZE );
123 
124         IAsyncUploadHandler handler = getHandler( strHandlerName );
125 
126         int nMaxFileSize = Integer.parseInt( AppPropertiesService.getProperty( PROPERTY_KEY_PREFIX + PARAMETER_MAX_FILE_SIZE ) );
127         if ( StringUtils.isNumeric( strMaxFileSize ) )
128         {
129             nMaxFileSize = Integer.parseInt( strMaxFileSize );
130         }
131 
132         int nImageMaxHeight = Integer.parseInt( AppPropertiesService.getProperty( PROPERTY_KEY_PREFIX + PARAMETER_IMAGE_MAX_HEIGHT ) );
133         if ( StringUtils.isNumeric( strImageMaxHeight ) )
134         {
135             nImageMaxHeight = Integer.parseInt( strImageMaxHeight );
136         }
137 
138         int nImageMaxWidth = Integer.parseInt( AppPropertiesService.getProperty( PROPERTY_KEY_PREFIX + PARAMETER_IMAGE_MAX_WIDTH ) );
139         if ( StringUtils.isNumeric( strImageMaxWidth ) )
140         {
141             nImageMaxWidth = Integer.parseInt( strImageMaxWidth );
142         }
143 
144         int nPreviewMaxWidth = Integer.parseInt( AppPropertiesService.getProperty( PROPERTY_KEY_PREFIX + PARAMETER_PREVIEW_MAX_WIDTH ) );
145         if ( StringUtils.isNumeric( strPreviewMaxWidth ) )
146         {
147             nPreviewMaxWidth = Integer.parseInt( strPreviewMaxWidth );
148         }
149 
150         int nPreviewMaxHeight = Integer.parseInt( AppPropertiesService.getProperty( PROPERTY_KEY_PREFIX + PARAMETER_PREVIEW_MAX_HEIGHT ) );
151         if ( StringUtils.isNumeric( strPreviewMaxHeight ) )
152         {
153             nPreviewMaxHeight = Integer.parseInt( strPreviewMaxHeight );
154         }
155 
156         int nMaxChunkSize = AppPropertiesService.getPropertyInt( PROPERTY_KEY_PREFIX + PARAMETER_MAX_CHUNK_SIZE, 0 );
157         if ( StringUtils.isNumeric( strMaxChunkSize ) )
158         {
159             nMaxChunkSize = Integer.parseInt( strMaxChunkSize );
160         }
161 
162         if ( StringUtils.isEmpty( strFieldName ) )
163         {
164             strFieldName = DEFAULT_FIELD_NAME;
165         }
166 
167         String strKey = StringUtils.defaultString( strHandlerName ) + strBaseUrl + StringUtils.defaultString( strMaxFileSize )
168                 + StringUtils.defaultString( strImageMaxWidth ) + StringUtils.defaultString( strImageMaxHeight )
169                 + StringUtils.defaultString( strPreviewMaxWidth ) + StringUtils.defaultString( strPreviewMaxHeight )
170                 + StringUtils.defaultString( strMaxChunkSize ) + strFieldName;
171 
172         String strContent = (String) UploadCacheService.getInstance( ).getFromCache( strKey );
173 
174         if ( strContent != null )
175         {
176             return strContent;
177         }
178 
179         Map<String, Object> model = new HashMap<>( );
180         boolean bSplitFile = handler.isManagePartialContent( ) && nMaxChunkSize > 0;
181         model.put( MARK_BASE_URL, strBaseUrl );
182         model.put( MARK_UPLOAD_URL, URL_UPLOAD_SERVLET );
183         model.put( MARK_HANDLER_NAME, strHandlerName );
184         model.put( PARAMETER_MAX_FILE_SIZE, nMaxFileSize );
185         model.put( PARAMETER_IMAGE_MAX_WIDTH, nImageMaxWidth );
186         model.put( PARAMETER_IMAGE_MAX_HEIGHT, nImageMaxHeight );
187         model.put( PARAMETER_PREVIEW_MAX_WIDTH, nPreviewMaxWidth );
188         model.put( PARAMETER_PREVIEW_MAX_HEIGHT, nPreviewMaxHeight );
189         model.put( MARK_SUBMIT_PREFIX, handler.getUploadSubmitPrefix( ) );
190         model.put( MARK_DELETE_PREFIX, handler.getUploadDeletePrefix( ) );
191         model.put( MARK_CHECKBOX_PREFIX, handler.getUploadCheckboxPrefix( ) );
192         model.put( PARAMETER_FIELD_NAME, strFieldName );
193         model.put( MARK_SPLIT_FILE, bSplitFile );
194         model.put( PARAMETER_MAX_CHUNK_SIZE, nMaxChunkSize );
195 
196         if ( Boolean.TRUE.equals( bContext ) )
197         {
198             strTemplate = TEMPLATE_MAIN_UPLOAD_JS;
199         }
200         else
201         {
202             strTemplate = TEMPLATE_ADMIN_MAIN_UPLOAD_JS;
203         }
204         HtmlTemplate template = AppTemplateService.getTemplate( strTemplate, getLocale( request ), model );
205         strContent = template.getHtml( );
206         UploadCacheService.getInstance( ).putInCache( strKey, strContent );
207         return strContent;
208     }
209 
210     /**
211      * Removes the uploaded fileItem.
212      * 
213      * @param request
214      *            the request
215      * @return The JSON result
216      */
217     public String doRemoveAsynchronousUploadedFile( HttpServletRequest request )
218     {
219         String strFieldName = request.getParameter( PARAMETER_FIELD_NAME );
220 
221         String strFieldIndex = request.getParameter( PARAMETER_FIELD_INDEX );
222 
223         List<Integer> listIndexesFilesToRemove = new ArrayList<>( );
224 
225         if ( StringUtils.isNotEmpty( strFieldIndex ) )
226         {
227             for ( String strIndex : StringUtils.split( strFieldIndex, CONSTANT_COMA ) )
228             {
229                 if ( StringUtils.isNotEmpty( strIndex ) && StringUtils.isNumeric( strIndex ) )
230                 {
231                     listIndexesFilesToRemove.add( Integer.parseInt( strIndex ) );
232                 }
233             }
234         }
235 
236         IAsyncUploadHandler handler = getHandler( request );
237 
238         return ( handler == null ) ? StringUtils.EMPTY : handler.doRemoveUploadedFile( request, strFieldName, listIndexesFilesToRemove );
239     }
240 
241     /**
242      * Gets the handler
243      * 
244      * @param request
245      *            the request
246      * @return the handler found, <code>null</code> otherwise.
247      * @see IAsynchronousUploadHandler#isInvoked(HttpServletRequest)
248      */
249     private IAsyncUploadHandler getHandler( HttpServletRequest request )
250     {
251         for ( IAsyncUploadHandler handler : SpringContextService.getBeansOfType( IAsyncUploadHandler.class ) )
252         {
253             if ( handler.isInvoked( request ) )
254             {
255                 return handler;
256             }
257         }
258 
259         return null;
260     }
261 
262     /**
263      * Get a handler from its name
264      * 
265      * @param strName
266      *            The name of the handler
267      * @return The handler, or null if no handler was found
268      */
269     private IAsyncUploadHandler getHandler( String strName )
270     {
271         for ( IAsyncUploadHandler handler : SpringContextService.getBeansOfType( IAsyncUploadHandler.class ) )
272         {
273             if ( StringUtils.equals( handler.getHandlerName( ), strName ) )
274             {
275                 return handler;
276             }
277         }
278 
279         return null;
280     }
281 
282     /**
283      * Get the uploaded fileItem.
284      * 
285      * @param request
286      *            the request
287      * @param response
288      *            the response
289      */
290     public void doRetrieveAsynchronousUploadedFile( HttpServletRequest request, HttpServletResponse response )
291     {
292         IAsyncUploadHandler handler = getHandler( request );
293         byte [ ] data = handler.doRetrieveUploadedFile( request );
294         String strFieldName = request.getParameter( "fileName" );
295 
296         response.setHeader( "Content-length", Integer.toString( data.length ) );
297         response.setHeader( "Content-Disposition", "attachment;;filename=" + strFieldName );
298         try
299         {
300             response.getOutputStream( ).write( data, 0, data.length );
301             response.getOutputStream( ).flush( );
302         }
303         catch( IOException e )
304         {
305             AppLogService.error( e );
306         }
307     }
308 }