View Javadoc
1   /*
2    * Copyright (c) 2002-2021, City of Paris
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met:
8    *
9    *  1. Redistributions of source code must retain the above copyright notice
10   *     and the following disclaimer.
11   *
12   *  2. Redistributions in binary form must reproduce the above copyright notice
13   *     and the following disclaimer in the documentation and/or other materials
14   *     provided with the distribution.
15   *
16   *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
17   *     contributors may be used to endorse or promote products derived from
18   *     this software without specific prior written permission.
19   *
20   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
24   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30   * POSSIBILITY OF SUCH DAMAGE.
31   *
32   * License 1.0
33   */
34  package fr.paris.lutece.plugins.extend.modules.comment.web;
35  
36  import java.io.IOException;
37  import java.lang.reflect.InvocationTargetException;
38  import java.util.Collection;
39  import java.util.HashMap;
40  import java.util.List;
41  import java.util.Locale;
42  import java.util.Map;
43  import java.util.Set;
44  
45  import javax.servlet.http.HttpServletRequest;
46  import javax.servlet.http.HttpServletResponse;
47  import javax.servlet.http.HttpSession;
48  import javax.validation.ConstraintViolation;
49  
50  import org.apache.commons.beanutils.BeanUtils;
51  import org.apache.commons.lang3.StringUtils;
52  
53  import fr.paris.lutece.plugins.extend.modules.comment.business.AddCommentPosition;
54  import fr.paris.lutece.plugins.extend.modules.comment.business.Comment;
55  import fr.paris.lutece.plugins.extend.modules.comment.business.config.CommentExtenderConfig;
56  import fr.paris.lutece.plugins.extend.modules.comment.service.CommentListenerService;
57  import fr.paris.lutece.plugins.extend.modules.comment.service.CommentPlugin;
58  import fr.paris.lutece.plugins.extend.modules.comment.service.CommentService;
59  import fr.paris.lutece.plugins.extend.modules.comment.service.ICommentService;
60  import fr.paris.lutece.plugins.extend.modules.comment.service.extender.CommentResourceExtender;
61  import fr.paris.lutece.plugins.extend.modules.comment.util.constants.CommentConstants;
62  import fr.paris.lutece.plugins.extend.service.ExtendPlugin;
63  import fr.paris.lutece.plugins.extend.service.extender.IResourceExtenderService;
64  import fr.paris.lutece.plugins.extend.service.extender.ResourceExtenderService;
65  import fr.paris.lutece.plugins.extend.service.extender.config.IResourceExtenderConfigService;
66  import fr.paris.lutece.plugins.extend.service.extender.history.IResourceExtenderHistoryService;
67  import fr.paris.lutece.plugins.extend.service.extender.history.ResourceExtenderHistoryService;
68  import fr.paris.lutece.portal.business.mailinglist.Recipient;
69  import fr.paris.lutece.portal.service.captcha.CaptchaSecurityService;
70  import fr.paris.lutece.portal.service.content.XPageAppService;
71  import fr.paris.lutece.portal.service.i18n.I18nService;
72  import fr.paris.lutece.portal.service.mail.MailService;
73  import fr.paris.lutece.portal.service.mailinglist.AdminMailingListService;
74  import fr.paris.lutece.portal.service.message.AdminMessage;
75  import fr.paris.lutece.portal.service.message.AdminMessageService;
76  import fr.paris.lutece.portal.service.message.SiteMessage;
77  import fr.paris.lutece.portal.service.message.SiteMessageException;
78  import fr.paris.lutece.portal.service.message.SiteMessageService;
79  import fr.paris.lutece.portal.service.plugin.Plugin;
80  import fr.paris.lutece.portal.service.plugin.PluginService;
81  import fr.paris.lutece.portal.service.prefs.UserPreferencesService;
82  import fr.paris.lutece.portal.service.security.LuteceUser;
83  import fr.paris.lutece.portal.service.security.SecurityService;
84  import fr.paris.lutece.portal.service.security.UserNotSignedException;
85  import fr.paris.lutece.portal.service.spring.SpringContextService;
86  import fr.paris.lutece.portal.service.template.AppTemplateService;
87  import fr.paris.lutece.portal.service.util.AppLogService;
88  import fr.paris.lutece.portal.service.util.AppPathService;
89  import fr.paris.lutece.portal.service.util.AppPropertiesService;
90  import fr.paris.lutece.portal.util.mvc.commons.annotations.Action;
91  import fr.paris.lutece.portal.util.mvc.utils.MVCUtils;
92  import fr.paris.lutece.portal.web.LocalVariables;
93  import fr.paris.lutece.portal.web.constants.Messages;
94  import fr.paris.lutece.portal.web.util.LocalizedDelegatePaginator;
95  import fr.paris.lutece.portal.web.xpages.XPage;
96  import fr.paris.lutece.portal.web.xpages.XPageApplication;
97  import fr.paris.lutece.util.beanvalidation.BeanValidationUtil;
98  import fr.paris.lutece.util.html.HtmlTemplate;
99  import fr.paris.lutece.util.html.IPaginator;
100 import fr.paris.lutece.util.html.Paginator;
101 import fr.paris.lutece.util.http.SecurityUtil;
102 import fr.paris.lutece.util.url.UrlItem;
103 
104 /**
105  * 
106  * CommentApp
107  * 
108  */
109 public class CommentApp implements XPageApplication
110 {
111     //
112     private static final String MESSAGE_STOP_GENERIC_MESSAGE = "module.extend.comment.message.stop.genericMessage";
113     private static final String MESSAGE_ERROR_BAD_JCAPTCHA = "module.extend.comment.message.error.badJcaptcha";
114 
115     // PROPERTIES
116     private static final String PROPERTY_USE_CAPTCHA = "module.extend.comment.useCaptcha";
117     private static final String PROPERTY_USER_INFO_EMAIL = "module.extend.comment.userInfo.email";
118 
119     // MARKS
120     private static final String MARK_CAPTCHA = "captcha";
121     private static final String MARK_IS_ACTIVE_CAPTCHA = "is_active_captcha";
122 
123     // TEMPLATES
124     private static final String TEMPLATE_XPAGE_VIEW_COMMENTS = "skin/plugins/extend/modules/comment/view_comments.html";
125     private static final String TEMPLATE_XPAGE_ADD_COMMENT = "skin/plugins/extend/modules/comment/add_comment.html";
126     private static final String TEMPLATE_XPAGE_MESSAGE_COMMENT_CREATED = "skin/plugins/extend/modules/comment/message_comment_created.html";
127     private static final String TEMPLATE_COMMENT_NOTIFY_MESSAGE = "skin/plugins/extend/modules/comment/comment_notify_message.html";
128 
129     // Jsp redirections
130     private static final String JSP_PORTAL = "jsp/site/Portal.jsp";
131     private static final String JSP_URL_DEFAULT_POST_BACK = "jsp/site/Portal.jsp?page=extend-comment";
132 
133     // CONSTANTS
134     private static final String JCAPTCHA_PLUGIN = "jcaptcha";
135     private static final String HTML_BR = "<br />";
136     private static final String CONSTANT_AND = "&";
137     private static final String CONSTANT_AND_HTML = "%26";
138 
139     // VARIABLES
140     private static ICommentService _commentService;
141     private static IResourceExtenderConfigService _configService;
142     private static IResourceExtenderService _resourceExtenderService;
143     private static IResourceExtenderHistoryService _resourceHistoryService;
144 
145     private static int _nDefaultItemsPerPage;
146 
147     private final boolean _bIsCaptchaEnabled = PluginService.isPluginEnable( JCAPTCHA_PLUGIN )
148             && Boolean.parseBoolean( AppPropertiesService.getProperty( PROPERTY_USE_CAPTCHA, Boolean.TRUE.toString( ) ) );
149 
150     /**
151      * {@inheritDoc}
152      */
153     @Override
154     public XPage getPage( HttpServletRequest request, int nMode, Plugin plugin ) throws UserNotSignedException, SiteMessageException
155     {
156         XPage page = null;
157 
158         // Check if the extender is indeed in the parameters
159         String strIdExtendableResource = request.getParameter( CommentConstants.PARAMETER_ID_EXTENDABLE_RESOURCE );
160         String strExtendableResourceType = request.getParameter( CommentConstants.PARAMETER_EXTENDABLE_RESOURCE_TYPE );
161 
162         if ( StringUtils.isBlank( strIdExtendableResource ) || StringUtils.isBlank( strExtendableResourceType ) )
163         {
164             SiteMessageService.setMessage( request, Messages.MANDATORY_FIELDS, SiteMessage.TYPE_STOP );
165         }
166 
167         String strAction = request.getParameter( CommentConstants.PARAMETER_ACTION );
168 
169         if ( StringUtils.isNotBlank( strAction ) )
170         {
171             if ( CommentConstants.ACTION_ADD_COMMENT.equals( strAction ) )
172             {
173                 page = getAddCommentPage( request, strIdExtendableResource, strExtendableResourceType );
174             }
175             else
176                 if ( CommentConstants.ACTION_DO_ADD_COMMENT.equals( strAction ) )
177                 {
178                     page = doAddComment( request, strIdExtendableResource, strExtendableResourceType );
179                 }
180                 else
181                     if ( CommentConstants.ACTION_CONFIRM_REMOVE_COMMENT.equals( strAction ) )
182                     {
183                         String strIdComment = String.valueOf( request.getParameter( CommentConstants.PARAMETER_ID_COMMENT ) );
184                         String strFromUrl = (String) request.getSession( ).getAttribute( ExtendPlugin.PLUGIN_NAME + CommentConstants.PARAMETER_FROM_URL );
185                         if ( strFromUrl != null )
186                         {
187                             strFromUrl = strFromUrl.replaceAll( CONSTANT_AND, CONSTANT_AND_HTML );
188                         }
189                         Map<String, Object> requestParameters = new HashMap<String, Object>( );
190                         requestParameters.put( CommentConstants.PARAMETER_PAGE, "extend-comment" );
191                         requestParameters.put( MVCUtils.PARAMETER_ACTION, CommentConstants.ACTION_REMOVE_COMMENT );
192                         requestParameters.put( CommentConstants.PARAMETER_ID_EXTENDABLE_RESOURCE, strIdExtendableResource );
193                         requestParameters.put( CommentConstants.PARAMETER_EXTENDABLE_RESOURCE_TYPE, strExtendableResourceType );
194                         requestParameters.put( CommentConstants.PARAMETER_ID_COMMENT, strIdComment );
195                         requestParameters.put( CommentConstants.PARAMETER_CONFIRM_REMOVE_COMMENT, "1" );
196                         requestParameters.put( CommentConstants.PARAMETER_FROM_URL, strFromUrl );
197                         SiteMessageService.setMessage( request, CommentConstants.MESSAGE_CONFIRM_REMOVE_COMMENT, SiteMessage.TYPE_CONFIRMATION, JSP_PORTAL,
198                                 requestParameters );
199                     }
200                     else
201                         if ( CommentConstants.ACTION_REMOVE_COMMENT.equals( strAction ) )
202                         {
203                             page = doRemoveComment( request, strIdExtendableResource, strExtendableResourceType );
204                         }
205         }
206 
207         if ( page == null )
208         {
209             page = getViewCommentPage( request, strIdExtendableResource, strExtendableResourceType );
210         }
211 
212         return page;
213     }
214 
215     /**
216      * Gets the view comment page.
217      * 
218      * @param request
219      *            the request
220      * @param strIdExtendableResource
221      *            the str id extendable resource
222      * @param strExtendableResourceType
223      *            the str extendable resource type
224      * @param strPostBackUrl
225      *            The URL to use for post backs.
226      * @return the view comment page
227      */
228     public static String getViewCommentPageContent( HttpServletRequest request, String strIdExtendableResource, String strExtendableResourceType,
229             String strPostBackUrl )
230     {
231         request.getSession( ).setAttribute( ExtendPlugin.PLUGIN_NAME + CommentConstants.SESSION_COMMENT_POST_BACK_URL, strPostBackUrl );
232 
233         Integer nItemsPerPage = getDefaultItemsPerPage( );
234         String strCurrentPageIndex = CommentConstants.CONSTANT_FIRST_PAGE_NUMBER;
235         Boolean bIsAscSort = false;
236         Object object = request.getSession( ).getAttribute( CommentConstants.SESSION_COMMENT_ITEMS_PER_PAGE );
237         if ( object != null )
238         {
239             nItemsPerPage = (Integer) object;
240         }
241         object = request.getSession( ).getAttribute( CommentConstants.SESSION_COMMENT_CURRENT_PAGE_INDEX );
242         if ( object != null )
243         {
244             strCurrentPageIndex = (String) object;
245         }
246         object = request.getSession( ).getAttribute( CommentConstants.SESSION_COMMENT_IS_ASC_SORT );
247         if ( object != null )
248         {
249             bIsAscSort = (Boolean) object;
250         }
251 
252         String strSort = request.getParameter( CommentConstants.MARK_ASC_SORT );
253         if ( !StringUtils.isEmpty( strSort ) )
254         {
255             bIsAscSort = Boolean.parseBoolean( strSort );
256         }
257 
258         String strFromUrl = request.getParameter( CommentConstants.PARAMETER_FROM_URL );
259         if ( CommentConstants.FROM_SESSION.equals( strFromUrl ) )
260         {
261             strFromUrl = (String) request.getSession( ).getAttribute( ExtendPlugin.PLUGIN_NAME + CommentConstants.PARAMETER_FROM_URL );
262         }
263         if ( StringUtils.isEmpty( strFromUrl ) )
264         {
265             strFromUrl = request.getHeader( CommentConstants.PARAMETER_REFERER );
266         }
267         if ( strFromUrl != null )
268         {
269             strFromUrl = strFromUrl.replace( CONSTANT_AND, CONSTANT_AND_HTML );
270         }
271         request.getSession( ).setAttribute( ExtendPlugin.PLUGIN_NAME + CommentConstants.PARAMETER_FROM_URL, strFromUrl );
272 
273         strCurrentPageIndex = Paginator.getPageIndex( request, Paginator.PARAMETER_PAGE_INDEX, strCurrentPageIndex );
274         int nOldITemsPerPage = nItemsPerPage;
275         nItemsPerPage = Paginator.getItemsPerPage( request, Paginator.PARAMETER_ITEMS_PER_PAGE, nItemsPerPage, getDefaultItemsPerPage( ) );
276         if ( nItemsPerPage <= 0 )
277         {
278             nItemsPerPage = getDefaultItemsPerPage( );
279         }
280         // If we changed the number of items per page, we go back to the first page
281         if ( nItemsPerPage != nOldITemsPerPage )
282         {
283             strCurrentPageIndex = CommentConstants.CONSTANT_FIRST_PAGE_NUMBER;
284         }
285         UrlItem urlSort = new UrlItem( strPostBackUrl );
286         urlSort.addParameter( CommentConstants.MARK_ID_EXTENDABLE_RESOURCE, strIdExtendableResource );
287         urlSort.addParameter( CommentConstants.MARK_EXTENDABLE_RESOURCE_TYPE, strExtendableResourceType );
288         urlSort.addParameter( CommentConstants.MARK_ASC_SORT, strSort );
289         if ( StringUtils.isNotEmpty( strFromUrl ) )
290         {
291             urlSort.addParameter( CommentConstants.PARAMETER_FROM_URL, CommentConstants.FROM_SESSION );
292         }
293         boolean bGetSubComments = false;
294         boolean bUseBBCodeEditor = false;
295         boolean bAllowSubComments = false;
296         boolean bEnableAuthMode = false;
297         String strAdminBadge = StringUtils.EMPTY;
298         CommentExtenderConfig config = getConfigService( ).find( CommentResourceExtender.EXTENDER_TYPE_COMMENT, strIdExtendableResource,
299                 strExtendableResourceType );
300         if ( config != null )
301         {
302             bGetSubComments = config.getAuthorizeSubComments( );
303             bUseBBCodeEditor = config.getUseBBCodeEditor( );
304             bAllowSubComments = config.getAuthorizeSubComments( );
305             bEnableAuthMode = config.isEnabledAuthMode( );
306             strAdminBadge = config.getAdminBadge( );
307         }
308         int nItemsOffset = nItemsPerPage * ( Integer.parseInt( strCurrentPageIndex ) - 1 );
309 
310         List<Comment> listItems = getCommentService( ).findByResource( strIdExtendableResource, strExtendableResourceType, true, null, bIsAscSort, nItemsOffset,
311                 nItemsPerPage, bGetSubComments );
312 
313         int nItemsCount = getCommentService( ).getCommentNb( strIdExtendableResource, strExtendableResourceType, true, true );
314 
315         IPaginator<Comment> paginator = new LocalizedDelegatePaginator<Comment>( listItems, nItemsPerPage, urlSort.getUrl( ), Paginator.PARAMETER_PAGE_INDEX,
316                 strCurrentPageIndex, nItemsCount, request.getLocale( ) );
317 
318         Map<String, Object> model = new HashMap<String, Object>( );
319         model.put( CommentConstants.MARK_ID_EXTENDABLE_RESOURCE, strIdExtendableResource );
320         model.put( CommentConstants.MARK_EXTENDABLE_RESOURCE_TYPE, strExtendableResourceType );
321         model.put( CommentConstants.MARK_PAGINATOR, paginator );
322         model.put( CommentConstants.MARK_ASC_SORT, strSort );
323         model.put( CommentConstants.PARAMETER_FROM_URL, strFromUrl );
324         model.put( CommentConstants.PARAMETER_ID_COMMENT, request.getParameter( CommentConstants.PARAMETER_ID_COMMENT ) );
325         model.put( CommentConstants.MARK_USE_BBCODE, bUseBBCodeEditor );
326         model.put( CommentConstants.MARK_ENABLE_AUTH_MODE, bEnableAuthMode );
327         model.put( CommentConstants.MARK_ALLOW_SUB_COMMENTS, bAllowSubComments );
328         model.put( CommentConstants.MARK_ADMIN_BADGE, strAdminBadge );
329         model.put( CommentConstants.PARAMETER_POST_BACK_URL, strPostBackUrl );
330 
331         LuteceUser user = SecurityService.getInstance( ).getRegisteredUser( request );
332 
333         if ( user != null )
334         {
335             model.put( CommentConstants.MARK_REGISTERED_USER_EMAIL, user.getEmail( ) );
336         }
337 
338         HtmlTemplate template = AppTemplateService.getTemplate( TEMPLATE_XPAGE_VIEW_COMMENTS, request.getLocale( ), model );
339 
340         HttpSession session = request.getSession( );
341         session.setAttribute( CommentConstants.SESSION_COMMENT_ITEMS_PER_PAGE, nItemsPerPage );
342         session.setAttribute( CommentConstants.SESSION_COMMENT_CURRENT_PAGE_INDEX, strCurrentPageIndex );
343         session.setAttribute( CommentConstants.SESSION_COMMENT_IS_ASC_SORT, bIsAscSort );
344 
345         return template.getHtml( );
346     }
347 
348     /**
349      * Gets the view comment page.
350      * 
351      * @param request
352      *            the request
353      * @param strIdExtendableResource
354      *            the str id extendable resource
355      * @param strExtendableResourceType
356      *            the str extendable resource type
357      * @return the view comment page
358      */
359     public XPage getViewCommentPage( HttpServletRequest request, String strIdExtendableResource, String strExtendableResourceType )
360     {
361         XPage page = new XPage( );
362         page.setTitle( I18nService.getLocalizedString( CommentConstants.PROPERTY_XPAGE_VIEW_COMMENTS_PAGE_TITLE, request.getLocale( ) ) );
363         page.setPathLabel( I18nService.getLocalizedString( CommentConstants.PROPERTY_XPAGE_VIEW_COMMENTS_PATH_LABEL, request.getLocale( ) ) );
364 
365         page.setContent( getViewCommentPageContent( request, strIdExtendableResource, strExtendableResourceType, JSP_URL_DEFAULT_POST_BACK ) );
366         return page;
367     }
368 
369     /**
370      * Gets the adds the comment page.
371      * 
372      * @param request
373      *            the request
374      * @param strIdExtendableResource
375      *            the str id extendable resource
376      * @param strExtendableResourceType
377      *            the str extendable resource type
378      * @return the adds the comment page
379      */
380     private XPage getAddCommentPage( HttpServletRequest request, String strIdExtendableResource, String strExtendableResourceType )
381             throws UserNotSignedException
382     {
383         XPage page = new XPage( );
384 
385         page.setTitle( I18nService.getLocalizedString( CommentConstants.PROPERTY_XPAGE_ADD_COMMENT_PAGE_TITLE, request.getLocale( ) ) );
386         page.setPathLabel( I18nService.getLocalizedString( CommentConstants.PROPERTY_XPAGE_ADD_COMMENT_PAGE_LABEL, request.getLocale( ) ) );
387 
388         String strFromUrl = request.getParameter( CommentConstants.PARAMETER_FROM_URL );
389         if ( CommentConstants.FROM_SESSION.equals( strFromUrl ) )
390         {
391             strFromUrl = (String) request.getSession( ).getAttribute( ExtendPlugin.PLUGIN_NAME + CommentConstants.PARAMETER_FROM_URL );
392         }
393         if ( StringUtils.isEmpty( strFromUrl ) )
394         {
395             strFromUrl = request.getHeader( CommentConstants.PARAMETER_REFERER );
396         }
397         if ( strFromUrl != null )
398         {
399             strFromUrl = strFromUrl.replace( CONSTANT_AND, CONSTANT_AND_HTML );
400         }
401         request.getSession( ).setAttribute( ExtendPlugin.PLUGIN_NAME + CommentConstants.PARAMETER_FROM_URL, strFromUrl );
402 
403         CommentExtenderConfig config = getConfigService( ).find( CommentResourceExtender.EXTENDER_TYPE_COMMENT, strIdExtendableResource,
404                 strExtendableResourceType );
405 
406         if ( config != null )
407         {
408             Map<String, Object> model = new HashMap<String, Object>( );
409 
410             if ( config.isEnabledAuthMode( ) )
411             {
412                 LuteceUser user = SecurityService.getInstance( ).getRegisteredUser( request );
413                 if ( user == null )
414                 {
415                     throw new UserNotSignedException( );
416                 }
417                 model.put( CommentConstants.MARK_NICKNAME, UserPreferencesService.instance( ).getNickname( user ) );
418             }
419             model.put( CommentConstants.MARK_ADD_COMMENT_POSITION, config.getAddCommentPosition( ) );
420             model.put( CommentConstants.MARK_COMMENT_CONFIG, config );
421             model.put( CommentConstants.MARK_ID_EXTENDABLE_RESOURCE, strIdExtendableResource );
422             model.put( CommentConstants.MARK_EXTENDABLE_RESOURCE_TYPE, strExtendableResourceType );
423             model.put( CommentConstants.PARAMETER_FROM_URL, strFromUrl );
424             model.put( CommentConstants.MARK_RETURN_TO_COMMENT_LIST,
425                     Boolean.parseBoolean( request.getParameter( CommentConstants.MARK_RETURN_TO_COMMENT_LIST ) ) );
426             model.put( CommentConstants.PARAMETER_ID_COMMENT, request.getParameter( CommentConstants.PARAMETER_ID_COMMENT ) );
427             model.put( CommentConstants.MARK_WEBAPP_URL, AppPathService.getBaseUrl( request ) );
428             model.put( CommentConstants.MARK_LOCALE, Locale.getDefault( ) );
429 
430             // Add Captcha
431             model.put( MARK_IS_ACTIVE_CAPTCHA, _bIsCaptchaEnabled );
432 
433             if ( _bIsCaptchaEnabled )
434             {
435                 CaptchaSecurityService captchaService = new CaptchaSecurityService( );
436                 model.put( MARK_CAPTCHA, captchaService.getHtmlCode( ) );
437             }
438 
439             if ( SecurityService.isAuthenticationEnable( ) )
440             {
441                 LuteceUser user = SecurityService.getInstance( ).getRegisteredUser( request );
442 
443                 if ( user != null )
444                 {
445                     model.put( CommentConstants.MARK_MYLUTECE_USER, user );
446                 }
447             }
448 
449             HtmlTemplate template = AppTemplateService.getTemplate( TEMPLATE_XPAGE_ADD_COMMENT, request.getLocale( ), model );
450 
451             page.setContent( template.getHtml( ) );
452         }
453         else
454         {
455             return redirectToLastUrl( request, CommentConstants.MESSAGE_ERROR_GENERIC_MESSAGE, strIdExtendableResource );
456         }
457         return page;
458     }
459 
460     /**
461      * Do add comment.
462      * 
463      * @param request
464      *            the request
465      * @param strIdExtendableResource
466      *            the str id extendable resource
467      * @param strExtendableResourceType
468      *            the str extendable resource type
469      * @return The page to display, or null to display the default page
470      * @throws SiteMessageException
471      *             the site message exception
472      */
473     private XPage doAddComment( HttpServletRequest request, String strIdExtendableResource, String strExtendableResourceType )
474             throws SiteMessageException, UserNotSignedException
475     {
476 
477         Commentugins/extend/modules/comment/business/Comment.html#Comment">Comment comment = new Comment( );
478         LuteceUser user = null;
479         try
480         {
481             BeanUtils.populate( comment, request.getParameterMap( ) );
482         }
483         catch( IllegalAccessException e )
484         {
485             AppLogService.error( "Unable to fetch data from request", e );
486         }
487         catch( InvocationTargetException e )
488         {
489             AppLogService.error( "Unable to fetch data from request", e );
490         }
491 
492         // Test the captcha
493         if ( _bIsCaptchaEnabled )
494         {
495             CaptchaSecurityService captchaService = new CaptchaSecurityService( );
496 
497             if ( !captchaService.validate( request ) )
498             {
499                 SiteMessageService.setMessage( request, MESSAGE_ERROR_BAD_JCAPTCHA, SiteMessage.TYPE_STOP );
500             }
501         }
502 
503         CommentExtenderConfig config = getConfigService( ).find( CommentResourceExtender.EXTENDER_TYPE_COMMENT, strIdExtendableResource,
504                 strExtendableResourceType );
505         // test authentication if needed
506         if ( config != null && config.isEnabledAuthMode( ) )
507         {
508             user = SecurityService.getInstance( ).getRegisteredUser( request );
509             if ( user == null )
510             {
511                 throw new UserNotSignedException( );
512             }
513         }
514 
515         // Check mandatory fields
516         Set<ConstraintViolation<Comment>> constraintViolations = BeanValidationUtil.validate( comment );
517 
518         if ( constraintViolations.size( ) > 0 )
519         {
520             Object [ ] params = {
521                     buildStopMessage( constraintViolations )
522             };
523 
524             SiteMessageService.setMessage( request, MESSAGE_STOP_GENERIC_MESSAGE, params, SiteMessage.TYPE_STOP );
525         }
526 
527         if ( StringUtils.isBlank( comment.getName( ) ) || StringUtils.isBlank( comment.getComment( ) ) || StringUtils.isBlank( comment.getEmail( ) ) )
528         {
529             SiteMessageService.setMessage( request, Messages.MANDATORY_FIELDS, SiteMessage.TYPE_STOP );
530         }
531 
532         if ( config != null )
533         {
534             if ( config.isEnabledAuthMode( ) )
535             {
536                 String strParamError = CommentListenerService.checkComment( comment.getComment( ), strExtendableResourceType,
537                         comment.getIdExtendableResource( ), user.getName( ) );
538                 Object [ ] paramsError = {
539                         strParamError
540                 };
541 
542                 if ( strParamError != null && StringUtils.isNotEmpty( strParamError ) )
543                 {
544                     SiteMessageService.setMessage( request, MESSAGE_STOP_GENERIC_MESSAGE, paramsError, SiteMessage.TYPE_STOP );
545                 }
546 
547                 comment.setEmail( user.getUserInfo( AppPropertiesService.getProperty( PROPERTY_USER_INFO_EMAIL ) ) );
548                 comment.setLuteceUserName( user.getName( ) );
549                 if ( !StringUtils.isEmpty( comment.getName( ) ) )
550                 {
551                     if ( UserPreferencesService.instance( ).getNickname( user ) == null
552                             || !UserPreferencesService.instance( ).getNickname( user ).equals( comment.getName( ) ) )
553                     {
554                         UserPreferencesService.instance( ).setNickname( user.getName( ), comment.getName( ) );
555                     }
556                 }
557             }
558 
559             comment.setIpAddress( SecurityUtil.getRealIp( request ) );
560             comment.setIdExtendableResource( strIdExtendableResource );
561             comment.setExtendableResourceType( strExtendableResourceType );
562             comment.setPublished( !config.isModerated( ) );
563             if ( !config.getAuthorizeSubComments( ) )
564             {
565                 comment.setIdParentComment( 0 );
566             }
567             boolean bIsCreated = false;
568 
569             comment.setComment( comment.getComment( ).replaceAll( "\r", "<br />" ) );
570             try
571             {
572                 getCommentService( ).create( comment, request );
573                 bIsCreated = true;
574             }
575             catch( Exception ex )
576             {
577                 // Something wrong happened... a database check might be needed
578                 AppLogService.error( ex.getMessage( ) + " when creating a comment ", ex );
579                 // Revert
580                 getCommentService( ).remove( comment.getIdComment( ) );
581 
582                 SiteMessageService.setMessage( request, CommentConstants.MESSAGE_ERROR_GENERIC_MESSAGE, SiteMessage.TYPE_ERROR );
583             }
584 
585             if ( bIsCreated )
586             {
587                 // Add to the resource extender history
588                 getResourceExtenderHistoryService( ).create( CommentResourceExtender.EXTENDER_TYPE_COMMENT, strIdExtendableResource, strExtendableResourceType,
589                         request );
590 
591                 // Notify the mailing list
592                 sendCommentNotification( request, comment, config );
593 
594                 XPage page = new XPage( );
595                 page.setTitle( I18nService.getLocalizedString( CommentConstants.PROPERTY_XPAGE_ADD_COMMENT_PAGE_TITLE, request.getLocale( ) ) );
596                 page.setPathLabel( I18nService.getLocalizedString( CommentConstants.PROPERTY_XPAGE_ADD_COMMENT_PAGE_LABEL, request.getLocale( ) ) );
597 
598                 String strPostBackUrl = (String) request.getSession( )
599                         .getAttribute( ExtendPlugin.PLUGIN_NAME + CommentConstants.SESSION_COMMENT_POST_BACK_URL );
600                 if ( strPostBackUrl == null )
601                 {
602                     strPostBackUrl = JSP_URL_DEFAULT_POST_BACK;
603                 }
604 
605                 String strFromUrl = request.getParameter( CommentConstants.PARAMETER_FROM_URL );
606                 if ( CommentConstants.FROM_SESSION.equals( strFromUrl ) )
607                 {
608                     strFromUrl = (String) request.getSession( ).getAttribute( ExtendPlugin.PLUGIN_NAME + CommentConstants.PARAMETER_FROM_URL );
609                 }
610                 if ( strFromUrl != null )
611                 {
612                     strFromUrl = strFromUrl.replaceAll( CONSTANT_AND_HTML, CONSTANT_AND );
613                 }
614 
615                 if ( !request.getParameter( "page" ).equals( CommentPlugin.PLUGIN_NAME ) && config.getAddCommentPosition( ) != AddCommentPosition.NEW_PAGE )
616                 {
617                     return redirectToLastUrl( request, config.getMessageCommentCreated( ), strIdExtendableResource );
618                 }
619 
620                 Map<String, Object> model = new HashMap<String, Object>( );
621 
622                 model.put( CommentConstants.MARK_MESSAGE_COMMENT_CREATED, config.getMessageCommentCreated( ) );
623                 model.put( CommentConstants.MARK_ID_EXTENDABLE_RESOURCE, strIdExtendableResource );
624                 model.put( CommentConstants.MARK_EXTENDABLE_RESOURCE_TYPE, strExtendableResourceType );
625                 model.put( CommentConstants.PARAMETER_ID_COMMENT,
626 
627                         comment.getIdParentComment( ) == 0 ? comment.getIdComment( ) : comment.getIdParentComment( ) );
628                 model.put( CommentConstants.PARAMETER_POST_BACK_URL, strPostBackUrl );
629                 model.put( CommentConstants.MARK_RETURN_TO_COMMENT_LIST,
630                         Boolean.parseBoolean( request.getParameter( CommentConstants.MARK_RETURN_TO_COMMENT_LIST ) ) );
631                 model.put( CommentConstants.PARAMETER_FROM_URL, strFromUrl );
632 
633                 HtmlTemplate template = AppTemplateService.getTemplate( TEMPLATE_XPAGE_MESSAGE_COMMENT_CREATED, request.getLocale( ), model );
634 
635                 page.setContent( template.getHtml( ) );
636 
637                 return page;
638             }
639             return null;
640         }
641         redirectToLastUrl( request, CommentConstants.MESSAGE_ERROR_GENERIC_MESSAGE, strIdExtendableResource );
642         return null;
643     }
644 
645     private XPage redirectToLastUrl( HttpServletRequest request, String message, String strIdExtendableResource )
646     {
647         request.getSession( ).setAttribute( CommentConstants.SESSION_COMMENT_ADD_MESSAGE_RESULT + strIdExtendableResource, message );
648         HttpServletResponse response = LocalVariables.getResponse( );
649         UrlItem url = new UrlItem( (String) request.getSession( ).getAttribute( ExtendPlugin.PLUGIN_NAME + CommentConstants.PARAMETER_FROM_URL ) );
650         url.setAnchor( CommentConstants.ADD_COMMENT_MESSAGE_RESULT_ANCHOR );
651         try
652         {
653             response.sendRedirect( url.getUrl( ) );
654         }
655         catch( IOException e )
656         {
657             // log ?
658         }
659         return new XPage( );
660     }
661 
662     /**
663      * Builds the stop message.
664      * 
665      * @param <A>
666      *            the generic type
667      * @param listErrors
668      *            the list errors
669      * @return the string
670      */
671     private static <A> String buildStopMessage( Set<ConstraintViolation<A>> listErrors )
672     {
673         StringBuilder sbError = new StringBuilder( );
674 
675         if ( ( listErrors != null ) && !listErrors.isEmpty( ) )
676         {
677             for ( ConstraintViolation<A> error : listErrors )
678             {
679                 sbError.append( error.getMessage( ) );
680                 sbError.append( HTML_BR );
681             }
682         }
683 
684         return sbError.toString( );
685     }
686 
687     /**
688      * Send comment notification.
689      * 
690      * @param request
691      *            the request
692      * @param comment
693      *            the document comment
694      * @param config
695      *            the config
696      */
697     private void sendCommentNotification( HttpServletRequest request, Comment comment, CommentExtenderConfig config )
698     {
699         int nMailingListId = config.getIdMailingList( );
700         Collection<Recipient> listRecipients = AdminMailingListService.getRecipients( nMailingListId );
701 
702         for ( Recipient recipient : listRecipients )
703         {
704             Map<String, Object> model = new HashMap<String, Object>( );
705 
706             String strSenderEmail = comment.getEmail( );
707             String strSenderName = comment.getName( );
708             String strResourceName = getResourceExtenderService( ).getExtendableResourceName( comment.getIdExtendableResource( ),
709                     comment.getExtendableResourceType( ) );
710 
711             Object [ ] params = {
712                     strResourceName
713             };
714             String strSubject = I18nService.getLocalizedString( CommentConstants.MESSAGE_NOTIFY_SUBJECT, params, request.getLocale( ) );
715 
716             UrlItem url = new UrlItem( AppPathService.getBaseUrl( request ) + AppPathService.getPortalUrl( ) );
717             url.addParameter( XPageAppService.PARAM_XPAGE_APP, CommentPlugin.PLUGIN_NAME );
718             url.addParameter( CommentConstants.PARAMETER_ID_EXTENDABLE_RESOURCE, comment.getIdExtendableResource( ) );
719             url.addParameter( CommentConstants.PARAMETER_EXTENDABLE_RESOURCE_TYPE, comment.getExtendableResourceType( ) );
720 
721             model.put( CommentConstants.MARK_ID_EXTENDABLE_RESOURCE, comment.getIdExtendableResource( ) );
722             model.put( CommentConstants.MARK_EXTENDABLE_RESOURCE_TYPE, comment.getExtendableResourceType( ) );
723             model.put( CommentConstants.MARK_RESOURCE_EXTENDER_NAME, strResourceName );
724             model.put( CommentConstants.MARK_RESOURCE_EXTENDER_URL, url.getUrl( ) );
725             model.put( CommentConstants.MARK_COMMENT, comment );
726 
727             HtmlTemplate template = AppTemplateService.getTemplate( TEMPLATE_COMMENT_NOTIFY_MESSAGE, request.getLocale( ), model );
728             String strBody = template.getHtml( );
729 
730             MailService.sendMailHtml( recipient.getEmail( ), strSenderName, strSenderEmail, strSubject, strBody );
731         }
732     }
733 
734     /**
735      * Get the default number of items per page
736      * 
737      * @return the default number of items per page
738      */
739     private static int getDefaultItemsPerPage( )
740     {
741         if ( _nDefaultItemsPerPage == 0 )
742         {
743             _nDefaultItemsPerPage = AppPropertiesService.getPropertyInt( CommentConstants.PROPERTY_DEFAULT_LIST_COMMENTS_PER_PAGE, 50 );
744         }
745         return _nDefaultItemsPerPage;
746     }
747 
748     /**
749      * Get the comment service
750      * 
751      * @return the comment service
752      */
753     private static ICommentService getCommentService( )
754     {
755         if ( _commentService == null )
756         {
757             _commentService = SpringContextService.getBean( CommentService.BEAN_SERVICE );
758         }
759         return _commentService;
760     }
761 
762     /**
763      * Get the config service
764      * 
765      * @return the config service
766      */
767     private static IResourceExtenderConfigService getConfigService( )
768     {
769         if ( _configService == null )
770         {
771             _configService = SpringContextService.getBean( CommentConstants.BEAN_CONFIG_SERVICE );
772         }
773         return _configService;
774     }
775 
776     /**
777      * Get the resource extender service
778      * 
779      * @return the resource extender service
780      */
781     private static IResourceExtenderService getResourceExtenderService( )
782     {
783         if ( _resourceExtenderService == null )
784         {
785             _resourceExtenderService = SpringContextService.getBean( ResourceExtenderService.BEAN_SERVICE );
786         }
787         return _resourceExtenderService;
788     }
789 
790     /**
791      * Get the resource extender history service
792      * 
793      * @return the resource extender history service
794      */
795     private static IResourceExtenderHistoryService getResourceExtenderHistoryService( )
796     {
797         if ( _resourceHistoryService == null )
798         {
799             _resourceHistoryService = SpringContextService.getBean( ResourceExtenderHistoryService.BEAN_SERVICE );
800         }
801         return _resourceHistoryService;
802     }
803 
804     /**
805      * Removes comment
806      * 
807      * @param request
808      * @param strIdExtendableResource
809      * @param strExtendableResourceType
810      * @return
811      * @throws SiteMessageException
812      * @throws UserNotSignedException
813      */
814     private XPage doRemoveComment( HttpServletRequest request, String strIdExtendableResource, String strExtendableResourceType )
815             throws SiteMessageException, UserNotSignedException
816     {
817         String strConfirmRemoveComment = String.valueOf( request.getParameter( CommentConstants.PARAMETER_CONFIRM_REMOVE_COMMENT ) );
818         String strIdComment = String.valueOf( request.getParameter( CommentConstants.PARAMETER_ID_COMMENT ) );
819         int nIdComment = Integer.parseInt( strIdComment );
820         Comment comment = getCommentService( ).findByPrimaryKey( nIdComment );
821         LuteceUser user = null;
822 
823         CommentExtenderConfig config = getConfigService( ).find( CommentResourceExtender.EXTENDER_TYPE_COMMENT, strIdExtendableResource,
824                 strExtendableResourceType );
825 
826         if ( comment == null || strConfirmRemoveComment == null )
827         {
828             SiteMessageService.setMessage( request, CommentConstants.MESSAGE_ERROR_GENERIC_MESSAGE, SiteMessage.TYPE_ERROR );
829         }
830 
831         // test authentication if needed
832         if ( config != null && config.isEnabledAuthMode( ) )
833         {
834             user = SecurityService.getInstance( ).getRegisteredUser( request );
835             if ( user == null )
836             {
837                 throw new UserNotSignedException( );
838             }
839             else
840             {
841                 String strParamError = CommentListenerService.checkComment( comment.getComment( ), strExtendableResourceType,
842                         comment.getIdExtendableResource( ), user.getName( ) );
843                 Object [ ] paramsError = {
844                         strParamError
845                 };
846 
847                 if ( strParamError != null && StringUtils.isNotEmpty( strParamError ) )
848                 {
849                     SiteMessageService.setMessage( request, MESSAGE_STOP_GENERIC_MESSAGE, paramsError, SiteMessage.TYPE_STOP );
850                 }
851 
852                 // Remove the comment only if it does not have subcomments && the delete option is enabled
853                 // and the user must be the author
854                 if ( config.getDeleteComments( ) && comment.getEmail( ).equals( user.getEmail( ) )
855                         && ( comment.getListSubComments( ) == null || comment.getListSubComments( ).size( ) == 0 ) )
856                 {
857                     _commentService.remove( nIdComment );
858                 }
859                 else
860                 {
861                     SiteMessageService.setMessage( request, CommentConstants.MESSAGE_ERROR_CANNOT_DELETE, SiteMessage.TYPE_ERROR );
862                 }
863 
864                 String strFromUrl = request.getParameter( CommentConstants.PARAMETER_FROM_URL );
865                 if ( CommentConstants.FROM_SESSION.equals( strFromUrl ) )
866                 {
867                     strFromUrl = (String) request.getSession( ).getAttribute( ExtendPlugin.PLUGIN_NAME + CommentConstants.PARAMETER_FROM_URL );
868                 }
869                 if ( strFromUrl != null )
870                 {
871                     strFromUrl = strFromUrl.replaceAll( CONSTANT_AND_HTML, CONSTANT_AND );
872                 }
873 
874                 HttpServletResponse response = LocalVariables.getResponse( );
875                 try
876                 {
877                     response.sendRedirect( strFromUrl );
878                 }
879                 catch( IOException e )
880                 {
881                     // log ?
882                 }
883                 return new XPage( );
884             }
885         }
886         redirectToLastUrl( request, CommentConstants.MESSAGE_ERROR_GENERIC_MESSAGE, strIdExtendableResource );
887         return null;
888     }
889 }