View Javadoc
1   /*
2    * Copyright (c) 2002-2022, 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.portal.service.page;
35  
36  import java.io.IOException;
37  import java.util.ArrayList;
38  import java.util.Collection;
39  import java.util.Enumeration;
40  import java.util.HashMap;
41  import java.util.List;
42  import java.util.Locale;
43  import java.util.Map;
44  import java.util.Optional;
45  import java.util.Properties;
46  
47  import javax.inject.Inject;
48  import javax.servlet.http.HttpServletRequest;
49  
50  import org.apache.commons.lang3.StringUtils;
51  import org.apache.commons.fileupload.FileItem;
52  import org.apache.commons.lang3.BooleanUtils;
53  
54  import fr.paris.lutece.portal.business.page.Page;
55  import fr.paris.lutece.portal.business.page.PageHome;
56  import fr.paris.lutece.portal.business.page.PageRoleRemovalListener;
57  import fr.paris.lutece.portal.business.portlet.Portlet;
58  import fr.paris.lutece.portal.business.portlet.PortletRoleRemovalListener;
59  import fr.paris.lutece.portal.business.portlet.PortletType;
60  import fr.paris.lutece.portal.business.style.ModeHome;
61  import fr.paris.lutece.portal.business.user.AdminUser;
62  import fr.paris.lutece.portal.service.admin.AdminUserService;
63  import fr.paris.lutece.portal.service.cache.ICacheKeyService;
64  import fr.paris.lutece.portal.service.content.PageData;
65  import fr.paris.lutece.portal.service.html.XmlTransformerService;
66  import fr.paris.lutece.portal.service.i18n.I18nService;
67  import fr.paris.lutece.portal.service.image.ImageResource;
68  import fr.paris.lutece.portal.service.image.ImageResourceManager;
69  import fr.paris.lutece.portal.service.image.ImageResourceProvider;
70  import fr.paris.lutece.portal.service.includes.PageInclude;
71  import fr.paris.lutece.portal.service.includes.PageIncludeService;
72  import fr.paris.lutece.portal.service.message.SiteMessageException;
73  import fr.paris.lutece.portal.service.portal.PortalService;
74  import fr.paris.lutece.portal.service.portal.ThemesService;
75  import fr.paris.lutece.portal.service.portlet.PortletEvent;
76  import fr.paris.lutece.portal.service.portlet.PortletEventListener;
77  import fr.paris.lutece.portal.service.portlet.PortletResourceIdService;
78  import fr.paris.lutece.portal.service.rbac.RBACService;
79  import fr.paris.lutece.portal.service.security.LuteceUser;
80  import fr.paris.lutece.portal.service.security.SecurityService;
81  import fr.paris.lutece.portal.service.template.AppTemplateService;
82  import fr.paris.lutece.portal.service.util.AppException;
83  import fr.paris.lutece.portal.service.util.AppLogService;
84  import fr.paris.lutece.portal.service.util.AppPathService;
85  import fr.paris.lutece.portal.service.util.AppPropertiesService;
86  import fr.paris.lutece.portal.service.util.RemovalListenerService;
87  import fr.paris.lutece.portal.web.LocalVariables;
88  import fr.paris.lutece.portal.web.constants.Parameters;
89  import fr.paris.lutece.portal.web.l10n.LocaleService;
90  import fr.paris.lutece.util.html.HtmlTemplate;
91  import fr.paris.lutece.util.url.UrlItem;
92  
93  /**
94   * This class delivers pages to web componants. It handles XML tranformation to HTML and provides a cache feature in order to reduce the number of
95   * tranformations.
96   */
97  public class PageService implements IPageService, ImageResourceProvider, PageEventListener, PortletEventListener
98  {
99      // //////////////////////////////////////////////////////////////////////////
100     // Variables
101 
102     /**
103      * Key for redirections
104      */
105     public static final String REDIRECTION_KEY = "redirect:";
106 
107     // Templates
108     /** Access denied template */
109     public static final String TEMPLATE_PAGE_ACCESS_DENIED = "/skin/site/page_access_denied.html";
110 
111     /** Access Controlled template */
112     public static final String TEMPLATE_PAGE_ACCESS_CONTROLED = "/skin/site/page_access_controled.html";
113     private static final String TEMPLATE_ADMIN_BUTTONS = "/admin/admin_buttons.html";
114     private static final String TEMPLATE_COLUMN_OUTLINE = "/admin/column_outline.html";
115 
116     // Markers
117     private static final String MARK_PORTLET = "portlet";
118     private static final String MARK_STATUS_PUBLISHED = "portlet_status_published";
119     private static final String MARK_STATUS_UNPUBLISHED = "portlet_status_unpublished";
120     private static final String MARK_CUSTOM_ACTIONS = "custom_action_list";
121     private static final String MARK_URL_LOGIN = "url_login";
122     private static final String MARKER_TARGET = "target";
123     private static final String MARKER_IS_USER_AUTHENTICATED = "is-user-authenticated";
124     private static final String MARK_COLUMN_CONTENT = "column_content";
125     private static final String MARK_COLUMN_ID = "column_id";
126     private static final String MARK_MAX_ORDER = "order_max";
127 
128     // Parameters
129     private static final String PARAMETER_SITE_PATH = "site-path";
130     private static final String PARAMETER_USER_SELECTED_LOCALE = "user-selected-language";
131     private static final String PARAMETER_PLUGIN_NAME = "plugin-name";
132     private static final String PARAMETER_PORTLET = "portlet";
133 
134     // Properties
135     private static final String PROPERTY_MESSAGE_PAGE_ACCESS_DENIED = "portal.site.message.pageAccessDenied";
136     private static final String CONTENT_SERVICE_NAME = "PageService";
137     private static final String PROPERTY_COLUMN_MAX = "nb.columns";
138     private static final int DEFAULT_COLUMN_MAX = 5;
139     private static final int DEFAULT_PORTLET_ORDER_MAX = 15;
140     private static final String KEY_THEME = "theme";
141     private static final String TARGET_TOP = "target='_top'";
142     private static final String WELCOME_PAGE_ID = "1";
143     private static final String WELCOME_PAGE_CACHE_KEY = "mode0";
144     private static final int MODE_ADMIN = 1;
145     private static final String VALUE_TRUE = "1";
146     private static final String VALUE_FALSE = "0";
147     private static final String XSL_UNIQUE_PREFIX = "page-";
148     private static final String ATTRIBUTE_CORE_CAN_PAGE_BE_CACHED = "core.canPageBeCached";
149     private static final String DEFAULT_OPEN_TAG_PREFIX = "<div class=\"lutece-admin-portlet\" draggable=\"true\">";
150     private static final String DEFAULT_CLOSE_TAG_PREFIX = "</div>";
151     private static final String ADMIN_PORTLET_OPEN_TAG = AppPropertiesService.getProperty( "lutece.portlet.open.tag", DEFAULT_OPEN_TAG_PREFIX );
152     private static final String ADMIN_PORTLET_CLOSE_TAG = AppPropertiesService.getProperty( "lutece.portlet.close.tag", DEFAULT_CLOSE_TAG_PREFIX );
153     private static final int PORTLET_MAX_ORDER = AppPropertiesService.getPropertyInt( "lutece.list.order.max", DEFAULT_PORTLET_ORDER_MAX );
154 
155     // Specific for plugin-document
156     private static final String DOCUMENT_LIST_PORTLET = "DOCUMENT_LIST_PORTLET";
157     private static final String DOCUMENT_PORTLET = "DOCUMENT_PORTLET";
158     private static final String DOCUMENT_ACTION_URL = "jsp/admin/plugins/document/ManagePublishing.jsp";
159     private static final String DOCUMENT_IMAGE_URL = "images/admin/skin/actions/publish.png";
160     private static final String DOCUMENT_TITLE = "portal.site.portletPreview.buttonManage";
161     private static final int MAX_COLUMNS = AppPropertiesService.getPropertyInt( PROPERTY_COLUMN_MAX, DEFAULT_COLUMN_MAX );
162     private static List<PageEventListener> _listEventListeners = new ArrayList<>( );
163     private ICacheKeyService _cksPage;
164     private ICacheKeyService _cksPortlet;
165     private PageCacheService _cachePages;
166     private PortletCacheService _cachePortlets;
167 
168     /**
169      * Creates a new PageService object.
170      * 
171      * @param pageCacheService
172      *            the page cache service
173      * @param portletCacheService
174      *            the portlet cache service
175      */
176     @Inject
177     public PageService( PageCacheService pageCacheService, PortletCacheService portletCacheService )
178     {
179         _cachePages = pageCacheService;
180         _cachePortlets = portletCacheService;
181         init( );
182     }
183 
184     /**
185      * Initializes the service
186      */
187     private void init( )
188     {
189         _cachePages.initCache( );
190         _cachePortlets.initCache( );
191         ImageResourceManager.registerProvider( this );
192         addPageEventListener( this );
193     }
194 
195     /**
196      * Returns the Content Service name
197      *
198      * @return The name as a String
199      */
200     public String getName( )
201     {
202         return CONTENT_SERVICE_NAME;
203     }
204 
205     /**
206      * Returns the page for a given ID. The page is built using XML data of each portlet or retrieved from the cache if it's enable.
207      *
208      * @param request
209      *            The page ID
210      * @param nMode
211      *            The current mode.
212      * @return The HTML code of the page as a String.
213      * @throws SiteMessageException
214      *             If a message shouldbe displayed
215      */
216     @Override
217     public String getPage( HttpServletRequest request, int nMode ) throws SiteMessageException
218     {
219         String strPageId = request.getParameter( Parameters.PAGE_ID );
220 
221         return getPage( strPageId, nMode, request );
222     }
223 
224     /**
225      * Returns the page for a given ID. The page is built using XML data of each portlet or retrieved from the cache if it's enable.
226      *
227      * @param strIdPage
228      *            The page ID
229      * @param nMode
230      *            The current mode.
231      * @param request
232      *            The HttpRequest
233      * @return The HTML code of the page as a String.
234      * @throws SiteMessageException
235      *             occurs when a site message need to be displayed
236      */
237     @Override
238     public String getPage( String strIdPage, int nMode, HttpServletRequest request ) throws SiteMessageException
239     {
240         try
241         {
242             String strPage;
243 
244             // The cache is enable !
245             if ( _cachePages.isCacheEnable( ) )
246             {
247                 strPage = getCachedPage( strIdPage, nMode, request );
248 
249                 // redirection handling
250                 strPage = redirect( strPage );
251             }
252             else
253             {
254                 strPage = buildPageContent( strIdPage, nMode, request );
255             }
256 
257             return strPage;
258         }
259         catch( NumberFormatException nfe )
260         {
261             AppLogService.error( "PageService.getPage() : {}", nfe.getLocalizedMessage( ), nfe );
262 
263             throw new PageNotFoundException( );
264         }
265     }
266 
267     private String redirect( String strPage )
268     {
269         if ( strPage.startsWith( REDIRECTION_KEY ) )
270         {
271             strPage = strPage.replaceFirst( REDIRECTION_KEY, "" );
272 
273             try
274             {
275                 LocalVariables.getResponse( ).sendRedirect( strPage );
276             }
277             catch( IOException e )
278             {
279                 AppLogService.error( "Error on sendRedirect for {}", strPage );
280             }
281         }
282 
283         return strPage;
284     }
285 
286     private String getCachedPage( String strIdPage, int nMode, HttpServletRequest request ) throws SiteMessageException
287     {
288         // Get request paramaters and store them in a HashMap
289         Map<String, String> htParamRequest = readRequestParams( request, strIdPage );
290 
291         LuteceUser user = SecurityService.getInstance( ).getRegisteredUser( request );
292 
293         // we add the key in the memory key only if cache is enable
294         String strKey = getKey( htParamRequest, nMode, user );
295 
296         // get page from cache
297         String strPage = (String) _cachePages.getFromCache( strKey );
298 
299         if ( strPage == null )
300         {
301             // only one thread can evaluate the page
302             synchronized( strKey )
303             {
304                 // can be useful if an other thread had evaluate the
305                 // page
306                 strPage = (String) _cachePages.getFromCache( strKey );
307 
308                 // ignore checkstyle, this double verification is useful
309                 // when page cache has been created when thread is
310                 // blocked on synchronized
311                 if ( strPage == null )
312                 {
313                     boolean bCanBeCached = true;
314 
315                     AppLogService.debug( "Page generation {}", strKey );
316 
317                     RedirectionResponseWrapperionResponseWrapper.html#RedirectionResponseWrapper">RedirectionResponseWrapper response = new RedirectionResponseWrapper( LocalVariables.getResponse( ) );
318 
319                     LocalVariables.setLocal( LocalVariables.getConfig( ), LocalVariables.getRequest( ), response );
320                     request.setAttribute( ATTRIBUTE_CORE_CAN_PAGE_BE_CACHED, null );
321                     // The key is not in the cache, so we have to build
322                     // the page
323                     strPage = buildPageContent( strIdPage, nMode, request );
324 
325                     // We check if the page contains portlets that can not be cached.
326                     if ( Boolean.FALSE.equals( request.getAttribute( ATTRIBUTE_CORE_CAN_PAGE_BE_CACHED ) ) )
327                     {
328                         bCanBeCached = false;
329                     }
330 
331                     if ( response.getRedirectLocation( ) != null )
332                     {
333                         AppLogService.debug( "Redirection found {}", response.getRedirectLocation( ) );
334                         strPage = REDIRECTION_KEY + response.getRedirectLocation( );
335                     }
336 
337                     // Add the page to the cache if the page can be
338                     // cached
339                     if ( bCanBeCached && ( nMode != MODE_ADMIN ) )
340                     {
341                         _cachePages.putInCache( strKey, strPage );
342                     }
343                 }
344                 else
345                 {
346                     AppLogService.debug( "Page read from cache after synchronisation {}", strKey );
347                 }
348             }
349         }
350         else
351         {
352             AppLogService.debug( "Page read from cache {}", strKey );
353         }
354 
355         return strPage;
356     }
357 
358     private Map<String, String> readRequestParams( HttpServletRequest request, String strIdPage )
359     {
360         Enumeration<?> enumParam = request.getParameterNames( );
361         Map<String, String> htParamRequest = new HashMap<>( );
362 
363         while ( enumParam.hasMoreElements( ) )
364         {
365             String paramName = (String) enumParam.nextElement( );
366             htParamRequest.put( paramName, request.getParameter( paramName ) );
367         }
368 
369         if ( !htParamRequest.containsKey( Parameters.PAGE_ID ) )
370         {
371             htParamRequest.put( Parameters.PAGE_ID, strIdPage );
372         }
373 
374         if ( !htParamRequest.containsKey( Parameters.BASE_URL ) )
375         {
376             htParamRequest.put( Parameters.BASE_URL, AppPathService.getBaseUrl( request ) );
377         }
378 
379         String strUserTheme = ThemesService.getUserTheme( request );
380 
381         if ( strUserTheme != null )
382         {
383             htParamRequest.put( KEY_THEME, strUserTheme );
384         }
385         return htParamRequest;
386     }
387 
388     /**
389      * Build the page content.
390      *
391      * @param strIdPage
392      *            The page ID
393      * @param nMode
394      *            The current mode.
395      * @param request
396      *            The HttpRequest
397      * @return The HTML code of the page as a String.
398      * @throws SiteMessageException
399      *             occurs when a site message need to be displayed
400      */
401     public String buildPageContent( String strIdPage, int nMode, HttpServletRequest request ) throws SiteMessageException
402     {
403         int nIdPage;
404         Page page;
405 
406         nIdPage = Integer.parseInt( strIdPage );
407 
408         boolean bPageExist = PageHome.checkPageExist( nIdPage );
409 
410         if ( bPageExist )
411         {
412             page = PageHome.getPage( nIdPage );
413         }
414         else
415         {
416             // If there is a problem finding the page, returns the home page
417             nIdPage = PortalService.getRootPageId( );
418             page = PageHome.getPage( nIdPage );
419         }
420 
421         PageData/service/content/PageData.html#PageData">PageData data = new PageData( );
422         data.setName( page.getName( ) );
423         data.setPagePath( PortalService.getPagePathContent( nIdPage, nMode, request ) );
424         data.setTheme( page.getCodeTheme( ) );
425         data.setMetaKeywords( page.getMetaKeywords( ) );
426         data.setMetaDescription( page.getMetaDescription( ) );
427         data.setDisplayDateUpdate( page.getDisplayDateUpdate( ) );
428         data.setDateUpdate( page.getDateUpdate( ) );
429 
430         // Checks the page role (v1.1)
431         String strRole = page.getRole( );
432 
433         if ( !strRole.equals( Page.ROLE_NONE ) && ( SecurityService.isAuthenticationEnable( ) ) && ( nMode != MODE_ADMIN ) )
434         {
435             LuteceUser user = SecurityService.getInstance( ).getRegisteredUser( request );
436 
437             if ( ( user == null ) && ( !SecurityService.getInstance( ).isExternalAuthentication( ) ) )
438             {
439                 // The user is not registered and identify itself with the
440                 // Portal authentication
441                 String strAccessControledTemplate = SecurityService.getInstance( ).getAccessControledTemplate( );
442                 HashMap<String, Object> model = new HashMap<>( );
443                 String strLoginUrl = SecurityService.getInstance( ).getLoginPageUrl( );
444                 model.put( MARK_URL_LOGIN, strLoginUrl );
445 
446                 HtmlTemplate tAccessControled = AppTemplateService.getTemplate( strAccessControledTemplate, request.getLocale( ), model );
447 
448                 data.setContent( tAccessControled.getHtml( ) );
449 
450                 return PortalService.buildPageContent( nIdPage, data, nMode, request );
451             }
452 
453             if ( !SecurityService.getInstance( ).isUserInRole( request, strRole ) )
454             {
455                 // The user doesn't have the correct role
456                 String strAccessDeniedTemplate = SecurityService.getInstance( ).getAccessDeniedTemplate( );
457                 HtmlTemplate tAccessDenied = AppTemplateService.getTemplate( strAccessDeniedTemplate, request.getLocale( ) );
458                 data.setContent( tAccessDenied.getHtml( ) );
459 
460                 return PortalService.buildPageContent( nIdPage, data, nMode, request );
461             }
462         }
463 
464         // Added in v2.0
465         // Add the page authorization
466         if ( nMode == MODE_ADMIN )
467         {
468             AdminUser user = AdminUserService.getAdminUser( request );
469 
470             if ( isAuthorizedAdminPage( nIdPage, PageResourceIdService.PERMISSION_VIEW, user ) )
471             {
472                 // Fill a PageData structure for those elements
473                 data.setContent( getPageContent( nIdPage, nMode, request ) );
474             }
475             else
476             {
477                 data.setContent( I18nService.getLocalizedString( PROPERTY_MESSAGE_PAGE_ACCESS_DENIED, user.getLocale( ) ) );
478             }
479         }
480         else
481         {
482             data.setContent( getPageContent( nIdPage, nMode, request ) );
483         }
484 
485         if ( nIdPage == PortalService.getRootPageId( ) )
486         {
487             // This page is the home page.
488             data.setHomePage( true );
489         }
490 
491         return PortalService.buildPageContent( nIdPage, data, nMode, request );
492     }
493 
494     /**
495      * Build the page content.
496      *
497      * @param nIdPage
498      *            The page ID
499      * @param nMode
500      *            The current mode.
501      * @param request
502      *            The HttpRequest
503      * @return The HTML code of the page as a String.
504      * @throws SiteMessageException
505      *             occurs when a site message need to be displayed
506      */
507     @Override
508     public String getPageContent( int nIdPage, int nMode, HttpServletRequest request ) throws SiteMessageException
509     {
510         Locale locale = Optional.ofNullable( request ).map( HttpServletRequest::getLocale ).orElse( LocaleService.getDefault( ) );
511 
512         String [ ] arrayContent = new String [ MAX_COLUMNS];
513 
514         for ( int i = 0; i < MAX_COLUMNS; i++ )
515         {
516             arrayContent [i] = "";
517         }
518 
519         Page page = PageHome.findByPrimaryKey( nIdPage );
520         Map<String, String> mapParams = getParams( request, nMode );
521         boolean bCanPageBeCached = Boolean.TRUE;
522         LuteceUser user = SecurityService.getInstance( ).getRegisteredUser( request );
523 
524         for ( Portlet portlet : page.getPortlets( ) )
525         {
526             int nCol = portlet.getColumn( ) - 1;
527 
528             if ( nCol < MAX_COLUMNS )
529             {
530                 arrayContent [nCol] += getPortletContent( request, portlet, mapParams, nMode );
531             }
532 
533             // We check if the portlet can be cached
534             if ( ( user != null ) ? ( !portlet.canBeCachedForConnectedUsers( ) ) : ( !portlet.canBeCachedForAnonymousUsers( ) ) )
535             {
536                 bCanPageBeCached = false;
537             }
538         }
539 
540         // Add columns outline in admin mode
541         if ( nMode == MODE_ADMIN )
542         {
543             for ( int i = 0; i < MAX_COLUMNS; i++ )
544             {
545                 arrayContent [i] = addColumnOutline( i + 1, arrayContent [i], locale );
546             }
547         }
548 
549         // We save that the page that is generating can not be cached
550         if ( !bCanPageBeCached && request != null )
551         {
552             request.setAttribute( ATTRIBUTE_CORE_CAN_PAGE_BE_CACHED, false );
553         }
554 
555         Map<String, Object> rootModel = new HashMap<>( );
556 
557         for ( int j = 0; j < MAX_COLUMNS; j++ )
558         {
559             rootModel.put( "page_content_col" + ( j + 1 ), arrayContent [j] );
560         }
561 
562         List<PageInclude> listIncludes = PageIncludeService.getIncludes( );
563         PageData/service/content/PageData.html#PageData">PageData data = new PageData( );
564 
565         for ( PageInclude pic : listIncludes )
566         {
567             pic.fillTemplate( rootModel, data, nMode, request );
568         }
569 
570         HtmlTemplate t = AppTemplateService.getTemplate( page.getTemplate( ), locale, rootModel );
571 
572         return t.getHtml( );
573     }
574 
575     /**
576      * Add the HTML code to display column outlines
577      *
578      * @param columnId
579      *            the column id
580      * @param content
581      *            the column content
582      * @param locale
583      *            the locale
584      * @return The column code
585      */
586     private String addColumnOutline( int columnId, String content, Locale locale )
587     {
588         Map<String, Object> model = new HashMap<>( 2 );
589         model.put( MARK_COLUMN_CONTENT, content );
590         model.put( MARK_COLUMN_ID, columnId );
591 
592         HtmlTemplate template = AppTemplateService.getTemplate( TEMPLATE_COLUMN_OUTLINE, locale, model );
593 
594         return template.getHtml( );
595     }
596 
597     /**
598      * Get the portlet content
599      *
600      * @param request
601      *            The HTTP request
602      * @param portlet
603      *            The portlet
604      * @param mapRequestParams
605      *            request parameters
606      * @param nMode
607      *            The mode
608      * @return The content
609      * @throws SiteMessageException
610      *             If an error occurs
611      */
612     private String getPortletContent( HttpServletRequest request, Portlet portlet, Map<String, String> mapRequestParams, int nMode ) throws SiteMessageException
613     {
614         if ( ( request != null ) && !isPortletVisible( request, portlet, nMode ) )
615         {
616             return StringUtils.EMPTY;
617         }
618 
619         if ( request != null )
620         {
621             String strPluginName = portlet.getPluginName( );
622             request.setAttribute( PARAMETER_PLUGIN_NAME, strPluginName );
623         }
624 
625         String strPortletContent = StringUtils.EMPTY;
626 
627         // Add the admin buttons for portlet management on admin mode
628         if ( nMode == MODE_ADMIN )
629         {
630             strPortletContent = ADMIN_PORTLET_OPEN_TAG + addAdminButtons( request, portlet );
631         }
632 
633         String strKey = StringUtils.EMPTY;
634 
635         LuteceUser user = null;
636 
637         if ( SecurityService.isAuthenticationEnable( ) )
638         {
639             user = SecurityService.getInstance( ).getRegisteredUser( request );
640         }
641 
642         boolean isCacheEnabled = nMode != MODE_ADMIN && _cachePortlets.isCacheEnable( );
643         boolean bCanBeCached = user != null ? portlet.canBeCachedForConnectedUsers( ) : portlet.canBeCachedForAnonymousUsers( );
644 
645         if ( portlet.isContentGeneratedByXmlAndXsl( ) )
646         {
647             Map<String, String> mapParams = mapRequestParams;
648             Map<String, String> mapXslParams = portlet.getXslParams( );
649 
650             if ( mapParams != null )
651             {
652                 if ( mapXslParams != null )
653                 {
654                     mapParams.putAll( mapXslParams );
655                 }
656             }
657             else
658             {
659                 mapParams = mapXslParams;
660             }
661 
662             if ( isCacheEnabled && bCanBeCached )
663             {
664                 mapParams.put( PARAMETER_PORTLET, String.valueOf( portlet.getId( ) ) );
665                 strKey = _cksPortlet.getKey( mapParams, nMode, user );
666 
667                 String strPortlet = (String) _cachePortlets.getFromCache( strKey );
668 
669                 if ( strPortlet != null )
670                 {
671                     return strPortlet;
672                 }
673             }
674 
675             Properties outputProperties = ModeHome.getOuputXslProperties( nMode );
676             String strXslUniqueId = XSL_UNIQUE_PREFIX + String.valueOf( portlet.getStyleId( ) );
677             XmlTransformerServicervice.html#XmlTransformerService">XmlTransformerService xmlTransformerService = new XmlTransformerService( );
678             String strPortletXmlContent = portlet.getXml( request );
679             strPortletContent += xmlTransformerService.transformBySourceWithXslCache( strPortletXmlContent, portlet.getXslSource( nMode ), strXslUniqueId, mapParams, outputProperties );
680         }
681         else
682         {
683             if ( isCacheEnabled && bCanBeCached )
684             {
685                 mapRequestParams.put( PARAMETER_PORTLET, String.valueOf( portlet.getId( ) ) );
686                 strKey = _cksPortlet.getKey( mapRequestParams, nMode, user );
687 
688                 String strPortlet = (String) _cachePortlets.getFromCache( strKey );
689 
690                 if ( strPortlet != null )
691                 {
692                     return strPortlet;
693                 }
694             }
695 
696             strPortletContent += portlet.getHtmlContent( request );
697         }
698 
699         if ( isCacheEnabled && StringUtils.isNotEmpty( strKey ) )
700         {
701             _cachePortlets.putInCache( strKey, strPortletContent );
702         }
703         
704         if ( nMode == MODE_ADMIN )
705         {
706             strPortletContent += ADMIN_PORTLET_CLOSE_TAG;
707         }
708         
709         return strPortletContent;
710     }
711 
712     private boolean isPortletVisible( HttpServletRequest request, Portlet portlet, int nMode )
713     {
714         if ( ( nMode != MODE_ADMIN ) && ( portlet.getStatus( ) == Portlet.STATUS_UNPUBLISHED ) )
715         {
716             return false;
717         }
718 
719         String strRole = portlet.getRole( );
720         boolean bUserInRole = SecurityService.isAuthenticationEnable( ) ? SecurityService.getInstance( ).isUserInRole( request, strRole ) : true;
721 
722         boolean [ ] conditions = new boolean [ ] {
723                 strRole.equals( Page.ROLE_NONE ), // No role is required so the portlet is visible for anyone
724                 !SecurityService.isAuthenticationEnable( ), // No authentication
725                 nMode == MODE_ADMIN, // We are in Admin mode, so all the portlet should be visible
726                 bUserInRole // The authentication is ON and the user get the role
727         };
728 
729         return BooleanUtils.or( conditions );
730     }
731 
732     /**
733      * Build the Cache HashMap key for pages Goal is to be able to have a synchronized on the key but a synchronize only work with strict reference. So we
734      * manage an hashmap of string reference for cache keys to be able to get them back.
735      *
736      * @param mapParams
737      *            The Map params
738      * @param nMode
739      *            The current mode.
740      * @param user
741      *            Current Lutece user
742      * @return The HashMap key for articles pages as a String.
743      */
744     private String getKey( Map<String, String> mapParams, int nMode, LuteceUser user )
745     {
746         String strKey = _cksPage.getKey( mapParams, nMode, user );
747 
748         return _cachePages.getKey( strKey );
749     }
750 
751     /**
752      * Remove a page from the cache
753      *
754      * @param nIdPage
755      *            The page ID
756      */
757     private void invalidatePage( int nIdPage )
758     {
759         String strIdPage = String.valueOf( nIdPage );
760         invalidatePage( strIdPage );
761     }
762 
763     /**
764      * @param cacheKeyService
765      *            the _cacheKeyService to set
766      */
767     public void setPageCacheKeyService( ICacheKeyService cacheKeyService )
768     {
769         _cksPage = cacheKeyService;
770     }
771 
772     /**
773      * @param cacheKeyService
774      *            the _cacheKeyService to set
775      */
776     public void setPortletCacheKeyService( ICacheKeyService cacheKeyService )
777     {
778         _cksPortlet = cacheKeyService;
779     }
780 
781     /**
782      * @param removalService
783      *            the removal listener service
784      */
785     public void setRoleRemovalService( RemovalListenerService removalService )
786     {
787         removalService.registerListener( new PageRoleRemovalListener( ) );
788         removalService.registerListener( new PortletRoleRemovalListener( ) );
789     }
790 
791     /**
792      * Remove a page from the cache
793      *
794      * @param strIdPage
795      *            The page ID
796      */
797     private void invalidatePage( String strIdPage )
798     {
799         if ( _cachePages.isCacheEnable( ) )
800         {
801             String strKey = "[" + Parameters.PAGE_ID + ":" + strIdPage + "]";
802 
803             for ( String strKeyTemp : (List<String>) _cachePages.getCache( ).getKeys( ) )
804             {
805                 if ( ( strKeyTemp.contains( strKey ) ) || ( WELCOME_PAGE_ID.equals( strIdPage ) && WELCOME_PAGE_CACHE_KEY.equals( strKeyTemp ) ) )
806                 {
807                     _cachePages.getCache( ).remove( strKeyTemp );
808 
809                     if ( AppLogService.isDebugEnabled( ) )
810                     {
811                         AppLogService.debug( "Page (cache key : " + strKeyTemp + ") removed from the cache." );
812                     }
813                 }
814             }
815         }
816     }
817 
818     // ///////////////////////////////////////////////////////////////////////////
819     // Events Listeners management
820     /**
821      * Add a new page event listener
822      *
823      * @param listener
824      *            An event listener to add
825      */
826     public static void addPageEventListener( PageEventListener listener )
827     {
828         _listEventListeners.add( listener );
829         AppLogService.info( "New Page Event Listener registered : {}", listener.getClass( ).getName( ) );
830     }
831 
832     /**
833      * Notify an event to all listeners
834      *
835      * @param event
836      *            A page Event
837      */
838     private void notifyListeners( PageEvent event )
839     {
840         for ( PageEventListener listener : _listEventListeners )
841         {
842             listener.processPageEvent( event );
843         }
844     }
845 
846     /**
847      * Returns the resource type Id
848      *
849      * @return The resource type Id
850      */
851     @Override
852     public String getResourceTypeId( )
853     {
854         return Page.IMAGE_RESOURCE_TYPE_ID;
855     }
856 
857     /**
858      * Gets the image resource for a given resource
859      *
860      * @param nIdResource
861      *            The Resource id
862      * @return The image resource
863      */
864     @Override
865     public ImageResource getImageResource( int nIdResource )
866     {
867         return PageHome.getImageResource( nIdResource );
868     }
869 
870     /**
871      * Create a page
872      *
873      * @param page
874      *            The page to create
875      */
876     @Override
877     public void createPage( Page page )
878     {
879         PageHome.create( page );
880 
881         PageEventervice/page/PageEvent.html#PageEvent">PageEvent event = new PageEvent( page, PageEvent.PAGE_CREATED );
882         notifyListeners( event );
883     }
884 
885     /**
886      * Update a given page
887      *
888      * @param page
889      *            The page to update
890      */
891     @Override
892     public void updatePage( Page page )
893     {
894         PageHome.update( page );
895 
896         PageEventervice/page/PageEvent.html#PageEvent">PageEvent event = new PageEvent( page, PageEvent.PAGE_CONTENT_MODIFIED );
897         notifyListeners( event );
898     }
899 
900     /**
901      * Remove a given page
902      *
903      * @param nPageId
904      *            The page Id
905      */
906     @Override
907     public void removePage( int nPageId )
908     {
909         Page page = PageHome.findByPrimaryKey( nPageId );
910         PageEventervice/page/PageEvent.html#PageEvent">PageEvent event = new PageEvent( page, PageEvent.PAGE_DELETED );
911         PageHome.remove( nPageId );
912         notifyListeners( event );
913     }
914 
915     /**
916      * Process a page event
917      *
918      * @param event
919      *            The event to process
920      */
921     @Override
922     public void processPageEvent( PageEvent event )
923     {
924         Page page = event.getPage( );
925         invalidatePage( page.getId( ) );
926     }
927 
928     /**
929      * {@inheritDoc}
930      */
931     @Override
932     public void processPortletEvent( PortletEvent event )
933     {
934         invalidateContent( event.getPageId( ) );
935     }
936 
937     /**
938      * Invalidate Page Content
939      *
940      * @param nPageId
941      *            The Page ID
942      */
943     @Override
944     public void invalidateContent( int nPageId )
945     {
946         Page page = PageHome.findByPrimaryKey( nPageId );
947         PageEventervice/page/PageEvent.html#PageEvent">PageEvent event = new PageEvent( page, PageEvent.PAGE_CONTENT_MODIFIED );
948         notifyListeners( event );
949     }
950 
951     /**
952      * Check that a given user is allowed to access a page for a given permission
953      *
954      * @param nIdPage
955      *            the id of the page to check
956      * @param strPermission
957      *            the permission needed
958      * @param user
959      *            The current user
960      * @return true if authorized, otherwise false
961      */
962     @Override
963     public boolean isAuthorizedAdminPage( int nIdPage, String strPermission, AdminUser user )
964     {
965         Page page = PageHome.findByPrimaryKey( nIdPage );
966 
967         if ( page.getIdAuthorizationNode( ) != null )
968         {
969             String strAuthorizationNode = Integer.toString( page.getIdAuthorizationNode( ) );
970 
971             return ( RBACService.isAuthorized( Page.RESOURCE_TYPE, strAuthorizationNode, strPermission, user ) );
972         }
973 
974         return true;
975     }
976 
977     /**
978      * Add the HTML code to display admin buttons under each portlet
979      *
980      * @param request
981      *            The Http request
982      * @param portlet
983      *            The portlet
984      * @return The buttons code
985      */
986     private String addAdminButtons( HttpServletRequest request, Portlet portlet )
987     {
988         AdminUser user = AdminUserService.getAdminUser( request );
989 
990         if ( RBACService.isAuthorized( PortletType.RESOURCE_TYPE, portlet.getPortletTypeId( ), PortletResourceIdService.PERMISSION_MANAGE, user ) )
991         {
992             Locale locale = user.getLocale( );
993             Collection<PortletCustomAdminAction> listCustomActions = new ArrayList<>( );
994 
995             // TODO : listCustomActions should be provided by PortletType
996             // FIXME : Delete plugin-document specifics
997             if ( portlet.getPortletTypeId( ).equals( DOCUMENT_LIST_PORTLET ) || portlet.getPortletTypeId( ).equals( DOCUMENT_PORTLET ) )
998             {
999                 PortletCustomAdminActiontomAdminAction.html#PortletCustomAdminAction">PortletCustomAdminAction customAction = new PortletCustomAdminAction( );
1000                 customAction.setActionUrl( DOCUMENT_ACTION_URL );
1001                 customAction.setImageUrl( DOCUMENT_IMAGE_URL );
1002                 customAction.setTitle( DOCUMENT_TITLE );
1003                 listCustomActions.add( customAction );
1004             }
1005 
1006             Map<String, Object> model = new HashMap<>( );
1007             model.put( MARK_PORTLET, portlet );
1008             model.put( MARK_STATUS_PUBLISHED, Portlet.STATUS_PUBLISHED );
1009             model.put( MARK_MAX_ORDER, PORTLET_MAX_ORDER );
1010             model.put( MARK_STATUS_UNPUBLISHED, Portlet.STATUS_UNPUBLISHED );
1011             model.put( MARK_CUSTOM_ACTIONS, listCustomActions );
1012 
1013             HtmlTemplate template = AppTemplateService.getTemplate( TEMPLATE_ADMIN_BUTTONS, locale, model );
1014 
1015             return template.getHtml( );
1016         }
1017 
1018         return StringUtils.EMPTY;
1019     }
1020 
1021     /**
1022      * Gets the params map
1023      *
1024      * @param request
1025      *            The HTTP request
1026      * @param nMode
1027      *            The mode
1028      * @return the map
1029      */
1030     private Map<String, String> getParams( HttpServletRequest request, int nMode )
1031     {
1032         Map<String, String> mapModifyParam = new HashMap<>( );
1033         String strParamName;
1034 
1035         // Get request paramaters and store them in a HashMap
1036         if ( request != null )
1037         {
1038             Enumeration<?> enumParam = request.getParameterNames( );
1039 
1040             while ( enumParam.hasMoreElements( ) )
1041             {
1042                 strParamName = (String) enumParam.nextElement( );
1043                 mapModifyParam.put( strParamName, request.getParameter( strParamName ) );
1044             }
1045 
1046             // Add selected locale
1047             mapModifyParam.put( PARAMETER_USER_SELECTED_LOCALE, LocaleService.getUserSelectedLocale( request ).getLanguage( ) );
1048         }
1049 
1050         // Added in v1.3
1051         // Add a path param for choose url to use in admin or normal mode
1052         if ( nMode != MODE_ADMIN )
1053         {
1054             mapModifyParam.put( PARAMETER_SITE_PATH, AppPathService.getPortalUrl( ) );
1055 
1056             if ( SecurityService.isAuthenticationEnable( ) )
1057             {
1058                 mapModifyParam.put( MARKER_IS_USER_AUTHENTICATED,
1059                         ( SecurityService.getInstance( ).getRegisteredUser( request ) != null ) ? VALUE_TRUE : VALUE_FALSE );
1060             }
1061         }
1062         else
1063         {
1064             mapModifyParam.put( PARAMETER_SITE_PATH, AppPathService.getAdminPortalUrl( ) );
1065             mapModifyParam.put( MARKER_TARGET, TARGET_TOP );
1066         }
1067 
1068         if ( !mapModifyParam.containsKey( Parameters.PAGE_ID ) )
1069         {
1070             mapModifyParam.put( Parameters.PAGE_ID, Integer.toString( PortalService.getRootPageId( ) ) );
1071         }
1072 
1073         return mapModifyParam;
1074     }
1075 
1076     /**
1077      * Management of the image associated to the page
1078      *
1079      * @param strPageId
1080      *            The page identifier
1081      * @return The url
1082      */
1083     public String getResourceImagePage( String strPageId )
1084     {
1085         String strResourceType = getResourceTypeId( );
1086         UrlItem/url/UrlItem.html#UrlItem">UrlItem url = new UrlItem( Parameters.IMAGE_SERVLET );
1087         url.addParameter( Parameters.RESOURCE_TYPE, strResourceType );
1088         url.addParameter( Parameters.RESOURCE_ID, strPageId );
1089 
1090         return url.getUrlWithEntity( );
1091     }
1092 
1093     /**
1094      * Gets the page cache service.
1095      * 
1096      * @return the page cache service
1097      */
1098     public PageCacheService getPageCacheService( )
1099     {
1100         return _cachePages;
1101     }
1102 
1103     /**
1104      * Sets the cache page service
1105      * 
1106      * @param pageCacheService
1107      *            the page cache service
1108      */
1109     public void setPageCacheService( PageCacheService pageCacheService )
1110     {
1111         _cachePages = pageCacheService;
1112     }
1113 
1114     /**
1115      * Gets the portlet cache service
1116      * 
1117      * @return the porlet cache service
1118      */
1119     public PortletCacheService getPortletCacheService( )
1120     {
1121         return _cachePortlets;
1122     }
1123 
1124     /**
1125      * Gets the portlet cache service
1126      * 
1127      * @param portletCacheService
1128      *            the portlet cache service
1129      */
1130     public void setPortletCacheService( PortletCacheService portletCacheService )
1131     {
1132         _cachePortlets = portletCacheService;
1133     }
1134 
1135     /**
1136      * update authorization node of children page
1137      * 
1138      * @param nIdParentPage
1139      *            id of the parent page
1140      * @param nIdNewAuthorizationNode
1141      *            the new authorization id
1142      */
1143     public static void updateChildrenAuthorizationNode( int nIdParentPage, Integer nIdNewAuthorizationNode )
1144     {
1145         List<Integer> listPagesChildren = PageHome.getPagesWhichMustChangeAuthorizationNode( nIdParentPage );
1146 
1147         if ( listPagesChildren != null )
1148         {
1149             for ( Integer idPage : listPagesChildren )
1150             {
1151                 PageHome.updateAuthorizationNode( idPage, nIdNewAuthorizationNode );
1152                 updateChildrenAuthorizationNode( idPage, nIdNewAuthorizationNode );
1153             }
1154         }
1155     }
1156 
1157 }