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