View Javadoc
1   /*
2    * Copyright (c) 2002-2014, 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.upload.web;
35  
36  import fr.paris.lutece.plugins.upload.util.UploadUtil;
37  import fr.paris.lutece.portal.service.fileupload.FileUploadService;
38  import fr.paris.lutece.portal.service.message.AdminMessage;
39  import fr.paris.lutece.portal.service.message.AdminMessageService;
40  import fr.paris.lutece.portal.service.plugin.Plugin;
41  import fr.paris.lutece.portal.service.plugin.PluginService;
42  import fr.paris.lutece.portal.service.template.AppTemplateService;
43  import fr.paris.lutece.portal.service.util.AppLogService;
44  import fr.paris.lutece.portal.service.util.AppPathService;
45  import fr.paris.lutece.portal.service.util.AppPropertiesService;
46  import fr.paris.lutece.portal.web.admin.PluginAdminPageJspBean;
47  import fr.paris.lutece.portal.web.constants.Messages;
48  import fr.paris.lutece.portal.web.upload.MultipartHttpServletRequest;
49  import fr.paris.lutece.util.ReferenceList;
50  import fr.paris.lutece.util.date.DateUtil;
51  import fr.paris.lutece.util.html.HtmlTemplate;
52  import fr.paris.lutece.util.html.Paginator;
53  import fr.paris.lutece.util.url.UrlItem;
54  
55  import org.apache.commons.fileupload.FileItem;
56  import org.apache.commons.lang.StringUtils;
57  
58  import java.io.BufferedInputStream;
59  import java.io.BufferedOutputStream;
60  import java.io.File;
61  import java.io.FileFilter;
62  import java.io.FileOutputStream;
63  import java.io.IOException;
64  import java.io.InputStream;
65  import java.io.OutputStream;
66  import java.util.ArrayList;
67  import java.util.Date;
68  import java.util.Enumeration;
69  import java.util.HashMap;
70  import java.util.Hashtable;
71  import java.util.List;
72  import java.util.Map;
73  import java.util.Stack;
74  import java.util.zip.ZipEntry;
75  import java.util.zip.ZipException;
76  import java.util.zip.ZipFile;
77  
78  import javax.servlet.http.HttpServletRequest;
79  
80  
81  /**
82   * This class provides the user interface to manage Library features ( manage, create, modify, remove, change order of
83   * contact )
84   */
85  public class UploadJspBean extends PluginAdminPageJspBean
86  {
87      ////////////////////////////////////////////////////////////////////////////
88      // Constants
89  
90      // Right
91      public static final String RIGHT_MANAGE_UPLOAD = "UPLOAD_MANAGEMENT";
92  
93      // Templates
94      private static final String TEMPLATE_MANAGE_UPLOAD = "admin/plugins/upload/manage_upload.html";
95      private static final String TEMPLATE_FILE_ROW = "admin/plugins/upload/file_row.html";
96  
97      // JSP
98      private static final String JSP_DO_REMOVE_FILE = "jsp/admin/plugins/upload/DoRemoveFile.jsp";
99  
100     // Parameters
101     private static final String PARAMETER_FILE_NAME = "file_name";
102     private static final String PARAMETER_DIRECTORY = "directory";
103     private static final String PARAMETER_UNZIP = "unzip";
104 
105     // Properties
106     private static final String PLUGIN_PARAM_DIRECTORY = "directory";
107     private static final String PROPERTY_PAGE_TITLE_MANAGE_UPLOAD = "upload.manage_upload.pageTitle";
108     private static final String PROPERTY_FILES_PER_PAGE = "paginator.upload.files.itemsPerPage";
109 
110     // Markers
111     private static final String MARK_FILE_NAME = "file_name";
112     private static final String MARK_FILE_SIZE = "file_size";
113     private static final String MARK_FILE_DATE = "file_date";
114     private static final String MARK_FILES_LIST = "files_list";
115     private static final String MARK_DIRECTORY = "directory";
116     private static final String MARK_DIRECTORY_LIST = "directory_list";
117     private static final String MARK_PAGINATOR = "paginator";
118     private static final String MARK_NB_ITEMS_PER_PAGE = "nb_items_per_page";
119 
120     // Message keys
121     private static final String MESSAGE_CONFIRM_REMOVE_FILE = "upload.message.confirmRemoveFile";
122     private static final String MESSAGE_FILE_EXISTS = "upload.message.fileExists";
123     private static final String MESSAGE_ZIP_ERROR = "upload.message.zipError";
124 
125     //FileFilters used in the getDirectorySize method.
126     private static final FileFilter dirFilter = new FileFilter(  )
127         {
128             public boolean accept( File pathname )
129             {
130                 return pathname.isDirectory(  );
131             }
132         };
133 
134     private static final FileFilter fileFilter = new FileFilter(  )
135         {
136             public boolean accept( File pathname )
137             {
138                 return ( !pathname.isDirectory(  ) );
139             }
140         };
141 
142     private int _nItemsPerPage;
143     private int _nDefaultItemsPerPage;
144     private String _strCurrentPageIndex;
145     private Hashtable<String, String> _htDirectories = new Hashtable<String, String>(  );
146     private ReferenceList _listDirectories = new ReferenceList(  );
147 
148     /**
149      * Creates a new UploadJspBean object.
150      */
151     public UploadJspBean(  )
152     {
153     }
154 
155     /**
156      * Returns the Files management form
157      *
158      * @param request The Http request
159      * @return The Html form
160      */
161     public String getFiles( HttpServletRequest request )
162     {
163         setPageTitleProperty( PROPERTY_PAGE_TITLE_MANAGE_UPLOAD );
164 
165         String strPluginName = this.getPlugin(  ).getName(  );
166 
167         if ( strPluginName != null )
168         {
169             initDirectories( strPluginName );
170         }
171 
172         String strDirectoryIndex = request.getParameter( PARAMETER_DIRECTORY );
173 
174         if ( ( strDirectoryIndex == null ) || ( strDirectoryIndex.equals( "" ) ) )
175         {
176             strDirectoryIndex = "1";
177         }
178 
179         String strRelativeDirectory = getDirectory( strDirectoryIndex );
180         String strDirectory = AppPathService.getWebAppPath(  ) + strRelativeDirectory;
181         File directoryPictures = new File( strDirectory );
182         File[] filePictures = directoryPictures.listFiles(  );
183 
184         // Creating names array
185         int nFileNumber = filePictures.length;
186         String[] arrayFile = new String[nFileNumber];
187 
188         for ( int i = 0; i < nFileNumber; i++ )
189         {
190             arrayFile[i] = filePictures[i].getName(  );
191         }
192 
193         //defines the order of the files
194         File[] screenArrayFile = new File[nFileNumber];
195         String name;
196 
197         for ( int i = 0; i < nFileNumber; i++ )
198         {
199             int numero = i;
200             name = arrayFile[i];
201 
202             for ( int a = 0; a < nFileNumber; a++ )
203             {
204                 String strNameTemp = arrayFile[a];
205 
206                 if ( name.toLowerCase(  ).trim(  ).compareTo( strNameTemp.toLowerCase(  ).trim(  ) ) > 0 )
207                 {
208                     name = strNameTemp;
209                     numero = a;
210                 }
211             }
212 
213             screenArrayFile[i] = filePictures[numero];
214             arrayFile[numero] = "{";
215         }
216 
217         List<String> filesList = new ArrayList<String>(  );
218 
219         for ( int i = 0; i < nFileNumber; i++ )
220         {
221             File file = (File) screenArrayFile[i];
222             HashMap modelFile = new HashMap(  );
223 
224             if ( file.isDirectory(  ) )
225             {
226                 modelFile.put( MARK_FILE_NAME, file.getName(  ) + "\\" );
227 
228                 // Getting the directory's size
229                 modelFile.put( MARK_FILE_SIZE, getDirectorySize( file ) );
230             }
231             else
232             {
233                 modelFile.put( MARK_FILE_NAME, file.getName(  ) );
234 
235                 // Getting the file's size
236                 modelFile.put( MARK_FILE_SIZE, file.length(  ) );
237             }
238 
239             // Getting the file's date
240             modelFile.put( MARK_FILE_DATE, DateUtil.getDateTimeString( file.lastModified(  ) ) );
241             modelFile.put( MARK_DIRECTORY, strDirectoryIndex );
242 
243             HtmlTemplate templateFile = AppTemplateService.getTemplate( TEMPLATE_FILE_ROW, getLocale(  ), modelFile );
244 
245             //strRows.append( templateFile.getHtml(  ) );
246             filesList.add( templateFile.getHtml(  ) );
247         }
248 
249         // Paginator construction
250         _strCurrentPageIndex = Paginator.getPageIndex( request, Paginator.PARAMETER_PAGE_INDEX, _strCurrentPageIndex );
251         _nDefaultItemsPerPage = AppPropertiesService.getPropertyInt( PROPERTY_FILES_PER_PAGE, 10 );
252         _nItemsPerPage = Paginator.getItemsPerPage( request, Paginator.PARAMETER_ITEMS_PER_PAGE, _nItemsPerPage,
253                 _nDefaultItemsPerPage );
254 
255         UrlItem url = new UrlItem( getHomeUrl( request ) );
256         url.addParameter( PARAMETER_DIRECTORY, strDirectoryIndex );
257 
258         Paginator paginator = new Paginator( filesList, _nItemsPerPage, url.getUrl(  ), Paginator.PARAMETER_PAGE_INDEX,
259                 _strCurrentPageIndex );
260 
261         StringBuffer strRows = new StringBuffer(  );
262         List<String> paginatedFilesList = paginator.getPageItems(  );
263 
264         for ( String fileRow : paginatedFilesList )
265         {
266             strRows.append( fileRow );
267         }
268 
269         HashMap model = new HashMap(  );
270         model.put( MARK_NB_ITEMS_PER_PAGE, "" + _nItemsPerPage );
271         model.put( MARK_PAGINATOR, paginator );
272         model.put( MARK_FILES_LIST, strRows.toString(  ) );
273         model.put( MARK_DIRECTORY, strDirectoryIndex );
274         model.put( MARK_DIRECTORY_LIST, _listDirectories );
275 
276         HtmlTemplate template = AppTemplateService.getTemplate( TEMPLATE_MANAGE_UPLOAD, getLocale(  ), model );
277 
278         return getAdminPage( template.getHtml(  ) );
279     }
280 
281     /**
282      * Initialize the directories for the plugin. Use the plugin configuration file
283      * @param strPluginName The name of the plugin
284      */
285     private void initDirectories( String strPluginName )
286     {
287         // Reset lists
288         _htDirectories.clear(  );
289         _listDirectories.clear(  );
290 
291         // Gets plugin's parameters
292         Plugin plugin = PluginService.getPlugin( strPluginName );
293         Map<String, String> htParams = plugin.getParams(  );
294         int i = 1;
295 
296         while ( true )
297         {
298             String strKey = PLUGIN_PARAM_DIRECTORY + i;
299             String strDirectory = htParams.get( strKey );
300 
301             if ( strDirectory == null )
302             {
303                 break;
304             }
305 
306             _htDirectories.put( strKey, strDirectory );
307             _listDirectories.addItem( i, strDirectory );
308             i++;
309         }
310     }
311 
312     /**
313      * Get the directory in the map odf the directories read in the plugin configuration file
314      * @param strDirectoryIndex The index of Directory
315      * @return The Key of The Directory Index
316      */
317     private String getDirectory( String strDirectoryIndex )
318     {
319         String strKey = PLUGIN_PARAM_DIRECTORY + strDirectoryIndex;
320 
321         return _htDirectories.get( strKey );
322     }
323 
324     /**
325      * Process file upload
326      *
327      * @param request Http request
328      * @return Html form
329      */
330     public String doCreateFile( HttpServletRequest request )
331     {
332         String strDirectoryIndex = null;
333 
334         try
335         {
336             MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request;
337             FileItem item = multiRequest.getFile( PARAMETER_FILE_NAME );
338 
339             // The index and value of the directory combo in the form
340             strDirectoryIndex = multiRequest.getParameter( PARAMETER_DIRECTORY );
341 
342             String strRelativeDirectory = getDirectory( strDirectoryIndex );
343 
344             // The absolute path of the target directory
345             String strDestDirectory = AppPathService.getWebAppPath(  ) + strRelativeDirectory;
346 
347             // List the files in the target directory
348             File[] existingFiles = ( new File( strDestDirectory ) ).listFiles(  );
349 
350             // Is the 'unzip' checkbox checked?
351             boolean bUnzip = ( multiRequest.getParameter( PARAMETER_UNZIP ) != null );
352 
353             if ( item != null && !item.getName( ).equals( StringUtils.EMPTY ) )
354             {
355                 if ( !bUnzip ) // copy the file
356                 {
357                     AppLogService.debug( "copying the file" );
358 
359                     // Getting downloadFile's name
360                     String strNameFile = FileUploadService.getFileNameOnly( item );
361 
362                     // Clean name
363                     String strClearName = UploadUtil.cleanFileName( strNameFile );
364 
365                     // Checking duplicate
366                     if ( duplicate( strClearName, existingFiles ) )
367                     {
368                         return AdminMessageService.getMessageUrl( request, MESSAGE_FILE_EXISTS, AdminMessage.TYPE_STOP );
369                     }
370 
371                     // Move the file to the target directory
372                     File destFile = new File( strDestDirectory, strClearName );
373 
374                     FileOutputStream fos = new FileOutputStream( destFile );
375                     fos.flush(  );
376                     fos.write( item.get(  ) );
377                     fos.close(  );
378                 }
379                 else // unzip the file
380                 {
381                     AppLogService.debug( "unzipping the file" );
382 
383                     ZipFile zipFile;
384 
385                     try
386                     {
387                         // Create a temporary file with result of getTime() as unique file name
388                         File tempFile = File.createTempFile( Long.toString( ( new Date(  ) ).getTime(  ) ), ".zip" );
389                         // Delete temp file when program exits.
390                         tempFile.deleteOnExit(  );
391 
392                         FileOutputStream fos = new FileOutputStream( tempFile );
393                         fos.flush(  );
394                         fos.write( item.get(  ) );
395                         fos.close(  );
396                         zipFile = new ZipFile( tempFile );
397                     }
398                     catch ( ZipException ze )
399                     {
400                         AppLogService.error( "Error opening zip file", ze );
401 
402                         return AdminMessageService.getMessageUrl( request, MESSAGE_ZIP_ERROR, AdminMessage.TYPE_STOP );
403                     }
404 
405                     // Each zipped file is indentified by a zip entry :
406                     Enumeration zipEntries = zipFile.entries(  );
407 
408                     while ( zipEntries.hasMoreElements(  ) )
409                     {
410                         ZipEntry zipEntry = (ZipEntry) zipEntries.nextElement(  );
411 
412                         // Clean the name :
413                         String strZippedName = zipEntry.getName(  );
414                         String strClearName = UploadUtil.cleanFilePath( strZippedName );
415 
416                         if ( strZippedName.equals( strClearName ) )
417                         {
418                             // The unzipped file :
419                             File destFile = new File( strDestDirectory, strClearName );
420 
421                             // Create the parent directory structure if needed :
422                             destFile.getParentFile(  ).mkdirs(  );
423 
424                             if ( !zipEntry.isDirectory(  ) ) // don't unzip directories
425                             {
426                                 AppLogService.debug( "unzipping " + strZippedName + " to " + destFile.getName(  ) );
427 
428                                 // InputStream from zipped data
429                                 InputStream inZipStream = zipFile.getInputStream( zipEntry );
430 
431                                 // OutputStream to the destination file
432                                 OutputStream outDestStream = new FileOutputStream( destFile );
433 
434                                 // Helper method to copy data
435                                 copyStream( inZipStream, outDestStream );
436 
437                                 inZipStream.close(  );
438                                 outDestStream.close(  );
439                             }
440                             else
441                             {
442                                 AppLogService.debug( "skipping directory " + strZippedName );
443                             }
444                         }
445                         else
446                         {
447                             AppLogService.debug( "skipping accented file " + strZippedName );
448                         }
449                     }
450                 }
451             }
452             else
453             {
454                 return AdminMessageService.getMessageUrl( request, Messages.MANDATORY_FIELDS, AdminMessage.TYPE_STOP );
455             }
456         }
457         catch ( IOException e )
458         {
459             AppLogService.error( e.getMessage(  ), e );
460         }
461 
462         // Returns upload management page
463         UrlItem url = new UrlItem( getHomeUrl( request ) );
464 
465         if ( strDirectoryIndex != null )
466         {
467             url.addParameter( PARAMETER_DIRECTORY, strDirectoryIndex );
468         }
469 
470         return url.getUrl(  );
471     }
472 
473     /**
474      * Check if file is already in library
475      *
476      * @param strFileName The name of the file to upload
477      * @param repContent The name of the directory
478      * @return The result (boolean)
479      */
480     private boolean duplicate( String strFileName, File[] repContent )
481     {
482         boolean duplicate = false;
483 
484         for ( int i = 0; i < repContent.length; i++ )
485         {
486             if ( repContent[i].getName(  ).equals( strFileName ) )
487             {
488                 duplicate = true;
489             }
490         }
491 
492         return duplicate;
493     }
494 
495     /**
496      * Returns the confirmation to remove the file
497      *
498      * @param request The Http request
499      * @return the confirmation page
500      */
501     public String getConfirmRemoveFile( HttpServletRequest request )
502     {
503         UrlItem url = new UrlItem( JSP_DO_REMOVE_FILE );
504         url.addParameter( PARAMETER_DIRECTORY, request.getParameter( PARAMETER_DIRECTORY ) );
505         url.addParameter( PARAMETER_FILE_NAME, request.getParameter( PARAMETER_FILE_NAME ) );
506 
507         return AdminMessageService.getMessageUrl( request, MESSAGE_CONFIRM_REMOVE_FILE, url.getUrl(  ),
508             AdminMessage.TYPE_CONFIRMATION );
509     }
510 
511     /**
512      * Process image's deleting
513      *
514      * @param request Http request
515      * @return The library management page url
516      */
517     public String doRemoveFile( HttpServletRequest request )
518     {
519         String strDirectoryIndex = request.getParameter( PARAMETER_DIRECTORY );
520         String strRelativeDirectory = getDirectory( strDirectoryIndex );
521         String strDir = AppPathService.getWebAppPath(  ) + strRelativeDirectory;
522         String strNameFile = request.getParameter( PARAMETER_FILE_NAME );
523 
524         // Deleting downloadFile physically
525         File downloadFile = new File( strDir, strNameFile );
526 
527         if ( downloadFile.isDirectory(  ) )
528         {
529             deleteDirectory( downloadFile );
530         }
531         else
532         {
533             downloadFile.delete(  );
534         }
535 
536         // Returns upload management page url
537         UrlItem url = new UrlItem( getHomeUrl( request ) );
538 
539         if ( strDirectoryIndex != null )
540         {
541             url.addParameter( PARAMETER_DIRECTORY, strDirectoryIndex );
542         }
543 
544         return url.getUrl(  );
545     }
546 
547     /**
548      * Copies data from an input stream to an output stream.
549      * @param inStream The input stream
550      * @param outStream The output stream
551      * @throws IOException If an I/O error occurs
552      */
553     private static void copyStream( InputStream inStream, OutputStream outStream )
554         throws IOException
555     {
556         BufferedInputStream inBufferedStream = new BufferedInputStream( inStream );
557         BufferedOutputStream outBufferedStream = new BufferedOutputStream( outStream );
558 
559         int nByte = 0;
560 
561         while ( ( nByte = inBufferedStream.read(  ) ) > -1 )
562         {
563             outBufferedStream.write( nByte );
564         }
565 
566         outBufferedStream.close(  );
567         inBufferedStream.close(  );
568     }
569 
570     /**
571      * Returns the total size of a directory.
572      * @param directory The directory
573      * @return The total size
574      */
575     private static long getDirectorySize( File directory )
576     {
577         long lResult = 0;
578 
579         // We use a Stack (LIFO) to keep track of the unprocessed directories
580         Stack<File> dirsToProcess = new Stack<File>(  );
581 
582         // The stack is initialized with the main directory
583         dirsToProcess.push( directory );
584 
585         // Loop until all directories have been processed
586         while ( !dirsToProcess.empty(  ) )
587         {
588             // Get a new directory from the stack
589             File currentDir = dirsToProcess.pop(  );
590 
591             // Don't forget the directory's own size!
592             lResult += currentDir.length(  );
593 
594             // Add the local files' size to the global size
595             File[] files = currentDir.listFiles( fileFilter );
596 
597             for ( int i = 0; i < files.length; i++ )
598             {
599                 lResult += files[i].length(  );
600             }
601 
602             // Add the sub-directories to the stack
603             File[] subDirs = currentDir.listFiles( dirFilter );
604 
605             for ( int i = 0; i < subDirs.length; i++ )
606             {
607                 dirsToProcess.push( subDirs[i] );
608             }
609         }
610 
611         return lResult;
612     }
613 
614     /**
615      * Deletes a directory recursively.
616      *
617      * @param directory The directory to delete
618      */
619     private static void deleteDirectory( File directory )
620     {
621         // We use a Stack (LIFO) to keep track of the directories to delete
622         Stack<File> dirsToDelete = new Stack<File>(  );
623 
624         // The stack is initialized with the main directory
625         dirsToDelete.push( directory );
626 
627         // Loop until all directories have been deleted
628         while ( !dirsToDelete.empty(  ) )
629         {
630             // Look at the directory on top of the stack (don't remove it!)
631             File currentDir = (File) dirsToDelete.peek(  );
632 
633             // Are there any subdirectories?
634             File[] subDirs = currentDir.listFiles( dirFilter );
635 
636             if ( subDirs.length > 0 )
637             {
638                 // If so, add them to the stack
639                 for ( int i = 0; i < subDirs.length; i++ )
640                 {
641                     dirsToDelete.push( subDirs[i] );
642                 }
643             }
644             else
645             {
646                 // If not, delete all files in the directory
647                 File[] files = currentDir.listFiles( fileFilter );
648 
649                 for ( int i = 0; i < files.length; i++ )
650                 {
651                     files[i].delete(  );
652                 }
653 
654                 // Then delete the directory
655                 currentDir.delete(  );
656 
657                 // Then remove the directory from the stack
658                 dirsToDelete.pop(  );
659             }
660         }
661     }
662 }