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.formengine.web;
35  
36  import java.io.File;
37  import java.io.IOException;
38  import java.io.StringWriter;
39  import java.text.MessageFormat;
40  import java.util.ArrayList;
41  import java.util.Collection;
42  import java.util.Enumeration;
43  import java.util.HashMap;
44  import java.util.Iterator;
45  import java.util.List;
46  import java.util.Locale;
47  import java.util.Map;
48  import java.util.Properties;
49  
50  import javax.servlet.http.HttpServletRequest;
51  import javax.servlet.http.HttpSession;
52  import javax.xml.bind.JAXBException;
53  import javax.xml.bind.Marshaller;
54  import javax.xml.transform.stream.StreamSource;
55  
56  import org.apache.commons.fileupload.FileItem;
57  import org.apache.commons.lang.StringUtils;
58  
59  import fr.paris.lutece.plugins.blobstore.service.BlobStoreFileItem;
60  import fr.paris.lutece.plugins.blobstore.service.IBlobStoreService;
61  import fr.paris.lutece.plugins.blobstore.service.NoSuchBlobException;
62  import fr.paris.lutece.plugins.formengine.business.Notice;
63  import fr.paris.lutece.plugins.formengine.business.NoticeFilter;
64  import fr.paris.lutece.plugins.formengine.business.NoticeGroup;
65  import fr.paris.lutece.plugins.formengine.business.NoticeGroupFilter;
66  import fr.paris.lutece.plugins.formengine.business.NoticeGroupHome;
67  import fr.paris.lutece.plugins.formengine.business.NoticeHome;
68  import fr.paris.lutece.plugins.formengine.business.jaxb.formdefinition.CheckFieldRules;
69  import fr.paris.lutece.plugins.formengine.business.jaxb.formdefinition.CheckRule;
70  import fr.paris.lutece.plugins.formengine.business.jaxb.formdefinition.CheckSubFormRules;
71  import fr.paris.lutece.plugins.formengine.business.jaxb.formdefinition.Choice;
72  import fr.paris.lutece.plugins.formengine.business.jaxb.formdefinition.ChoiceList;
73  import fr.paris.lutece.plugins.formengine.business.jaxb.formdefinition.Field;
74  import fr.paris.lutece.plugins.formengine.business.jaxb.formdefinition.Fields;
75  import fr.paris.lutece.plugins.formengine.business.jaxb.formdefinition.FileName;
76  import fr.paris.lutece.plugins.formengine.business.jaxb.formdefinition.FormElements;
77  import fr.paris.lutece.plugins.formengine.business.jaxb.formdefinition.Notices;
78  import fr.paris.lutece.plugins.formengine.business.jaxb.formdefinition.ObjectFactory;
79  import fr.paris.lutece.plugins.formengine.service.FormsRegistrationService;
80  import fr.paris.lutece.plugins.formengine.service.XslOutputPropertiesLoader;
81  import fr.paris.lutece.plugins.formengine.service.validator.FieldValidator;
82  import fr.paris.lutece.plugins.formengine.service.validator.SubFormValidator;
83  import fr.paris.lutece.plugins.formengine.service.validator.ValidatorFactory;
84  import fr.paris.lutece.portal.service.content.PageData;
85  import fr.paris.lutece.portal.service.fileupload.FileUploadService;
86  import fr.paris.lutece.portal.service.html.XmlTransformerService;
87  import fr.paris.lutece.portal.service.includes.PageInclude;
88  import fr.paris.lutece.portal.service.includes.PageIncludeService;
89  import fr.paris.lutece.portal.service.plugin.Plugin;
90  import fr.paris.lutece.portal.service.plugin.PluginService;
91  import fr.paris.lutece.portal.service.security.UserNotSignedException;
92  import fr.paris.lutece.portal.service.template.AppTemplateService;
93  import fr.paris.lutece.portal.service.util.AppException;
94  import fr.paris.lutece.portal.service.util.AppHTTPSService;
95  import fr.paris.lutece.portal.service.util.AppLogService;
96  import fr.paris.lutece.portal.service.util.AppPathService;
97  import fr.paris.lutece.portal.service.util.AppPropertiesService;
98  import fr.paris.lutece.portal.web.upload.MultipartHttpServletRequest;
99  import fr.paris.lutece.util.ReferenceItem;
100 import fr.paris.lutece.util.ReferenceList;
101 import fr.paris.lutece.util.filesystem.UploadUtil;
102 import fr.paris.lutece.util.html.HtmlTemplate;
103 
104 
105 /**
106  * This classe defines the main characteristics and methods of a subform
107  * @see #getXslFilesNames
108  */
109 public abstract class SubForm
110 {
111     public static final String PARAMETER_XSL_BASE_URL = "baseUrl";
112     private static final String SESSION_ATTRIBUTE_SUBFORM_ALLOWED = "SUBFORM_ALLOWED";
113     private static final String SESSION_ATTRIBUTE_FORM_ELEMENTS = "FORM_ELEMENTS";
114     private static final String TEMPLATE_MAIN = "/skin/plugins/formengine/subform_main_template.html";
115     private static final String TEMPLATE_HEADER = "/skin/plugins/formengine/subform_template_header.html";
116     private static final String TEMPLATE_ERROR = "/skin/plugins/formengine/error.html";
117     private static final String TEMPLATE_ACCESS_FORBIDDEN = "/skin/plugins/formengine/access_forbidden.html";
118     private static final String MARK_FORM_NAME = "form_name";
119     private static final String MARK_WARNING_MESSAGE_WS = "warning_message_ws";
120     private static final String BOOKMARK_ERROR_MESSAGE = "@error_message@";
121     private static final String BOOKMARK_ERROR_LIST = "@error_list@";
122     private static final String BOOKMARK_HEADER = "@subform_header@";
123     private static final String BOOKMARK_SUMMARY = "@selection_summary@";
124     private static final String BOOKMARK_SUBFORM_CONTENT = "@subform_content@";
125     private static final String BOOKMARK_SUBFORM_TITLE = "@subform_title@";
126     private static final String BOOKMARK_FORM_TITLE = "@form_title@";
127     private static final String BOOKMARK_PORTAL_URL = "@portal_url@";
128     private static final String PROPERTY_FRAGMENT_URL_FOR_LAST = ".url.exit";
129     private static final String PROPERTY_MESSAGE_EXISTING_FILE = "formengine.validator.message.existingFile";
130     private static final String PARAMETER_XSL_FORM_NAME = "formName";
131     private static final String PARAMETER_XSL_SUBFORM_NAME = "subFormName";
132 
133     /**
134      * This variable is used in XSLTransformation, to change default form action
135      * base url with the baseUrl given.
136      */
137     private static final String PARAMETER_XSL_PORTAL_URI = "portalURI";
138     private static final String PARAMETER_XSL_URL_EXIT = "defaultUrlToExit";
139     private static final String PARAMETER_XSL_UPLOAD_SUBMIT_PREFIX = "uploadSubmitPrefix";
140     private static final String PARAMETER_XSL_UPLOAD_DELETE_PREFIX = "uploadDeletePrefix";
141     private static final String PARAMETER_XSL_UPLOAD_CHECKBOX_PREFIX = "uploadCheckboxPrefix";
142     private static final String PLUGIN_NAME = "formengine";
143     private String _strTitle;
144     private String _strName;
145     private String _strPortalURI;
146     private Form _formParent;
147     private SubForm _subFormNext;
148     private SubForm _subFormPrevious;
149     private FormElements _initialFormElements;
150 
151     /**
152      * Constructor
153      */
154     public SubForm( )
155     {
156     }
157 
158     ////////////////////////////////////////////////////////////////////////////
159     // Abstract methods
160 
161     /**
162      * This method should be overriden.
163      * It defines the name of the file associated with this subform
164      * @return The absolute path for xsl stylesheets
165      */
166     protected abstract String getXslFormElementsFileName( );
167 
168     /**
169      * Performs an action depending on the given action name
170      * and possibly on other parameters in request
171      * @param strActionName the action to perform
172      * @param request the http request
173      * @return the name of the subform to display after the requested action is
174      *         performed.
175      */
176     public abstract String doAction( String strActionName, HttpServletRequest request );
177 
178     /**
179      * Displays the summary part of the screen.
180      * @param request the http request
181      * @return the html code corresponding to the summary
182      */
183     protected abstract String displaySummary( HttpServletRequest request );
184 
185     ////////////////////////////////////////////////////////////////////////////
186     // Default implementation
187 
188     /**
189      * This method provides a default implementation for the form display
190      * It might be overriden if needed :
191      * <ul>
192      * <li>to initialize some fields from db (eg. combos)</li>
193      * <li>to pass parameters to the stylesheet</li>
194      * </ul>
195      * @param request the http request
196      * @return the html code corresponding to the form part
197      */
198     protected String displayForm( HttpServletRequest request )
199     {
200         FormElements formElements = this.getFormElements( request );
201 
202         // build the form page
203         String strForm = "";
204 
205         // generate html code via xsl transform
206         String strBaseUrl;
207         Map formParams = new HashMap( );
208 
209         if ( AppHTTPSService.isHTTPSSupportEnabled( ) )
210         {
211             strBaseUrl = AppHTTPSService.getHTTPSUrl( request );
212 
213             HttpSession session = request.getSession( );
214 
215             if ( session != null )
216             {
217                 session.setAttribute( AppPathService.SESSION_BASE_URL, AppPathService.getBaseUrl( request ) );
218             }
219         }
220         else
221         {
222             strBaseUrl = AppPathService.getBaseUrl( request );
223         }
224 
225         formParams.put( PARAMETER_XSL_BASE_URL, strBaseUrl );
226 
227         // merge eventually asynchronously uploaded files (user may have refreshed page and files are still available)
228         for ( Field field : getUploadFields( request ) )
229         {
230             mergeAsynchronousUploadedFiles( request, field.getName( ) );
231         }
232 
233         strForm = this.buildHtmlForm( formElements, formParams );
234 
235         return strForm;
236     }
237 
238     /**
239      * 
240      * @param request The HttpServletRequest
241      * @return The html code to display
242      */
243     protected String displayHeader( HttpServletRequest request )
244     {
245         HtmlTemplate template = AppTemplateService.getTemplate( TEMPLATE_HEADER );
246         template.substitute( BOOKMARK_FORM_TITLE, this.getParentForm( ).getTitle( ) );
247         template.substitute( BOOKMARK_SUBFORM_TITLE, this.getTitle( ) );
248 
249         // Added in v1.3
250         String strPortalUrl = request.getRequestURI( );
251 
252         if ( AppHTTPSService.isHTTPSSupportEnabled( ) )
253         {
254             strPortalUrl = AppPathService.getBaseUrl( request ) + strPortalUrl;
255         }
256 
257         template.substitute( BOOKMARK_PORTAL_URL, strPortalUrl );
258 
259         return template.getHtml( );
260     }
261 
262     /**
263      * This method should display. This is the main method called to display a
264      * subform.
265      * <ul>
266      * <li>the recap of what is already submitted</li>
267      * <li>the error list</li>
268      * <li>the form elements</li>
269      * </ul>
270      * @param request the http request
271      * @return the html code to display
272      * @throws UserNotSignedException if user is not signed on
273      */
274     public String display( HttpServletRequest request ) throws UserNotSignedException
275     {
276         if ( !this.getParentForm( ).checkIsAccessToSubFormAllowed( request, this.getName( ) ) )
277         {
278             HtmlTemplate templateForbidden = AppTemplateService.getTemplate( TEMPLATE_ACCESS_FORBIDDEN );
279 
280             return templateForbidden.getHtml( );
281         }
282 
283         HashMap<String, Object> model = new HashMap<String, Object>( );
284 
285         List<PageInclude> listIncludes = PageIncludeService.getIncludes( );
286         PageData data = new PageData( );
287 
288         for ( PageInclude pic : listIncludes )
289         {
290             pic.fillTemplate( model, data, 0, request ); /*
291                                                           * 0 : only for site
292                                                           * mode
293                                                           */
294         }
295 
296         model.put( MARK_FORM_NAME, this.getName( ) );
297 
298         if ( StringUtils.isNotBlank( (String) request.getSession( ).getAttribute(
299                 SharedConstants.SESSION_ATTRIBUTE_WARNING_ERROR_WS ) ) )
300         {
301             model.put( MARK_WARNING_MESSAGE_WS,
302                     request.getSession( ).getAttribute( SharedConstants.SESSION_ATTRIBUTE_WARNING_ERROR_WS ) );
303         }
304 
305         HtmlTemplate template = AppTemplateService.getTemplate( TEMPLATE_MAIN, Locale.getDefault( ), model );
306 
307         // display the header
308         template.substitute( BOOKMARK_HEADER, this.displayHeader( request ) );
309 
310         // Added in v1.3
311         String strPortalUrl = request.getRequestURI( );
312         template.substitute( BOOKMARK_PORTAL_URL, strPortalUrl );
313 
314         // Added in v2.0.8 for remove the Request url in session
315         this.setPortalURI( request.getRequestURI( ) );
316 
317         // display recap area
318         template.substitute( BOOKMARK_SUMMARY, this.displaySummary( request ) );
319 
320         // display error message
321         template.substitute( BOOKMARK_ERROR_LIST, this.getFormattedErrors( request ) );
322 
323         // display the form area
324         template.substitute( BOOKMARK_SUBFORM_CONTENT, this.displayForm( request ) );
325 
326         request.getSession( ).removeAttribute( SharedConstants.SESSION_ATTRIBUTE_WARNING_ERROR_WS );
327 
328         return template.getHtml( );
329     }
330 
331     /**
332      * This method should display. This is the main method called to display a
333      * subform.
334      * <ul>
335      * <li>the recap of what is already submitted</li>
336      * <li>the error list</li>
337      * <li>the form elements</li>
338      * </ul>
339      * @param request the http request
340      * @param strMailTemplate template for mail
341      * @return the html code to display
342      * @throws UserNotSignedException if user is not signed on
343      */
344     public String displayForMail( HttpServletRequest request, String strMailTemplate ) throws UserNotSignedException
345     {
346         if ( !this.getParentForm( ).checkIsAccessToSubFormAllowed( request, this.getName( ) ) )
347         {
348             HtmlTemplate templateForbidden = AppTemplateService.getTemplate( TEMPLATE_ACCESS_FORBIDDEN );
349 
350             return templateForbidden.getHtml( );
351         }
352 
353         HtmlTemplate template = AppTemplateService.getTemplate( strMailTemplate );
354 
355         // display the header
356         //template.substitute( BOOKMARK_HEADER, this.displayHeader( request ) );
357 
358         // Added in v1.3
359         //String strPortalUrl = request.getRequestURI(  );
360         //template.substitute( BOOKMARK_PORTAL_URL, strPortalUrl );
361 
362         // Added in v2.0.8 for remove the Request url in session
363         this.setPortalURI( request.getRequestURI( ) );
364 
365         // display recap area
366         template.substitute( BOOKMARK_SUMMARY, this.displaySummaryForMail( request ) );
367 
368         // display error message
369         //template.substitute( BOOKMARK_ERROR_LIST, this.getFormattedErrors( request ) );
370 
371         // display the form area
372         //template.substitute( BOOKMARK_SUBFORM_CONTENT, this.displayForm( request ) );
373         return template.getHtml( );
374     }
375 
376     /**
377      * Display the summary for the mail
378      * @param request the HTTP request
379      * @return the summary for mail
380      */
381     protected String displaySummaryForMail( HttpServletRequest request )
382     {
383         return null;
384     }
385 
386     /**
387      * Get the title of the subform
388      * @return the subform title
389      */
390     public String getTitle( )
391     {
392         return _strTitle;
393     }
394 
395     /**
396      * Set the title of the subform
397      * @param strTitle the title to set
398      */
399     public void setTitle( String strTitle )
400     {
401         _strTitle = strTitle;
402     }
403 
404     /**
405      * Get the name of the subform
406      * @return the subform name
407      */
408     public String getName( )
409     {
410         return _strName;
411     }
412 
413     /**
414      * Set the name of the subform
415      * @param strName the name to set
416      */
417     public void setName( String strName )
418     {
419         _strName = strName;
420     }
421 
422     /**
423      * Get the protal URI of subForm
424      * @return the portal URI
425      */
426     public String getPortalURI( )
427     {
428         return _strPortalURI;
429     }
430 
431     /**
432      * Set the protal URI of subForm
433      * @param strPortalURI the new portal URI
434      */
435     public void setPortalURI( String strPortalURI )
436     {
437         _strPortalURI = strPortalURI;
438     }
439 
440     /**
441      * setter for the parent form.
442      * @param formParent The parent Form
443      */
444     public void setParentForm( Form formParent )
445     {
446         _formParent = formParent;
447     }
448 
449     /**
450      * Accessor to the parent form.
451      * @return the parent form
452      */
453     public Form getParentForm( )
454     {
455         return _formParent;
456     }
457 
458     /**
459      * setter for the next subform.
460      * @param subFormNext tje next subform
461      */
462     public void setNextSubForm( SubForm subFormNext )
463     {
464         _subFormNext = subFormNext;
465     }
466 
467     /**
468      * Accessor to the next subform.
469      * @return the next subform
470      */
471     public SubForm getNextSubForm( )
472     {
473         return _subFormNext;
474     }
475 
476     /**
477      * setter for the previous subform.
478      * @param subFormPrevious the previous subform
479      */
480     public void setPreviousSubForm( SubForm subFormPrevious )
481     {
482         _subFormPrevious = subFormPrevious;
483     }
484 
485     /**
486      * Accessor to the previous subform.
487      * @return the previous subform
488      */
489     public SubForm getPreviousSubForm( )
490     {
491         return _subFormPrevious;
492     }
493 
494     /**
495      * Util to convert Xml docs into Html docs using XSLT stylesheets.
496      * Will try to use existing database file <strong>BEFORE</strong> the given
497      * xsl file name. Physical file is kept for compatibility.
498      * @param strXmlCode The Xml
499      * @param strXslFileName The Xsl stylesheet <strong>filename</strong>. Note
500      *            that the path is retrieved from the getXslDirectoryPath method
501      *            of the parent form
502      * @param params a map of parameters to pass to the stylesheet
503      * @return The Html code
504      */
505     protected String getHtml( String strXmlCode, String strXslFileName, Map params )
506     {
507         String strHtml;
508         StreamSource sourceStyleSheet = null;
509 
510         String strXslDirectory = this.getParentForm( ).getXslDirectoryPath( );
511         String strStyleSheetId;
512 
513         String strXslPath = strXslDirectory + File.separator + strXslFileName;
514 
515         File fileXsl = new File( strXslPath );
516         sourceStyleSheet = new StreamSource( fileXsl );
517         strStyleSheetId = strXslPath;
518 
519         Properties outputProperties = XslOutputPropertiesLoader.getOutputProperties( );
520 
521         try
522         {
523             XmlTransformerService xmlTransformerService = new XmlTransformerService( );
524             strHtml = xmlTransformerService.transformBySourceWithXslCache( strXmlCode, sourceStyleSheet,
525                     strStyleSheetId, params, outputProperties );
526         }
527         catch ( Exception e )
528         {
529             throw new AppException( "Formengine : an error occurred during XSL transformation - form : "
530                     + this.getParentForm( ).getName( ) + " - subform " + this.getName( ), e );
531         }
532 
533         return strHtml;
534     }
535 
536     /**
537      * Util to convert Xml docs into Html docs using XSLT stylesheets
538      * @param strXmlCode The Xml
539      * @param strXslFileName The Xsl stylesheet <strong>filename</strong>. Note
540      *            that the path is retrieved from the getXslDirectoryPath method
541      *            of the parent form
542      * @return The Html code
543      */
544     protected String getHtml( String strXmlCode, String strXslFileName )
545     {
546         return getHtml( strXmlCode, strXslFileName, null );
547     }
548 
549     /**
550      * Returns a default Html code to display filling errors
551      * @param request The HTTP requesr
552      * @return Errors presented in HTML
553      */
554     protected String getFormattedErrors( HttpServletRequest request )
555     {
556         String[] errors = getParentForm( ).getErrors( request );
557         StringBuilder strErrors = new StringBuilder( );
558 
559         if ( ( errors != null ) && ( errors.length > 0 ) )
560         {
561             for ( int i = 0; i < errors.length; i++ )
562             {
563                 HtmlTemplate tError = AppTemplateService.getTemplate( TEMPLATE_ERROR );
564                 //TODO
565                 // recuperer nom du champ
566                 // generer url avec #nom_du_champ
567                 // tError.substitute(balise ouvrante
568                 // tError.substitute(balise fermante
569                 tError.substitute( BOOKMARK_ERROR_MESSAGE, errors[i] );
570                 strErrors.append( tError.getHtml( ) );
571             }
572         }
573         else
574         {
575             strErrors.append( "" );
576         }
577 
578         return strErrors.toString( );
579     }
580 
581     /**
582      * Validates an given object.
583      * Before to add an object to the session, it should be validated.
584      * This method check the given object against the associated schema.t
585      * @param objectToValidate the object on which validation should be
586      *            performed
587      * @return true if validation has been successfull, false otherwise.
588      */
589     protected boolean validate( Object objectToValidate )
590     {
591         boolean bValid = true;
592 
593         try
594         {
595             bValid = this.getParentForm( ).getXmlValidator( ).validate( objectToValidate );
596         }
597         catch ( JAXBException e )
598         {
599             AppLogService.error( e.getMessage( ), e );
600         }
601 
602         return bValid;
603     }
604 
605     /**
606      * Add a variable in session to state wether
607      * the access to the current subform is allowed
608      * @param request the http request
609      * @param bIsAllowed true to allow access to the form, false otherwise
610      */
611     public void setIsSubFormAllowed( HttpServletRequest request, boolean bIsAllowed )
612     {
613         // Store the object in a session attribute
614         this.setSessionAttribute( request, SESSION_ATTRIBUTE_SUBFORM_ALLOWED, Boolean.valueOf( bIsAllowed ) );
615     }
616 
617     /**
618      * Get the variable in session that states wether
619      * the access to the current subform is allowed
620      * @param request the http request
621      * @return true if the subform access is allowed, false otherwise
622      */
623     public boolean getIsSubFormAllowed( HttpServletRequest request )
624     {
625         Boolean bIsAllowed = (Boolean) this.getSessionAttribute( request, SESSION_ATTRIBUTE_SUBFORM_ALLOWED );
626 
627         if ( bIsAllowed == null )
628         {
629             return false;
630         }
631         else
632         {
633             return bIsAllowed.booleanValue( );
634         }
635     }
636 
637     /**
638      * Fill the fields with the parameters in request.
639      * This method should be called before performing any action, to update the
640      * fields
641      * with the new values passed in request.
642      * @param request the http request
643      */
644     public void fillFields( HttpServletRequest request )
645     {
646         FormElements formElements = getFormElements( request );
647         Fields fields = formElements.getFields( );
648         Collection<Field> colFields = fields.getField( );
649 
650         for ( Field field : colFields )
651         {
652             String strName = field.getName( );
653 
654             if ( request.getParameter( strName ) != null )
655             {
656                 field.setValue( request.getParameter( strName ) );
657             }
658             else
659             {
660                 field.setValue( "" );
661             }
662 
663             if ( SharedConstants.FIELD_TYPE_UPLOAD.equals( field.getType( ) ) )
664             {
665                 // merge with asynchronous file uploaded (if any)
666                 mergeAsynchronousUploadedFiles( request, field.getName( ) );
667             }
668         }
669     }
670 
671     /**
672      * Merges asynchronous uploaded files with already session bounded ones.
673      * @param request the request
674      * @param strFieldName the upload field name
675      */
676     public void mergeAsynchronousUploadedFiles( HttpServletRequest request, String strFieldName )
677     {
678         mergeAsynchronousUploadedFiles( request, strFieldName, null );
679     }
680 
681     /**
682      * Merges asynchronous uploaded files with already session bounded ones.
683      * @param request the request
684      * @param strFieldName the upload field name
685      * @param formErrorsList fills the formErrorsList if not null.
686      */
687     public void mergeAsynchronousUploadedFiles( HttpServletRequest request, String strFieldName,
688             FormErrorsList formErrorsList )
689     {
690         Field field = getFieldFromName( request, strFieldName );
691 
692         // merge with asynchronous file uploaded (if any)
693         List<FileItem> listAsynchronousFileItem = getFileItems( request.getSession( ).getId( ), field.getName( ) );
694 
695         if ( listAsynchronousFileItem != null )
696         {
697             Iterator<FileItem> itFileItems = listAsynchronousFileItem.iterator( );
698 
699             while ( itFileItems.hasNext( ) )
700             {
701                 FileItem fileItem = itFileItems.next( );
702                 // this.addFileItem( request, field.getName(  ), fileItem );
703                 addFileItemToUploadedFiles( request, field, fileItem, formErrorsList );
704 
705                 // we do not need to store the file anymore for the session id - using regular session.
706                 itFileItems.remove( );
707             }
708         }
709     }
710 
711     /**
712      * Validate the data stored in the fields element against the checkrules
713      * specified.
714      * This method should be called when submitting a form, in order to check
715      * the validity
716      * of the data submitted. If a field is not valid, a list of errors is
717      * returned.
718      * @param request the http request
719      * @return true if all the fields are valid. If false, errors are written to
720      *         the parent form's array of errors.
721      */
722     public boolean validateFields( HttpServletRequest request )
723     {
724         boolean bValidate = true;
725 
726         FormElements formElements = this.getFormElements( request );
727         Fields fields = formElements.getFields( );
728 
729         // try to validate each field separatly
730         Collection<Field> colFields = fields.getField( );
731 
732         //Iterator i = colFields.iterator(  );
733         FormErrorsList listErrors = new FormErrorsList( );
734 
735         for ( Field field : colFields )
736         {
737             CheckFieldRules checkFieldRules = field.getCheckFieldRules( );
738 
739             if ( checkFieldRules != null )
740             {
741                 Collection<CheckRule> colRules = checkFieldRules.getCheckRule( );
742 
743                 for ( CheckRule rule : colRules )
744                 {
745                     FieldValidator validator = (FieldValidator) ValidatorFactory.getValidator( rule );
746                     bValidate &= validator.validate( field, listErrors );
747                 }
748             }
749         }
750 
751         //check rules that apply to more than one field at a time
752         CheckSubFormRules checkSubFormRules = formElements.getCheckSubFormRules( );
753 
754         // TODO enlever la 1ere condition pour avoir toutes les erreurs a la fois ??
755         if ( ( bValidate ) && ( checkSubFormRules != null ) )
756         {
757             Collection<CheckRule> colRules = checkSubFormRules.getCheckRule( );
758 
759             for ( CheckRule rule : colRules )
760             {
761                 SubFormValidator validator = (SubFormValidator) ValidatorFactory.getValidator( rule );
762                 bValidate &= validator.validate( this, request, listErrors );
763             }
764         }
765 
766         //Added in 2.0.8, replace anchors (#) with full url into all messages
767         listErrors.convertAnchorsToFullUrl( request, this.getName( ) );
768 
769         // add the errors to the form
770         for ( int k = 0; k < listErrors.size( ); k++ )
771         {
772             getParentForm( ).addErrorMessage( request, listErrors.get( k ) );
773         }
774 
775         return bValidate;
776     }
777 
778     /**
779      * Validate the data stored in an upload field element against the
780      * checkrules specified for that field.
781      * This method is called automatically by the upload methods.
782      * Fills the formErrorList if not null.
783      * @param request the HTTP request
784      * @param field the field to validate
785      * @param listFormErrors the list of error.
786      * @return true if the field is valid. If false, errors are written to the
787      *         parent form's array of errors.
788      */
789     private boolean validateUploadField( HttpServletRequest request, Field field, FormErrorsList listFormErrors )
790     {
791         boolean bValidate = true;
792 
793         // create the form error list
794         FormErrorsList listErrors;
795 
796         if ( listFormErrors != null )
797         {
798             listErrors = listFormErrors;
799         }
800         else
801         {
802             listErrors = new FormErrorsList( );
803         }
804 
805         CheckFieldRules checkFieldRules = field.getCheckFieldRules( );
806 
807         if ( checkFieldRules != null )
808         {
809             Collection<CheckRule> colRules = checkFieldRules.getCheckRule( );
810 
811             for ( CheckRule rule : colRules )
812             {
813                 FieldValidator validator = (FieldValidator) ValidatorFactory.getValidator( rule );
814                 bValidate &= validator.validate( field, listErrors );
815             }
816         }
817 
818         //Added in 2.0.8, replace anchors (#) with full url into all messages
819         listErrors.convertAnchorsToFullUrl( request, this.getName( ) );
820 
821         // add the errors to the form
822         for ( int k = 0; k < listErrors.size( ); k++ )
823         {
824             getParentForm( ).addErrorMessage( request, listErrors.get( k ) );
825         }
826 
827         return bValidate;
828     }
829 
830     /**
831      * Validates the upload fields
832      * @param request the request
833      * @param field the field
834      * @return the {@link FormErrorsList}
835      */
836     private FormErrorsList validateUploadField2( HttpServletRequest request, Field field )
837     {
838         return null;
839     }
840 
841     /**
842      * Returns the fields element associated with that subform.<br/>
843      * If the element is not found in session, a new one is created from the
844      * init element "_initialFields"
845      * and added to the session.<br/>
846      * The formelements element in session is returned
847      * @param request the http request
848      * @return the formelements object in session
849      */
850     public FormElements getFormElements( HttpServletRequest request )
851     {
852         // Retrieve the object from a session attribute
853         FormElements formElements = (FormElements) this.getSessionAttribute( request, SESSION_ATTRIBUTE_FORM_ELEMENTS );
854 
855         if ( formElements == null )
856         {
857             formElements = getinitializedFormElements( );
858             setFormElements( request, formElements );
859         }
860 
861         return formElements;
862     }
863 
864     /**
865      * Modify the formelements objectt in session
866      * @param request the http request
867      * @param formElements the element to add in session
868      */
869     public void setFormElements( HttpServletRequest request, FormElements formElements )
870     {
871         // Store the object in a session attribute
872         this.setSessionAttribute( request, SESSION_ATTRIBUTE_FORM_ELEMENTS, formElements );
873     }
874 
875     /**
876      * Remove the current formelement object from the session, which has an
877      * effect to reinit the later.
878      * @param request the http request
879      */
880     public void clearFormElements( HttpServletRequest request )
881     {
882         // remove the associated attribute in session
883         this.removeSessionAttribute( request, SESSION_ATTRIBUTE_FORM_ELEMENTS );
884     }
885 
886     /**
887      * Set the initialFormElements.
888      * This is called by the form registration service.
889      * @param formElements the elemnts of forms
890      */
891     public void initFormElements( FormElements formElements )
892     {
893         _initialFormElements = formElements;
894     }
895 
896     /**
897      * Create a new fields object initialised with the _initialFields values and
898      * elements
899      * @return a copy of _initialFields
900      */
901     protected FormElements getinitializedFormElements( )
902     {
903         FormElements formElements = null;
904 
905         ObjectFactory fieldFactory = new ObjectFactory( );
906         formElements = fieldFactory.createFormElements( );
907 
908         if ( _initialFormElements != null )
909         {
910             //  we won't modify the checkrules, so we don't need to clone it.
911             formElements.setCheckSubFormRules( _initialFormElements.getCheckSubFormRules( ) );
912 
913             // we won't modify the buttons, so we don't need to clone them.
914             formElements.setButtons( _initialFormElements.getButtons( ) );
915 
916             //copy notices
917             formElements.setNotices( _initialFormElements.getNotices( ) );
918 
919             Fields initialFields = _initialFormElements.getFields( );
920             List<Field> initialFieldList = initialFields.getField( );
921             Fields newFields = fieldFactory.createFields( );
922 
923             for ( Field initialField : initialFieldList )
924             {
925                 Field newField = fieldFactory.createField( );
926 
927                 newField.setLabel( initialField.getLabel( ) );
928                 newField.setName( initialField.getName( ) );
929                 newField.setType( initialField.getType( ) );
930                 newField.setValue( initialField.getValue( ) );
931                 newField.setCheckboxValue( initialField.getCheckboxValue( ) );
932                 newField.setAdditionalInfo( initialField.getAdditionalInfo( ) );
933                 newField.setRows( initialField.getRows( ) );
934                 newField.setCols( initialField.getCols( ) );
935 
936                 // we won't modify the checkrules, so we don't need to clone it.
937                 newField.setCheckFieldRules( initialField.getCheckFieldRules( ) );
938 
939                 ChoiceList initialChoices = initialField.getChoiceList( );
940                 ChoiceList newChoiceList = fieldFactory.createChoiceList( );
941 
942                 if ( initialChoices != null )
943                 {
944                     List<Choice> initialChoicesList = initialChoices.getChoice( );
945 
946                     for ( Choice initialChoice : initialChoicesList )
947                     {
948                         Choice newChoice = fieldFactory.createChoice( );
949 
950                         newChoice.setLabel( initialChoice.getLabel( ) );
951                         newChoice.setValue( initialChoice.getValue( ) );
952 
953                         newChoiceList.getChoice( ).add( newChoice );
954                     }
955                 }
956 
957                 newField.setChoiceList( newChoiceList );
958 
959                 newFields.getField( ).add( newField );
960             }
961 
962             formElements.setFields( newFields );
963         }
964 
965         return formElements;
966     }
967 
968     /**
969      * Get a field of a given name from a given fields element
970      * @param request the HttpServletRequest
971      * @param strName the name of the field to find
972      * @return the field of name given in parameter. If nothing is found, return
973      *         null. If one of the parameters is null, return null.
974      */
975     public Field getFieldFromName( HttpServletRequest request, String strName )
976     {
977         FormElements formElements = getFormElements( request );
978         Fields fields = formElements.getFields( );
979 
980         // test that there are fields to search into
981         if ( ( strName == null ) || ( fields == null ) || ( fields.getField( ) == null ) )
982         {
983             return null;
984         }
985 
986         // get the fields associated with the current subform
987         List<Field> fieldsList = fields.getField( );
988 
989         for ( Field field : fieldsList )
990         {
991             if ( field.getName( ).equals( strName.trim( ) ) )
992             {
993                 return field;
994             }
995         }
996 
997         // if we have found nothing, return null
998         return null;
999     }
1000 
1001     /**
1002      * Build the choice list of a field from the given reference list.
1003      * This is useful when the content of a combo field has to be loaded
1004      * from a database
1005      * @param field the field whose choice list should be set
1006      * @param referenceList the list containing the data to load into the choice
1007      *            list
1008      */
1009     public void setChoiceList( Field field, ReferenceList referenceList )
1010     {
1011         ObjectFactory formFactory = new ObjectFactory( );
1012         ChoiceList choices = formFactory.createChoiceList( );
1013         addElementsToChoiceList( choices, referenceList );
1014         field.setChoiceList( choices );
1015     }
1016 
1017     /**
1018      * Add the elements in given reference list to given field's choice list.
1019      * @param field the field whose choice list should be updated
1020      * @param referenceList the list containing the data to load into the choice
1021      *            list
1022      */
1023     public void appendToChoiceList( Field field, ReferenceList referenceList )
1024     {
1025         ChoiceList choices = field.getChoiceList( );
1026 
1027         if ( choices == null )
1028         {
1029             setChoiceList( field, referenceList );
1030         }
1031         else
1032         {
1033             addElementsToChoiceList( choices, referenceList );
1034         }
1035     }
1036 
1037     /**
1038      * Perform the insertion the elements from the given reference list into the
1039      * given choiceList
1040      * @param choices the list of choices where to add elements
1041      * @param referenceList the elements to add
1042      */
1043     private void addElementsToChoiceList( ChoiceList choices, ReferenceList referenceList )
1044     {
1045         ObjectFactory formFactory = new ObjectFactory( );
1046 
1047         if ( referenceList != null )
1048         {
1049             List<Choice> choiceList = choices.getChoice( );
1050 
1051             for ( ReferenceItem item : referenceList )
1052             {
1053                 String strValue = item.getCode( );
1054                 String strLabel = item.getName( );
1055                 Choice choice = formFactory.createChoice( );
1056                 choice.setLabel( strLabel );
1057                 choice.setValue( strValue );
1058                 choiceList.add( choice );
1059             }
1060         }
1061     }
1062 
1063     /**
1064      * Build the html code corresponding to the given form elements.
1065      * If you want your form to support HTTPS, you will have to put
1066      * secured <code>"baseUrl"</code> in <code>params</code>.
1067      * Example : <code>
1068      *         params.put( PARAMETER_XSL_BASE_URL, "https://www.my-host.com/my-webapp/ );
1069      * </code>
1070      * @see #PARAMETER_XSL_BASE_URL
1071      * @param formElements the fields and buttons to display
1072      * @param params additional params that might need to be passed to the
1073      *            stylesheet
1074      * @return the html code for the subform fields
1075      */
1076     public String buildHtmlForm( FormElements formElements, Map params )
1077     {
1078         String strXml = "";
1079         Plugin plugin = PluginService.getPlugin( PLUGIN_NAME );
1080 
1081         try
1082         {
1083             // generate the xml code correponding to the form-elements for this subform
1084             StringWriter writer = new StringWriter( );
1085             Marshaller marshaller = getParentForm( ).getFormElementsMarshaller( );
1086             marshaller.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE );
1087 
1088             addNotices( formElements );
1089 
1090             marshaller.marshal( formElements, writer );
1091             strXml = writer.toString( );
1092         }
1093         catch ( JAXBException e )
1094         {
1095             throw new AppException( "Formengine : an error occurred during marshalling - form : "
1096                     + this.getParentForm( ).getName( ) + " - subform " + this.getName( ), e );
1097         }
1098 
1099         Map<String, String> formParams = new HashMap<String, String>( );
1100 
1101         if ( params != null )
1102         {
1103             formParams.putAll( params );
1104         }
1105 
1106         //formParams.put( PARAMETER_XSL_NOTICE_VALUE, strNoticeValue );
1107         formParams.put( PARAMETER_XSL_FORM_NAME, this.getParentForm( ).getName( ) );
1108         formParams.put( PARAMETER_XSL_SUBFORM_NAME, this.getName( ) );
1109 
1110         //Added in v2.0.8 for add the Request url in parameter Post
1111         formParams.put( PARAMETER_XSL_PORTAL_URI, this.getPortalURI( ) );
1112 
1113         // The submit buttons and checkboxes associated with the upload widget
1114         // are given specific names (prefix + upload field name), we provide
1115         // the prefixes to the XSL stylesheet.
1116         formParams.put( PARAMETER_XSL_UPLOAD_SUBMIT_PREFIX, SharedConstants.UPLOAD_SUBMIT_PREFIX );
1117         formParams.put( PARAMETER_XSL_UPLOAD_DELETE_PREFIX, SharedConstants.UPLOAD_DELETE_PREFIX );
1118         formParams.put( PARAMETER_XSL_UPLOAD_CHECKBOX_PREFIX, SharedConstants.UPLOAD_CHECKBOX_PREFIX );
1119 
1120         if ( this.getNextSubForm( ) == null )
1121         {
1122             String strLastUrl = getDefaultUrlToExit( );
1123             formParams.put( PARAMETER_XSL_URL_EXIT, strLastUrl );
1124         }
1125 
1126         String strForm = this.getHtml( strXml, this.getXslFormElementsFileName( ), formParams );
1127 
1128         return strForm;
1129     }
1130 
1131     /**
1132      * Adds notices to the elements
1133      * @param formElements the elements
1134      */
1135     protected void addNotices( FormElements formElements )
1136     {
1137         Plugin plugin = PluginService.getPlugin( PLUGIN_NAME );
1138 
1139         String strFormName = getParentForm( ).getName( ) + "-" + getName( );
1140 
1141         String strIdForm = FormsRegistrationService.getIdForm( strFormName );
1142 
1143         NoticeGroupFilter filter = new NoticeGroupFilter( );
1144         filter.setForm( strIdForm );
1145         filter.setIsEnbled( 1 );
1146 
1147         List<NoticeGroup> listNoticesGroup = NoticeGroupHome.findByFilter( filter, plugin );
1148 
1149         Notices notices = new Notices( );
1150         formElements.setNotices( notices );
1151 
1152         for ( NoticeGroup noticeGroup : listNoticesGroup )
1153         {
1154             // find all notices
1155 
1156             // Notices
1157             List<String> strNoticeValue = new ArrayList<String>( );
1158             NoticeFilter noticeFilter = new NoticeFilter( );
1159             noticeFilter.setIdNoticeGroup( noticeGroup.getId( ) );
1160             noticeFilter.setIsEnabled( 1 );
1161             List<Notice> listNotice = NoticeHome.getNoticeList( noticeFilter, plugin );
1162 
1163             for ( Notice notice : listNotice )
1164             {
1165                 if ( notice.hasValidDate( ) )
1166                 {
1167                     strNoticeValue.add( notice.getMessage( ) );
1168                 }
1169             }
1170 
1171             fr.paris.lutece.plugins.formengine.business.jaxb.formdefinition.NoticeGroup xmlNoticeGroup = new fr.paris.lutece.plugins.formengine.business.jaxb.formdefinition.NoticeGroup( );
1172 
1173             // add notice to group
1174             xmlNoticeGroup.setName( Integer.toString( noticeGroup.getId( ) ) );
1175             xmlNoticeGroup.getNotice( ).addAll( strNoticeValue );
1176 
1177             // add group to XML
1178             notices.getNoticeGroup( ).add( xmlNoticeGroup );
1179         }
1180 
1181     }
1182 
1183     /**
1184      * Defines the url used to exit the form
1185      * @return the url
1186      */
1187     protected String getDefaultUrlToExit( )
1188     {
1189         return AppPropertiesService.getProperty( this.getParentForm( ).getName( ) + PROPERTY_FRAGMENT_URL_FOR_LAST );
1190     }
1191 
1192     /**
1193      * Gets all upload fields for the request
1194      * @param request the request
1195      * @return upload fields list
1196      */
1197     public final List<Field> getUploadFields( HttpServletRequest request )
1198     {
1199         List<Field> listUploadFields = new ArrayList<Field>( );
1200 
1201         for ( Field field : getFormElements( request ).getFields( ).getField( ) )
1202         {
1203             if ( SharedConstants.FIELD_TYPE_UPLOAD.equals( field.getType( ) ) )
1204             {
1205                 listUploadFields.add( field );
1206             }
1207         }
1208 
1209         return listUploadFields;
1210     }
1211 
1212     /**
1213      * Set a session attribute, with a name formatted to avoid conflicts between
1214      * form and subform instances.
1215      * @param request the http request
1216      * @param strAttributeName the name of the variable to put in session
1217      * @param attribute the object to put in session
1218      */
1219     public void setSessionAttribute( HttpServletRequest request, String strAttributeName, Object attribute )
1220     {
1221         HttpSession session = request.getSession( true );
1222         String strPrefix = SharedConstants.SESSION_PREFIX + "_" + this.getParentForm( ).getName( ) + "_"
1223                 + this.getName( ) + "_";
1224         String strSessionAttribute = strPrefix + strAttributeName.trim( );
1225         strSessionAttribute = strSessionAttribute.toUpperCase( );
1226         session.setAttribute( strSessionAttribute, attribute );
1227     }
1228 
1229     /**
1230      * Get an attribute in session. This method is to use to avoid conflicts
1231      * between form and subform instances.
1232      * @param request The Http Request
1233      * @param strAttributeName the name of the variable to retrieve from session
1234      * @return the corresponding object in session
1235      */
1236     public Object getSessionAttribute( HttpServletRequest request, String strAttributeName )
1237     {
1238         HttpSession session = request.getSession( true );
1239         String strPrefix = SharedConstants.SESSION_PREFIX + "_" + this.getParentForm( ).getName( ) + "_"
1240                 + this.getName( ) + "_";
1241         String strSessionAttribute = strPrefix + strAttributeName.trim( );
1242         strSessionAttribute = strSessionAttribute.toUpperCase( );
1243 
1244         return session.getAttribute( strSessionAttribute );
1245     }
1246 
1247     /**
1248      * Remove an attribute in session. This method is to use to avoid conflicts
1249      * between form and subform instances.
1250      * @param request The HttpServlerRequest
1251      * @param strAttributeName the name of the variable to remove from session
1252      */
1253     public void removeSessionAttribute( HttpServletRequest request, String strAttributeName )
1254     {
1255         HttpSession session = request.getSession( true );
1256         String strPrefix = SharedConstants.SESSION_PREFIX + "_" + this.getParentForm( ).getName( ) + "_"
1257                 + this.getName( ) + "_";
1258         String strSessionAttribute = strPrefix + strAttributeName.trim( );
1259         strSessionAttribute = strSessionAttribute.toUpperCase( );
1260         session.removeAttribute( strSessionAttribute );
1261     }
1262 
1263     /**
1264      * Performs an upload action.
1265      * 
1266      * @param request the HTTP request
1267      * @param strUploadAction the name of the upload action
1268      * @return the name of the subform to display after having performed the
1269      *         requested action
1270      */
1271     public String doUploadAction( HttpServletRequest request, String strUploadAction )
1272     {
1273         // The values of the other fields are saved, but no validation is performed
1274         this.fillFields( request );
1275 
1276         // Get the name of the upload field
1277         String strFieldName = ( strUploadAction.startsWith( SharedConstants.UPLOAD_SUBMIT_PREFIX ) ? strUploadAction
1278                 .substring( SharedConstants.UPLOAD_SUBMIT_PREFIX.length( ) ) : strUploadAction
1279                 .substring( SharedConstants.UPLOAD_DELETE_PREFIX.length( ) ) );
1280 
1281         // Find the corresponding Field object
1282         Field field = null;
1283         Iterator iterFields = this.getFormElements( request ).getFields( ).getField( ).iterator( );
1284         boolean bFound = false;
1285 
1286         while ( !bFound && iterFields.hasNext( ) )
1287         {
1288             Field aField = (Field) iterFields.next( );
1289 
1290             if ( strFieldName.equals( aField.getName( ) ) )
1291             {
1292                 bFound = true;
1293                 field = aField;
1294             }
1295         }
1296 
1297         if ( strUploadAction.startsWith( SharedConstants.UPLOAD_SUBMIT_PREFIX ) )
1298         {
1299             // A file was submitted
1300             MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
1301 
1302             FileItem fileItem = multipartRequest.getFile( strFieldName );
1303 
1304             if ( fileItem != null )
1305             {
1306                 // This is the name that will be displayed in the form. We keep
1307                 // the original name, but clean it to make it cross-platform.
1308                 String strFileName = UploadUtil.cleanFileName( FileUploadService.getFileNameOnly( fileItem ) );
1309 
1310                 // Check if this file has not already been uploaded
1311                 List<FileItem> uploadedFiles = this.getFileItems( request, strFieldName );
1312 
1313                 if ( uploadedFiles != null )
1314                 {
1315                     Iterator<FileItem> iterUploadedFiles = uploadedFiles.iterator( );
1316                     boolean bNew = true;
1317 
1318                     while ( bNew && iterUploadedFiles.hasNext( ) )
1319                     {
1320                         FileItem uploadedFile = iterUploadedFiles.next( );
1321                         String strUploadedFileName = UploadUtil.cleanFileName( FileUploadService
1322                                 .getFileNameOnly( uploadedFile ) );
1323                         // If we find a file with the same name and the same
1324                         // length, we consider that the current file has
1325                         // already been uploaded
1326                         bNew = !( strUploadedFileName.equals( strFileName ) && ( uploadedFile.getSize( ) == fileItem
1327                                 .getSize( ) ) );
1328                     }
1329 
1330                     if ( !bNew )
1331                     {
1332                         // Delete the temporary file
1333                         // file.delete(  );
1334 
1335                         // Raise an error
1336                         String[] messageParams = new String[2];
1337                         messageParams[0] = field.getName( );
1338                         messageParams[1] = field.getLabel( );
1339 
1340                         String strError = MessageFormat.format(
1341                                 AppPropertiesService.getProperty( PROPERTY_MESSAGE_EXISTING_FILE ),
1342                                 (Object[]) messageParams );
1343                         this.getParentForm( ).addErrorMessage( request, strError );
1344 
1345                         return this.getName( );
1346                     }
1347                 }
1348 
1349                 addFileItemToUploadedFiles( request, field, fileItem, null );
1350             }
1351         }
1352         else if ( strUploadAction.startsWith( SharedConstants.UPLOAD_DELETE_PREFIX ) )
1353         {
1354             // Some previously uploaded files were deleted
1355             // Build the prefix of the associated checkboxes
1356             String strPrefix = SharedConstants.UPLOAD_CHECKBOX_PREFIX + strFieldName;
1357 
1358             // Look for the checkboxes in the request
1359             Enumeration enumParamNames = request.getParameterNames( );
1360 
1361             while ( enumParamNames.hasMoreElements( ) )
1362             {
1363                 String strParamName = (String) enumParamNames.nextElement( );
1364 
1365                 if ( strParamName.startsWith( strPrefix ) )
1366                 {
1367                     // Get the index from the name of the checkbox
1368                     int iIndex = Integer.parseInt( strParamName.substring( strPrefix.length( ) ) );
1369 
1370                     // Delete the name from the Field object
1371                     field.getFileNames( ).getFileName( ).remove( iIndex );
1372 
1373                     // Remove the file from the subform (this will also delete
1374                     // the file physically)
1375                     this.removeFileItem( request, strFieldName, iIndex );
1376                 }
1377             }
1378         }
1379 
1380         // The result of an upload action is to reload the same subform
1381         return this.getName( );
1382     }
1383 
1384     /**
1385      * Adds file item to uploaded files and field.getFileNames( ).getFileName( )
1386      * @param request the request
1387      * @param field the field
1388      * @param fileItem the file item
1389      * @param formErrorsList the list of error which is filled if not null.
1390      *            Usefull for asynchronous upload. Classic validation should set
1391      *            it
1392      *            to <code>null</code> since the error list is bound to the
1393      *            session.
1394      */
1395     public final void addFileItemToUploadedFiles( HttpServletRequest request, Field field, FileItem fileItem,
1396             FormErrorsList formErrorsList )
1397     {
1398         ObjectFactory objectFactory = new ObjectFactory( );
1399 
1400         if ( field.getFileNames( ) == null )
1401         {
1402             field.setFileNames( objectFactory.createFileNames( ) );
1403         }
1404 
1405         FileName fileName = objectFactory.createFileName( );
1406         String strFileName = UploadUtil.cleanFileName( FileUploadService.getFileNameOnly( fileItem ) );
1407         fileName.setValue( strFileName );
1408         fileName.setSize( Long.toString( fileItem.getSize( ) ) );
1409         field.getFileNames( ).getFileName( ).add( fileName );
1410 
1411         // Run any validator associated to the upload field
1412         boolean bValid = validateUploadField( request, field, formErrorsList );
1413 
1414         if ( bValid )
1415         {
1416             // Associate the file with the subform
1417             this.addFileItem( request, field.getName( ), fileItem );
1418         }
1419         else
1420         {
1421             // An error occurred, so the file won't be associated with
1422             // the subform. Delete the temporary file
1423             // file.delete(  );
1424 
1425             // The name of the file is still in the Field, remove it
1426             List fileNames = field.getFileNames( ).getFileName( );
1427             fileNames.remove( fileNames.size( ) - 1 );
1428         }
1429     }
1430 
1431     /**
1432      * Associates a newly uploaded file to the subform. This method is used by
1433      * FormEngineApp and need not be overriden nor called from child classes.
1434      * 
1435      * @param request the HTTP request
1436      * @param strFieldName the name of the upload field
1437      * @param fileItem the File object
1438      */
1439     public final void addFileItem( HttpServletRequest request, String strFieldName, FileItem fileItem )
1440     {
1441         // Build the attribute name and instantiate the object if none exists yet
1442         String strAttributeName = SharedConstants.FILELIST_PREFIX + strFieldName;
1443 
1444         if ( this.getSessionAttribute( request, strAttributeName ) == null )
1445         {
1446             this.setSessionAttribute( request, strAttributeName, new ArrayList<FileItem>( ) );
1447         }
1448 
1449         // Get the HashMap from the session and put the File in it
1450         List<FileItem> listFiles = (List) this.getSessionAttribute( request, strAttributeName );
1451         listFiles.add( fileItem );
1452     }
1453 
1454     /**
1455      * For asynchronous usage
1456      * @param strIdSession session id
1457      * @param strFieldName field name
1458      * @param fileItem fileitem
1459      */
1460     public final void addFileItem( String strIdSession, String strFieldName, FileItem fileItem )
1461     {
1462         getParentForm( ).addFileItem( this.getName( ), strIdSession, strFieldName, fileItem );
1463     }
1464 
1465     /**
1466      * For asynchronous usage. Does not provide all uploaded file.
1467      * @param strIdSession session id
1468      * @param strFieldName field name
1469      * @return the asynchronous uploaded files
1470      */
1471     public final List<FileItem> getFileItems( String strIdSession, String strFieldName )
1472     {
1473         return getParentForm( ).getAsynchronousUploadedFiles( this.getName( ), strIdSession, strFieldName );
1474     }
1475 
1476     /**
1477      * Removes an uploaded fileitem using its index.
1478      * @param request the request
1479      * @param strFieldName the field name
1480      * @param nIndex the index
1481      * @return <code>true</code> if file is removed, <code>false</code>
1482      *         otherwise.
1483      */
1484     public final synchronized boolean removeUploadedFileItem( HttpServletRequest request, String strFieldName,
1485             int nIndex )
1486     {
1487         boolean bRemoved = false;
1488 
1489         List<FileItem> listFiles = getFileItems( request, strFieldName );
1490 
1491         if ( ( listFiles != null ) && ( listFiles.size( ) > nIndex ) )
1492         {
1493             FileItem fileItem = listFiles.remove( nIndex );
1494 
1495             if ( fileItem != null )
1496             {
1497                 fileItem.delete( );
1498                 bRemoved = true;
1499             }
1500             else
1501             {
1502                 bRemoved = false;
1503             }
1504 
1505             getFieldFromName( request, strFieldName ).getFileNames( ).getFileName( ).remove( nIndex );
1506         }
1507 
1508         return bRemoved;
1509     }
1510 
1511     /**
1512      * Deletes a file previously associated with the subform. This method is
1513      * used by FormEngineApp and need not be overriden nor called from child
1514      * classes.
1515      * 
1516      * @param request the HTTP request
1517      * @param strFieldName the name of the upload field
1518      * @param iIndex the index of the file to remove
1519      */
1520     public final void removeFileItem( HttpServletRequest request, String strFieldName, int iIndex )
1521     {
1522         // Build the attribute name and get the HashMap from the session
1523         String strAttributeName = SharedConstants.FILELIST_PREFIX + strFieldName;
1524         List<FileItem> listFiles = (List) this.getSessionAttribute( request, strAttributeName );
1525 
1526         if ( ( listFiles != null ) && ( listFiles.size( ) > iIndex ) )
1527         {
1528             // Remove the object from the Hashmap
1529             FileItem fileItem = listFiles.remove( iIndex );
1530             fileItem.delete( );
1531         }
1532     }
1533 
1534     /**
1535      * Returns the files associated with an upload field of the subform. This
1536      * method can be used by output processors to access the uploaded files'
1537      * data.
1538      * 
1539      * @param request the HTTP request
1540      * @param strFieldName the name of the upload field
1541      * @return a java.util.List containing UploadedFiles objects. Returns null
1542      *         if no file was uploaded for this field.
1543      * 
1544      */
1545     public final List<FileItem> getFileItems( HttpServletRequest request, String strFieldName )
1546     {
1547         // Build the attribute name and get the HashMap from the session
1548         String strAttributeName = SharedConstants.FILELIST_PREFIX + strFieldName;
1549 
1550         return (List<FileItem>) this.getSessionAttribute( request, strAttributeName );
1551     }
1552 
1553     /**
1554      * Sets the file items url (stored in blobstore) to the list (which is
1555      * cleared). If the file items are already BlobStored,
1556      * they are not reuploaded to keep file from the draft (if supported).
1557      * @param request the request
1558      * @param strFieldName the field name
1559      * @param listFilesUrl the list of url
1560      * @param IBlobStoreService the blobstore service where to store/restore
1561      *            files
1562      */
1563     protected void setBlobStoreFileItemsURL( HttpServletRequest request, String strFieldName,
1564             List<String> listFilesUrl, IBlobStoreService IBlobStoreService )
1565     {
1566         List<FileItem> listUploadedFileItems = getFileItems( request, strFieldName );
1567 
1568         if ( listUploadedFileItems != null )
1569         {
1570             listFilesUrl.clear( );
1571 
1572             List<BlobStoreFileItem> listBlobStoreFileItems = buildListBlobStoreFileItem( listUploadedFileItems,
1573                     IBlobStoreService );
1574 
1575             for ( BlobStoreFileItem fileItem : listBlobStoreFileItems )
1576             {
1577                 listFilesUrl.add( IBlobStoreService.getFileUrl( fileItem.getBlobId( ) ) );
1578             }
1579 
1580             // replace old file items with new ones
1581             listUploadedFileItems.clear( );
1582             listUploadedFileItems.addAll( listBlobStoreFileItems );
1583         }
1584     }
1585 
1586     /**
1587      * Builds blobstore items of the file items
1588      * @param fileItems the file items
1589      * @param IBlobStoreService the blobstore service
1590      * @return the list
1591      */
1592     private List<BlobStoreFileItem> buildListBlobStoreFileItem( List<FileItem> fileItems,
1593             IBlobStoreService IBlobStoreService )
1594     {
1595         List<BlobStoreFileItem> listBlobStoreFileItem = new ArrayList<BlobStoreFileItem>( );
1596 
1597         for ( FileItem fileItem : fileItems )
1598         {
1599             // we have to upload file to the blobstore
1600             BlobStoreFileItem blobFileItem;
1601 
1602             if ( !( fileItem instanceof BlobStoreFileItem ) )
1603             {
1604                 if ( AppLogService.isDebugEnabled( ) )
1605                 {
1606                     AppLogService.debug( "Forcing upload of " + fileItem.getName( ) + " to blobstore" );
1607                 }
1608 
1609                 blobFileItem = uploadFileToBlobStore( fileItem, IBlobStoreService );
1610             }
1611             else
1612             {
1613                 blobFileItem = (BlobStoreFileItem) fileItem;
1614             }
1615 
1616             listBlobStoreFileItem.add( blobFileItem );
1617         }
1618 
1619         return listBlobStoreFileItem;
1620     }
1621 
1622     /**
1623      * Uploads files to blobstore
1624      * @param fileItem fileItem
1625      * @param service the service
1626      * @return the file Item
1627      */
1628     private BlobStoreFileItem uploadFileToBlobStore( FileItem fileItem, IBlobStoreService service )
1629     {
1630         String strFileBlobId;
1631 
1632         try
1633         {
1634             strFileBlobId = service.storeInputStream( fileItem.getInputStream( ) );
1635         }
1636         catch ( IOException e )
1637         {
1638             AppLogService.error( e.getMessage( ), e );
1639 
1640             return null;
1641         }
1642 
1643         String strJSONMetada = BlobStoreFileItem.buildFileMetadata(
1644                 UploadUtil.cleanFileName( FileUploadService.getFileNameOnly( fileItem ) ), fileItem.getSize( ),
1645                 strFileBlobId, fileItem.getContentType( ) );
1646         String strKeyMetadata = service.store( strJSONMetada.getBytes( ) );
1647 
1648         try
1649         {
1650             return new BlobStoreFileItem( strKeyMetadata, service );
1651         }
1652         catch ( NoSuchBlobException e )
1653         {
1654             AppLogService.error( e.getMessage( ), e );
1655 
1656             return null;
1657         }
1658     }
1659 
1660     /**
1661      * Gets all XSL used by this subform. <br>
1662      * This method actually return an array with one value :
1663      * {@link #getXslFormElementsFileName}.
1664      * It should be overriden if all XSL is managed by this subform.
1665      * @return XSL infos
1666      */
1667     public String[] getXslFilesNames( )
1668     {
1669         return new String[] { getXslFormElementsFileName( ), };
1670     }
1671 }