View Javadoc
1   /*
2    * Copyright (c) 2002-2023, City of Paris
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met:
8    *
9    *  1. Redistributions of source code must retain the above copyright notice
10   *     and the following disclaimer.
11   *
12   *  2. Redistributions in binary form must reproduce the above copyright notice
13   *     and the following disclaimer in the documentation and/or other materials
14   *     provided with the distribution.
15   *
16   *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
17   *     contributors may be used to endorse or promote products derived from
18   *     this software without specific prior written permission.
19   *
20   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
24   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30   * POSSIBILITY OF SUCH DAMAGE.
31   *
32   * License 1.0
33   */
34  package fr.paris.lutece.plugins.document.service;
35  
36  import fr.paris.lutece.plugins.document.business.Document;
37  import fr.paris.lutece.plugins.document.business.DocumentHome;
38  import fr.paris.lutece.plugins.document.business.DocumentType;
39  import fr.paris.lutece.plugins.document.business.DocumentTypeHome;
40  import fr.paris.lutece.plugins.document.business.attributes.AttributeTypeParameter;
41  import fr.paris.lutece.plugins.document.business.attributes.DocumentAttribute;
42  import fr.paris.lutece.plugins.document.business.attributes.MapProviderManager;
43  import fr.paris.lutece.plugins.document.business.category.Category;
44  import fr.paris.lutece.plugins.document.business.category.CategoryHome;
45  import fr.paris.lutece.plugins.document.business.portlet.DocumentPortletHome;
46  import fr.paris.lutece.plugins.document.business.spaces.DocumentSpace;
47  import fr.paris.lutece.plugins.document.business.spaces.DocumentSpaceHome;
48  import fr.paris.lutece.plugins.document.business.workflow.DocumentAction;
49  import fr.paris.lutece.plugins.document.business.workflow.DocumentActionHome;
50  import fr.paris.lutece.plugins.document.service.metadata.MetadataHandler;
51  import fr.paris.lutece.plugins.document.service.publishing.PublishingService;
52  import fr.paris.lutece.plugins.document.service.spaces.DocumentSpacesService;
53  import fr.paris.lutece.plugins.document.utils.ImageUtils;
54  import fr.paris.lutece.plugins.document.utils.IntegerUtils;
55  import fr.paris.lutece.plugins.document.web.DocumentResourceServlet;
56  import fr.paris.lutece.portal.business.portlet.Portlet;
57  import fr.paris.lutece.portal.business.user.AdminUser;
58  import fr.paris.lutece.portal.service.cache.CacheService;
59  import fr.paris.lutece.portal.service.cache.CacheableService;
60  import fr.paris.lutece.portal.service.i18n.I18nService;
61  import fr.paris.lutece.portal.service.message.AdminMessage;
62  import fr.paris.lutece.portal.service.message.AdminMessageService;
63  import fr.paris.lutece.portal.service.rbac.RBACResource;
64  import fr.paris.lutece.portal.service.rbac.RBACService;
65  import fr.paris.lutece.portal.service.spring.SpringContextService;
66  import fr.paris.lutece.portal.web.constants.Messages;
67  import fr.paris.lutece.portal.web.upload.MultipartHttpServletRequest;
68  import fr.paris.lutece.util.date.DateUtil;
69  import fr.paris.lutece.util.string.StringUtil;
70  import fr.paris.lutece.util.xml.XmlUtil;
71  
72  import org.apache.commons.collections.CollectionUtils;
73  import org.apache.commons.fileupload.FileItem;
74  import org.apache.commons.io.FilenameUtils;
75  import org.apache.commons.lang3.ArrayUtils;
76  import org.apache.commons.lang3.BooleanUtils;
77  import org.apache.commons.lang3.StringUtils;
78  
79  import java.io.IOException;
80  
81  import java.sql.Timestamp;
82  
83  import java.util.ArrayList;
84  import java.util.Collection;
85  import java.util.Date;
86  import java.util.List;
87  import java.util.Locale;
88  
89  
90  /**
91   * This Service manages document actions (create, move, delete, validate ...)
92   * and notify listeners.
93   */
94  public class DocumentService
95  {
96      //PARAMETERS
97      private static final String PARAMETER_DOCUMENT_TITLE = "document_title";
98      private static final String PARAMETER_DOCUMENT_SUMMARY = "document_summary";
99      private static final String PARAMETER_DOCUMENT_COMMENT = "document_comment";
100     private static final String PARAMETER_VALIDITY_BEGIN = "document_validity_begin";
101     private static final String PARAMETER_VALIDITY_END = "document_validity_end";
102     private static final String PARAMETER_MAILING_LIST = "mailinglists";
103     private static final String PARAMETER_PAGE_TEMPLATE_DOCUMENT_ID = "page_template_id";
104     private static final String PARAMETER_SKIP_PORTLET = "document_skip_portlet";
105     private static final String PARAMETER_SKIP_CATEGORIES = "document_skip_categories";
106     private static final String PARAMETER_CATEGORY = "category_id";
107     private static final String PARAMETER_ATTRIBUTE_UPDATE = "update_";
108     private static final String PARAMETER_CROPPABLE = "_croppable";
109     private static final String PARAMETER_WIDTH = "_width";
110 
111     //MESSAGES
112     private static final String MESSAGE_ERROR_DATEEND_BEFORE_DATEBEGIN = "document.message.dateEndBeforeDateBegin";
113     private static final String MESSAGE_INVALID_DATEEND = "document.message.invalidDateEnd";
114     private static final String MESSAGE_INVALID_DATEBEGIN = "document.message.invalidDateBegin";
115     private static final String MESSAGE_INVALID_DATE_BEFORE_70 = "document.message.invalidDate.before1970";
116     private static final String MESSAGE_ATTRIBUTE_VALIDATION_ERROR = "document.message.attributeValidationError";
117     private static final String MESSAGE_ATTRIBUTE_WIDTH_ERROR = "document.message.widthError";
118     private static final String MESSAGE_ATTRIBUTE_RESIZE_ERROR = "document.message.resizeError";
119     private static final String MESSAGE_EXTENSION_ERROR = "document.message.extensionError";
120     private static DocumentServicervice/DocumentService.html#DocumentService">DocumentService _singleton = new DocumentService(  );
121     private static final String TAG_DOCUMENT_ID = "document-id";
122     private static final String TAG_DOCUMENT_TITLE = "document-title";
123     private static final String TAG_DOCUMENT_SUMMARY = "document-summary";
124     private static final String TAG_DOCUMENT_DATE_BEGIN = "document-date-begin";
125     private static final String TAG_DOCUMENT_DATE_END = "document-date-end";
126     private static final String TAG_DOCUMENT_SKIP_PORTLET = "document-skip-portlet";
127     private static final String TAG_DOCUMENT_SKIP_CATEGORIES = "document-skip-categories";
128     private static final String TAG_DOCUMENT_CATEGORIES = "document-categories";
129     private static final String TAG_DOCUMENT_CATEGORY = "category";
130     private static final String TAG_CDATA_BEGIN = "<![CDATA[";
131     private static final String TAG_CDATA_END = "]]>";
132     private static DocumentEventListernersManager _managerEventListeners;
133 
134     /** Creates a new instance of DocumentService */
135     private DocumentService(  )
136     {
137         _managerEventListeners = SpringContextService.getBean( "document.documentEventListernersManager" );
138     }
139 
140     /**
141      * Get the unique instance of the service
142      *
143      * @return The unique instance
144      */
145     public static DocumentService getInstance(  )
146     {
147         return _singleton;
148     }
149 
150     /**
151      * Build an XML document that contains document's data.
152      *
153      * @param document The document
154      * @return An XML fragment containing document's data
155      */
156     String buildXmlContent( Document document )
157     {
158         StringBuffer sbXml = new StringBuffer(  );
159         XmlUtil.beginElement( sbXml, document.getCodeDocumentType(  ) );
160 
161         XmlUtil.addElement( sbXml, TAG_DOCUMENT_ID, document.getId(  ) );
162         XmlUtil.addElement( sbXml, TAG_DOCUMENT_TITLE, TAG_CDATA_BEGIN + document.getTitle(  ) + TAG_CDATA_END );
163         XmlUtil.addElement( sbXml, TAG_DOCUMENT_SUMMARY, TAG_CDATA_BEGIN + document.getSummary(  ) + TAG_CDATA_END );
164         XmlUtil.addElement( sbXml, TAG_DOCUMENT_DATE_BEGIN,
165             DateUtil.getDateString( document.getDateValidityBegin(  ), I18nService.getDefaultLocale(  ) ) );
166         XmlUtil.addElement( sbXml, TAG_DOCUMENT_DATE_END,
167             DateUtil.getDateString( document.getDateValidityEnd(  ), I18nService.getDefaultLocale(  ) ) );
168         XmlUtil.addElement( sbXml, TAG_DOCUMENT_SKIP_PORTLET, BooleanUtils.toStringTrueFalse( document.isSkipPortlet( ) ) );
169         XmlUtil.addElement( sbXml, TAG_DOCUMENT_SKIP_CATEGORIES, BooleanUtils.toStringTrueFalse( document.isSkipCategories( ) ) );
170         XmlUtil.addElement( sbXml, TAG_DOCUMENT_CATEGORIES, buildXmlCategories( document ) );
171 
172         DocumentType documentType = DocumentTypeHome.findByPrimaryKey( document.getCodeDocumentType(  ) );
173 
174         for ( DocumentAttribute attribute : documentType.getAttributes(  ) )
175         {
176             DocumentAttribute attributeDocument = getDocumentAttribute( document, attribute.getId(  ) );
177 
178             if ( attributeDocument != null )
179             {
180                 AttributeManager manager = AttributeService.getInstance(  )
181                                                            .getManager( attributeDocument.getCodeAttributeType(  ) );
182                 XmlUtil.addElement( sbXml, document.getCodeDocumentType(  ) + "-" + attribute.getCode(  ),
183                     manager.getAttributeXmlValue( document, attributeDocument ) );
184             }
185         }
186 
187         XmlUtil.endElement( sbXml, document.getCodeDocumentType(  ) );
188 
189         return sbXml.toString(  );
190     }
191 
192     /**
193      * Build XML content for the categories
194      * @param document The document
195      * @return An XML fragment containing document's categories
196      */
197     private String buildXmlCategories( Document document )
198     {
199         StringBuffer strListCategories = new StringBuffer(  );
200 
201         if ( ( document != null ) && ( document.getCategories(  ) != null ) && !document.getCategories(  ).isEmpty(  ) )
202         {
203             for ( Category category : document.getCategories(  ) )
204             {
205                 XmlUtil.addElement( strListCategories, TAG_DOCUMENT_CATEGORY,
206                     TAG_CDATA_BEGIN + category.getName(  ) + TAG_CDATA_END );
207             }
208         }
209 
210         return strListCategories.toString(  );
211     }
212 
213     /**
214      * Change the state of the document
215      *
216      * @param document The document
217      * @param user The user doing the action
218      * @param nStateId The new state Id
219      * @throws DocumentException raise when error occurs in event or rule
220      */
221     public void changeDocumentState( Document document, AdminUser user, int nStateId )
222         throws DocumentException
223     {
224         document.setStateId( nStateId );
225         DocumentHome.update( document, false );
226 
227         notify( document.getId(  ), user, DocumentEvent.DOCUMENT_STATE_CHANGED );
228     }
229 
230     /**
231      * Create a new document
232      *
233      * @param document The document
234      * @param user The user doing the action
235      * @throws DocumentException raise when error occurs in event or rule
236      */
237     public void createDocument( Document document, AdminUser user )
238         throws DocumentException
239     {
240         document.setId( DocumentHome.newPrimaryKey(  ) );
241         document.setDateCreation( new Timestamp( new java.util.Date(  ).getTime(  ) ) );
242         document.setDateModification( new Timestamp( new java.util.Date(  ).getTime(  ) ) );
243         document.setXmlWorkingContent( buildXmlContent( document ) );
244 
245         DocumentHome.create( document );
246 
247         notify( document.getId(  ), user, DocumentEvent.DOCUMENT_CREATED );
248     }
249 
250     /**
251      * Modify a the content of a document
252      *
253      * @param document The document
254      * @param user The user doing the action
255      * @throws DocumentException raise when error occurs in event or rule
256      */
257     public void modifyDocument( Document document, AdminUser user )
258         throws DocumentException
259     {
260         document.setDateModification( new Timestamp( new java.util.Date(  ).getTime(  ) ) );
261         document.setXmlWorkingContent( buildXmlContent( document ) );
262         DocumentHome.update( document, true );
263 
264         notify( document.getId(  ), user, DocumentEvent.DOCUMENT_CONTENT_MODIFIED );
265     }
266 
267     /**
268      * Validate a document
269      * @param nStateId The new state id for a validated document
270      * @param document The document
271      * @param user The user doing the action
272      * @throws DocumentException raise when error occurs in event or rule
273      */
274     public void validateDocument( Document document, AdminUser user, int nStateId )
275         throws DocumentException
276     {
277         document.setStateId( nStateId );
278         // Copy the working content into the validated content
279         document.setXmlValidatedContent( document.getXmlWorkingContent(  ) );
280         DocumentHome.update( document, false );
281         DocumentHome.validateAttributes( document.getId(  ) );
282 
283         List<DocumentAttribute> listAttributes = document.getAttributes(  );
284 
285         for ( DocumentAttribute attribute : listAttributes )
286         {
287             DocumentResourceServlet.putInCache( document.getId(  ), attribute.getId(  ) );
288         }
289 
290         for ( CacheableService cs : CacheService.getCacheableServicesList(  ) )
291         {
292             if ( cs.getClass(  ).equals( DocumentContentService.class ) )
293             {
294                 for ( int nIdPortlet : DocumentPortletHome.findPortletForDocument( document.getId(  ) ) )
295                 {
296                     ( (DocumentContentService) cs ).removeFromCache( Integer.toString( document.getId(  ) ),
297                         Integer.toString( nIdPortlet ) );
298                 }
299             }
300         }
301 
302         notify( document.getId(  ), user, DocumentEvent.DOCUMENT_STATE_CHANGED );
303     }
304 
305     /**
306      * Archive a document
307      * @param nStateId The new state id for a validated document
308      * @param document The document
309      * @param user The user doing the action
310      * @throws DocumentException raise when error occurs in event or rule
311      */
312     public void archiveDocument( Document document, AdminUser user, int nStateId )
313         throws DocumentException
314     {
315         // Get the list of portlets linked with document 
316         Collection<Portlet> listPortlet = PublishingService.getInstance(  )
317                                                            .getPortletsByDocumentId( Integer.toString( 
318                     document.getId(  ) ) );
319 
320         for ( Portlet portlet : listPortlet )
321         {
322             //If document is published, unpublish it
323             if ( PublishingService.getInstance(  ).isPublished( document.getId(  ), portlet.getId(  ) ) )
324             {
325                 PublishingService.getInstance(  ).unPublish( document.getId(  ), portlet.getId(  ) );
326             }
327 
328             //Unassign Document to portlet
329             PublishingService.getInstance(  ).unAssign( document.getId(  ), portlet.getId(  ) );
330         }
331 
332         document.setStateId( nStateId );
333         DocumentHome.update( document, false );
334 
335         notify( document.getId(  ), user, DocumentEvent.DOCUMENT_STATE_CHANGED );
336     }
337 
338     /**
339      * Move a document from a space to another
340      *
341      * @param document The document
342      * @param user The user doing the action
343      * @param nNewSpace The Id of the destination space
344      * @throws DocumentException raise when error occurs in event or rule
345      */
346     public void moveDocument( Document document, AdminUser user, int nNewSpace )
347         throws DocumentException
348     {
349         document.setSpaceId( nNewSpace );
350         DocumentHome.update( document, false );
351 
352         notify( document.getId(  ), user, DocumentEvent.DOCUMENT_MOVED );
353     }
354 
355     /**
356      * Notify an event to all listeners
357      *
358      * @param nDocumentId The document Id
359      * @param user The user doing the action
360      * @param nEventType The type of event
361      * @throws DocumentException raise when error occurs in event or rule
362      */
363     private void notify( int nDocumentId, AdminUser user, int nEventType )
364         throws DocumentException
365     {
366         // Reload document to have all data (ie : state_key !)
367         Document document = DocumentHome.findByPrimaryKeyWithoutBinaries( nDocumentId );
368         DocumentEventment/service/DocumentEvent.html#DocumentEvent">DocumentEvent event = new DocumentEvent( document, user, nEventType );
369 
370         _managerEventListeners.notifyListeners( event );
371     }
372 
373     /**
374      * Add to the document all permitted actions according to the current user
375      * and
376      * using the current locale
377      * @param user The current user
378      * @param document The document
379      * @param locale The Locale
380      */
381     public void getActions( Document document, Locale locale, AdminUser user )
382     {
383         List<DocumentAction> listActions = DocumentActionHome.getActionsList( document, locale );
384         RBACResource documentType = DocumentTypeHome.findByPrimaryKey( document.getCodeDocumentType(  ) );
385         listActions = (List<DocumentAction>) RBACService.getAuthorizedActionsCollection( listActions, documentType, user );
386 
387         // Get all beans from the global ApplicationContext
388         List<IDocumentActionsService> listActionServices = SpringContextService.getBeansOfType( IDocumentActionsService.class );
389 
390         // Process all services
391         for ( IDocumentActionsService actionService : listActionServices )
392         {
393             listActions.addAll( actionService.getActions( document, locale, user ) );
394         }
395 
396         document.setActions( listActions );
397     }
398 
399     /**
400      * Get the published status of a document
401      * @param document The document to get the published status of
402      */
403     public void getPublishedStatus( Document document )
404     {
405         boolean isPublished = PublishingService.getInstance(  ).isPublished( document.getId(  ) );
406         int nPublishedStatus = 0;
407 
408         if ( !isPublished )
409         {
410             nPublishedStatus = 1;
411         }
412 
413         document.setPublishedStatus( nPublishedStatus );
414     }
415 
416     /**
417      * Build an HTML form for the document creation for a given document type
418      *
419      * @param strDocumentTypeCode The Document type code
420      * @param locale The Locale
421      * @param strBaseUrl The base Url
422      *
423      * @return The HTML form
424      */
425     public String getCreateForm( String strDocumentTypeCode, Locale locale, String strBaseUrl )
426     {
427         StringBuilder sbForm = new StringBuilder(  );
428         DocumentType documentType = DocumentTypeHome.findByPrimaryKey( strDocumentTypeCode );
429 
430         for ( DocumentAttribute attribute : documentType.getAttributes(  ) )
431         {
432             AttributeManager manager = AttributeService.getInstance(  ).getManager( attribute.getCodeAttributeType(  ) );
433             sbForm.append( manager.getCreateFormHtml( attribute, locale, strBaseUrl ) );
434         }
435 
436         return sbForm.toString(  );
437     }
438 
439     /**
440      * Build an HTML form for the document modification for a given document ID.
441      * <b>Warning</b> : This method loads the binaries of the document.
442      *
443      * @param strDocumentId The Id of the document to modify
444      * @param locale The Locale
445      * @param strBaseUrl The base url
446      *
447      * @return The HTML form
448      */
449     public String getModifyForm( String strDocumentId, Locale locale, String strBaseUrl )
450     {
451         int nDocumentId = IntegerUtils.convert( strDocumentId );
452         Document document = DocumentHome.findByPrimaryKey( nDocumentId );
453 
454         if ( document != null )
455         {
456             return getModifyForm( document, locale, strBaseUrl );
457         }
458 
459         return StringUtils.EMPTY;
460     }
461 
462     /**
463      * Build an HTML form for the document modification for a given document
464      *
465      * @param document The document
466      * @param locale The Locale
467      * @param strBaseUrl The base url
468      * @return The HTML form
469      */
470     public String getModifyForm( Document document, Locale locale, String strBaseUrl )
471     {
472         StringBuilder sbForm = new StringBuilder(  );
473         DocumentType documentType = DocumentTypeHome.findByPrimaryKey( document.getCodeDocumentType(  ) );
474 
475         for ( DocumentAttribute attribute : documentType.getAttributes(  ) )
476         {
477             DocumentAttribute attributeDocument = getDocumentAttribute( document, attribute.getId(  ) );
478 
479             if ( attributeDocument != null )
480             {
481                 AttributeManager manager = AttributeService.getInstance(  )
482                                                            .getManager( attributeDocument.getCodeAttributeType(  ) );
483                 sbForm.append( manager.getModifyFormHtml( attributeDocument, document, locale, strBaseUrl ) );
484             }
485             else
486             {
487                 AttributeManager manager = AttributeService.getInstance(  )
488                                                            .getManager( attribute.getCodeAttributeType(  ) );
489                 sbForm.append( manager.getCreateFormHtml( attribute, locale, strBaseUrl ) );
490             }
491         }
492 
493         return sbForm.toString(  );
494     }
495 
496     /**
497      * Retrieve an attribute of document from its ID
498      * @param document The document
499      * @param nAttributeId The attribute ID
500      * @return The attribute if found, otherwise null
501      */
502     private DocumentAttribute getDocumentAttribute( Document document, int nAttributeId )
503     {
504         for ( DocumentAttribute attribute : document.getAttributes(  ) )
505         {
506             if ( attribute.getId(  ) == nAttributeId )
507             {
508                 return attribute;
509             }
510         }
511 
512         return null;
513     }
514 
515     /**
516      * Check that a given user is allowed to access a document type for a given
517      * permission in a document space specified in parameter
518      * If permission is document creation, check if document creation is allowed
519      * for the specified space.
520      * @param nIdSpace the id of the document space
521      * @param strDocumentTypeId the id of the type document being considered
522      * @param strPermission the permission needed
523      * @param user the user trying to access the ressource
524      * @return true if the user can access the given resource with the given
525      *         permission, false otherwise
526      */
527     public boolean isAuthorizedAdminDocument( int nIdSpace, String strDocumentTypeId, String strPermission,
528         AdminUser user )
529     {
530         DocumentSpace documentSpace = DocumentSpaceHome.findByPrimaryKey( nIdSpace );
531         boolean bPermissionCreate = strPermission.equals( DocumentTypeResourceIdService.PERMISSION_CREATE )
532             ? documentSpace.isDocumentCreationAllowed(  ) : true;
533 
534         return DocumentSpacesService.getInstance(  ).isAuthorizedViewByWorkgroup( documentSpace.getId(  ), user ) &&
535         DocumentSpacesService.getInstance(  ).isAuthorizedViewByRole( documentSpace.getId(  ), user ) &&
536         RBACService.isAuthorized( DocumentType.RESOURCE_TYPE, strDocumentTypeId, strPermission, user ) &&
537         bPermissionCreate;
538     }
539 
540     /**
541      * Return the data of a document object
542      * @param mRequest The MultipartHttpServletRequest
543      * @param document The document object
544      * @param locale The locale
545      * @return data of document object
546      */
547     public String getDocumentData( MultipartHttpServletRequest mRequest, Document document, Locale locale )
548     {
549         String strDocumentTitle = mRequest.getParameter( PARAMETER_DOCUMENT_TITLE );
550         String strDocumentSummary = mRequest.getParameter( PARAMETER_DOCUMENT_SUMMARY );
551         String strDocumentComment = mRequest.getParameter( PARAMETER_DOCUMENT_COMMENT );
552         String strDateValidityEnd = mRequest.getParameter( PARAMETER_VALIDITY_END );
553         String strDateValidityBegin = mRequest.getParameter( PARAMETER_VALIDITY_BEGIN );
554         String strMailingListId = mRequest.getParameter( PARAMETER_MAILING_LIST );
555         int nMailingListId = IntegerUtils.convert( strMailingListId, 0 );
556         String strPageTemplateDocumentId = mRequest.getParameter( PARAMETER_PAGE_TEMPLATE_DOCUMENT_ID );
557         int nPageTemplateDocumentId = IntegerUtils.convert( strPageTemplateDocumentId, 0 );
558         String strSkipPortlet = mRequest.getParameter( PARAMETER_SKIP_PORTLET );
559         boolean bSkipPortlet = ( ( strSkipPortlet == null ) || "".equals( strSkipPortlet ) ) ? false : true;
560         String strSkipCategories = mRequest.getParameter( PARAMETER_SKIP_CATEGORIES );
561         boolean bSkipCategories = ( ( strSkipCategories == null ) || "".equals( strSkipCategories ) ) ? false : true;
562         String[] arrayCategory = mRequest.getParameterValues( PARAMETER_CATEGORY );
563 
564         // Check for mandatory value
565         if ( StringUtils.isBlank( strDocumentTitle ) || StringUtils.isBlank( strDocumentSummary ) )
566         {
567             return AdminMessageService.getMessageUrl( mRequest, Messages.MANDATORY_FIELDS, AdminMessage.TYPE_STOP );
568         }
569 
570         // Check for illegal character character
571         if ( StringUtil.containsHtmlSpecialCharacters( strDocumentTitle ) ||
572                 StringUtil.containsHtmlSpecialCharacters( strDocumentSummary ) )
573         {
574             return AdminMessageService.getMessageUrl( mRequest, Messages.MESSAGE_ILLEGAL_CHARACTER,
575                 AdminMessage.TYPE_STOP );
576         }
577 
578         DocumentType documentType = DocumentTypeHome.findByPrimaryKey( document.getCodeDocumentType(  ) );
579         List<DocumentAttribute> listAttributes = documentType.getAttributes(  );
580 
581         for ( DocumentAttribute attribute : listAttributes )
582         {
583             String strAdminMessage = setAttribute( attribute, document, mRequest, locale );
584 
585             if ( strAdminMessage != null )
586             {
587                 return strAdminMessage;
588             }
589         }
590         
591         Timestamp dateValidityBegin = null;
592         Timestamp dateValidityEnd = null;
593 
594         if ( ( strDateValidityBegin != null ) && !strDateValidityBegin.equals( "" ) )
595         {
596             Date dateBegin = DateUtil.parseIsoDate( strDateValidityBegin );
597 
598             if ( ( dateBegin == null ) )
599             {
600                 return AdminMessageService.getMessageUrl( mRequest, MESSAGE_INVALID_DATEBEGIN, AdminMessage.TYPE_STOP );
601             }
602 
603             dateValidityBegin = new Timestamp( dateBegin.getTime(  ) );
604 
605             if ( dateValidityBegin.before( new Timestamp( 0 ) ) )
606             {
607                 return AdminMessageService.getMessageUrl( mRequest, MESSAGE_INVALID_DATE_BEFORE_70,
608                     AdminMessage.TYPE_STOP );
609             }
610         }
611 
612         if ( ( strDateValidityEnd != null ) && !strDateValidityEnd.equals( "" ) )
613         {
614             Date dateEnd = DateUtil.parseIsoDate( strDateValidityEnd );
615 
616             if ( ( dateEnd == null ) )
617             {
618                 return AdminMessageService.getMessageUrl( mRequest, MESSAGE_INVALID_DATEEND, AdminMessage.TYPE_STOP );
619             }
620 
621             dateValidityEnd = new Timestamp( dateEnd.getTime(  ) );
622 
623             if ( dateValidityEnd.before( new Timestamp( 0 ) ) )
624             {
625                 return AdminMessageService.getMessageUrl( mRequest, MESSAGE_INVALID_DATE_BEFORE_70,
626                     AdminMessage.TYPE_STOP );
627             }
628         }
629 
630         //validate period (dateEnd > dateBegin )
631         if ( ( dateValidityBegin != null ) && ( dateValidityEnd != null ) )
632         {
633             if ( dateValidityEnd.before( dateValidityBegin ) )
634             {
635                 return AdminMessageService.getMessageUrl( mRequest, MESSAGE_ERROR_DATEEND_BEFORE_DATEBEGIN,
636                     AdminMessage.TYPE_STOP );
637             }
638         }
639 
640         document.setTitle( strDocumentTitle );
641         document.setSummary( strDocumentSummary );
642         document.setComment( strDocumentComment );
643         document.setDateValidityBegin( dateValidityBegin );
644         document.setDateValidityEnd( dateValidityEnd );
645         document.setMailingListId( nMailingListId );
646         document.setPageTemplateDocumentId( nPageTemplateDocumentId );
647         document.setSkipPortlet( bSkipPortlet );
648         document.setSkipCategories( bSkipCategories );
649 
650         MetadataHandler hMetadata = documentType.metadataHandler(  );
651 
652         if ( hMetadata != null )
653         {
654             document.setXmlMetadata( hMetadata.getXmlMetadata( mRequest.getParameterMap(  ) ) );
655         }
656 
657         document.setAttributes( listAttributes );
658 
659         //Categories
660         List<Category> listCategories = new ArrayList<Category>(  );
661 
662         if ( arrayCategory != null )
663         {
664             for ( String strIdCategory : arrayCategory )
665             {
666                 listCategories.add( CategoryHome.find( IntegerUtils.convert( strIdCategory ) ) );
667             }
668         }
669 
670         document.setCategories( listCategories );
671 
672         return null; // No error
673     }
674 
675     /**
676      * Update the specify attribute with the mRequest parameters
677      *
678      * @param attribute The {@link DocumentAttribute} to update
679      * @param document The {@link Document}
680      * @param mRequest The multipart http request
681      * @param locale The locale
682      * @return an admin message if error or null else
683      */
684     private String setAttribute( DocumentAttribute attribute, Document document, MultipartHttpServletRequest mRequest,
685         Locale locale )
686     {
687         String strParameterStringValue = mRequest.getParameter( attribute.getCode(  ) );
688         FileItem fileParameterBinaryValue = mRequest.getFile( attribute.getCode(  ) );
689         String strIsUpdatable = mRequest.getParameter( PARAMETER_ATTRIBUTE_UPDATE + attribute.getCode(  ) );
690         String strToResize = mRequest.getParameter( attribute.getCode(  ) + PARAMETER_CROPPABLE );
691         boolean bIsUpdatable = ( ( strIsUpdatable == null ) || strIsUpdatable.equals( "" ) ) ? false : true;
692         boolean bToResize = ( ( strToResize == null ) || strToResize.equals( "" ) ) ? false : true;
693 
694         if ( strParameterStringValue != null ) // If the field is a string
695         {
696             // Check for mandatory value
697             if ( attribute.isRequired(  ) && strParameterStringValue.trim(  ).equals( "" ) )
698             {
699                 return AdminMessageService.getMessageUrl( mRequest, Messages.MANDATORY_FIELDS, AdminMessage.TYPE_STOP );
700             }
701 
702             // Check for specific attribute validation
703             AttributeManager manager = AttributeService.getInstance(  ).getManager( attribute.getCodeAttributeType(  ) );
704             String strValidationErrorMessage = manager.validateValue( attribute.getId(  ), strParameterStringValue,
705                     locale );
706 
707             if ( strValidationErrorMessage != null )
708             {
709                 String[] listArguments = { attribute.getName(  ), strValidationErrorMessage };
710 
711                 return AdminMessageService.getMessageUrl( mRequest, MESSAGE_ATTRIBUTE_VALIDATION_ERROR, listArguments,
712                     AdminMessage.TYPE_STOP );
713             }
714 
715             attribute.setTextValue( strParameterStringValue );
716         }
717         else if ( fileParameterBinaryValue != null ) // If the field is a file
718         {
719             attribute.setBinary( true );
720 
721             String strContentType = fileParameterBinaryValue.getContentType(  );
722             byte[] bytes = fileParameterBinaryValue.get(  );
723             String strFileName = fileParameterBinaryValue.getName(  );
724             String strExtension = FilenameUtils.getExtension( strFileName );
725 
726             AttributeManager manager = AttributeService.getInstance(  ).getManager( attribute.getCodeAttributeType(  ) );
727 
728             if ( !bIsUpdatable )
729             {
730                 // there is no new value then take the old file value
731                 DocumentAttribute oldAttribute = document.getAttribute( attribute.getCode(  ) );
732 
733                 if ( ( oldAttribute != null ) && ( oldAttribute.getBinaryValue(  ) != null ) &&
734                         ( oldAttribute.getBinaryValue(  ).length > 0 ) )
735                 {
736                     bytes = oldAttribute.getBinaryValue(  );
737                     strContentType = oldAttribute.getValueContentType(  );
738                     strFileName = oldAttribute.getTextValue(  );
739                     strExtension = FilenameUtils.getExtension( strFileName );
740                 }
741             }
742 
743             List<AttributeTypeParameter> parameters = manager.getExtraParametersValues( locale, attribute.getId(  ) );
744 
745             String extensionList = StringUtils.EMPTY;
746 
747             if ( CollectionUtils.isNotEmpty( parameters ) &&
748                     CollectionUtils.isNotEmpty( parameters.get( 0 ).getValueList(  ) ) )
749             {
750                 extensionList = parameters.get( 0 ).getValueList(  ).get( 0 );
751             }
752 
753             // Check for mandatory value
754             if ( attribute.isRequired(  ) && ( ( bytes == null ) || ( bytes.length == 0 ) ) )
755             {
756                 return AdminMessageService.getMessageUrl( mRequest, Messages.MANDATORY_FIELDS, AdminMessage.TYPE_STOP );
757             }
758             else if ( StringUtils.isNotBlank( extensionList ) && !extensionList.contains( strExtension ) )
759             {
760                 Object[] params = new Object[2];
761                 params[0] = attribute.getName(  );
762                 params[1] = extensionList;
763 
764                 return AdminMessageService.getMessageUrl( mRequest, MESSAGE_EXTENSION_ERROR, params,
765                     AdminMessage.TYPE_STOP );
766             }
767 
768             // Check for specific attribute validation
769             String strValidationErrorMessage = manager.validateValue( attribute.getId(  ), strFileName, locale );
770 
771             if ( strValidationErrorMessage != null )
772             {
773                 String[] listArguments = { attribute.getName(  ), strValidationErrorMessage };
774 
775                 return AdminMessageService.getMessageUrl( mRequest, MESSAGE_ATTRIBUTE_VALIDATION_ERROR, listArguments,
776                     AdminMessage.TYPE_STOP );
777             }
778 
779             if ( bToResize && !ArrayUtils.isEmpty( bytes ) )
780             {
781                 // Resize image
782                 String strWidth = mRequest.getParameter( attribute.getCode(  ) + PARAMETER_WIDTH );
783 
784                 if ( StringUtils.isBlank( strWidth ) || !StringUtils.isNumeric( strWidth ) )
785                 {
786                     String[] listArguments = 
787                         {
788                             attribute.getName(  ),
789                             I18nService.getLocalizedString( MESSAGE_ATTRIBUTE_WIDTH_ERROR, mRequest.getLocale(  ) )
790                         };
791 
792                     return AdminMessageService.getMessageUrl( mRequest, MESSAGE_ATTRIBUTE_VALIDATION_ERROR,
793                         listArguments, AdminMessage.TYPE_STOP );
794                 }
795 
796                 try
797                 {
798                     bytes = ImageUtils.resizeImage( bytes, Integer.valueOf( strWidth ) );
799                 }
800                 catch ( IOException e )
801                 {
802                     return AdminMessageService.getMessageUrl( mRequest, MESSAGE_ATTRIBUTE_RESIZE_ERROR,
803                         AdminMessage.TYPE_STOP );
804                 }
805             }
806 
807             attribute.setBinaryValue( bytes );
808             attribute.setValueContentType( strContentType );
809             attribute.setTextValue( strFileName );
810         }
811 
812         return null;
813     }
814 }