PageService.java

  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. import java.io.IOException;
  36. import java.util.ArrayList;
  37. import java.util.Collection;
  38. import java.util.Enumeration;
  39. import java.util.HashMap;
  40. import java.util.List;
  41. import java.util.Locale;
  42. import java.util.Map;
  43. import java.util.Optional;
  44. import java.util.Properties;

  45. import javax.inject.Inject;
  46. import javax.servlet.http.HttpServletRequest;

  47. import org.apache.commons.lang3.StringUtils;
  48. import org.apache.commons.fileupload.FileItem;
  49. import org.apache.commons.lang3.BooleanUtils;

  50. import fr.paris.lutece.portal.business.page.Page;
  51. import fr.paris.lutece.portal.business.page.PageHome;
  52. import fr.paris.lutece.portal.business.page.PageRoleRemovalListener;
  53. import fr.paris.lutece.portal.business.portlet.Portlet;
  54. import fr.paris.lutece.portal.business.portlet.PortletRoleRemovalListener;
  55. import fr.paris.lutece.portal.business.portlet.PortletType;
  56. import fr.paris.lutece.portal.business.style.ModeHome;
  57. import fr.paris.lutece.portal.business.user.AdminUser;
  58. import fr.paris.lutece.portal.service.admin.AdminUserService;
  59. import fr.paris.lutece.portal.service.cache.ICacheKeyService;
  60. import fr.paris.lutece.portal.service.content.PageData;
  61. import fr.paris.lutece.portal.service.html.XmlTransformerService;
  62. import fr.paris.lutece.portal.service.i18n.I18nService;
  63. import fr.paris.lutece.portal.service.image.ImageResource;
  64. import fr.paris.lutece.portal.service.image.ImageResourceManager;
  65. import fr.paris.lutece.portal.service.image.ImageResourceProvider;
  66. import fr.paris.lutece.portal.service.includes.PageInclude;
  67. import fr.paris.lutece.portal.service.includes.PageIncludeService;
  68. import fr.paris.lutece.portal.service.message.SiteMessageException;
  69. import fr.paris.lutece.portal.service.portal.PortalService;
  70. import fr.paris.lutece.portal.service.portal.ThemesService;
  71. import fr.paris.lutece.portal.service.portlet.PortletEvent;
  72. import fr.paris.lutece.portal.service.portlet.PortletEventListener;
  73. import fr.paris.lutece.portal.service.portlet.PortletResourceIdService;
  74. import fr.paris.lutece.portal.service.rbac.RBACService;
  75. import fr.paris.lutece.portal.service.security.LuteceUser;
  76. import fr.paris.lutece.portal.service.security.SecurityService;
  77. import fr.paris.lutece.portal.service.template.AppTemplateService;
  78. import fr.paris.lutece.portal.service.util.AppException;
  79. import fr.paris.lutece.portal.service.util.AppLogService;
  80. import fr.paris.lutece.portal.service.util.AppPathService;
  81. import fr.paris.lutece.portal.service.util.AppPropertiesService;
  82. import fr.paris.lutece.portal.service.util.RemovalListenerService;
  83. import fr.paris.lutece.portal.web.LocalVariables;
  84. import fr.paris.lutece.portal.web.constants.Parameters;
  85. import fr.paris.lutece.portal.web.l10n.LocaleService;
  86. import fr.paris.lutece.util.html.HtmlTemplate;
  87. import fr.paris.lutece.util.url.UrlItem;

  88. /**
  89.  * 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
  90.  * tranformations.
  91.  */
  92. public class PageService implements IPageService, ImageResourceProvider, PageEventListener, PortletEventListener
  93. {
  94.     // //////////////////////////////////////////////////////////////////////////
  95.     // Variables

  96.     /**
  97.      * Key for redirections
  98.      */
  99.     public static final String REDIRECTION_KEY = "redirect:";

  100.     // Templates
  101.     /** Access denied template */
  102.     public static final String TEMPLATE_PAGE_ACCESS_DENIED = "/skin/site/page_access_denied.html";

  103.     /** Access Controlled template */
  104.     public static final String TEMPLATE_PAGE_ACCESS_CONTROLED = "/skin/site/page_access_controled.html";
  105.     private static final String TEMPLATE_ADMIN_BUTTONS = "/admin/admin_buttons.html";
  106.     private static final String TEMPLATE_COLUMN_OUTLINE = "/admin/column_outline.html";

  107.     // Markers
  108.     private static final String MARK_PORTLET = "portlet";
  109.     private static final String MARK_STATUS_PUBLISHED = "portlet_status_published";
  110.     private static final String MARK_STATUS_UNPUBLISHED = "portlet_status_unpublished";
  111.     private static final String MARK_CUSTOM_ACTIONS = "custom_action_list";
  112.     private static final String MARK_URL_LOGIN = "url_login";
  113.     private static final String MARKER_TARGET = "target";
  114.     private static final String MARKER_IS_USER_AUTHENTICATED = "is-user-authenticated";
  115.     private static final String MARK_COLUMN_CONTENT = "column_content";
  116.     private static final String MARK_COLUMN_ID = "column_id";
  117.     private static final String MARK_MAX_ORDER = "order_max";

  118.     // Parameters
  119.     private static final String PARAMETER_SITE_PATH = "site-path";
  120.     private static final String PARAMETER_USER_SELECTED_LOCALE = "user-selected-language";
  121.     private static final String PARAMETER_PLUGIN_NAME = "plugin-name";
  122.     private static final String PARAMETER_PORTLET = "portlet";

  123.     // Properties
  124.     private static final String PROPERTY_MESSAGE_PAGE_ACCESS_DENIED = "portal.site.message.pageAccessDenied";
  125.     private static final String CONTENT_SERVICE_NAME = "PageService";
  126.     private static final String PROPERTY_COLUMN_MAX = "nb.columns";
  127.     private static final int DEFAULT_COLUMN_MAX = 5;
  128.     private static final int DEFAULT_PORTLET_ORDER_MAX = 15;
  129.     private static final String KEY_THEME = "theme";
  130.     private static final String TARGET_TOP = "target='_top'";
  131.     private static final String WELCOME_PAGE_ID = "1";
  132.     private static final String WELCOME_PAGE_CACHE_KEY = "mode0";
  133.     private static final int MODE_ADMIN = 1;
  134.     private static final String VALUE_TRUE = "1";
  135.     private static final String VALUE_FALSE = "0";
  136.     private static final String XSL_UNIQUE_PREFIX = "page-";
  137.     private static final String ATTRIBUTE_CORE_CAN_PAGE_BE_CACHED = "core.canPageBeCached";
  138.     private static final String DEFAULT_OPEN_TAG_PREFIX = "<div class=\"lutece-admin-portlet\" draggable=\"true\">";
  139.     private static final String DEFAULT_CLOSE_TAG_PREFIX = "</div>";
  140.     private static final String ADMIN_PORTLET_OPEN_TAG = AppPropertiesService.getProperty( "lutece.portlet.open.tag", DEFAULT_OPEN_TAG_PREFIX );
  141.     private static final String ADMIN_PORTLET_CLOSE_TAG = AppPropertiesService.getProperty( "lutece.portlet.close.tag", DEFAULT_CLOSE_TAG_PREFIX );
  142.     private static final int PORTLET_MAX_ORDER = AppPropertiesService.getPropertyInt( "lutece.list.order.max", DEFAULT_PORTLET_ORDER_MAX );

  143.     // Specific for plugin-document
  144.     private static final String DOCUMENT_LIST_PORTLET = "DOCUMENT_LIST_PORTLET";
  145.     private static final String DOCUMENT_PORTLET = "DOCUMENT_PORTLET";
  146.     private static final String DOCUMENT_ACTION_URL = "jsp/admin/plugins/document/ManagePublishing.jsp";
  147.     private static final String DOCUMENT_IMAGE_URL = "images/admin/skin/actions/publish.png";
  148.     private static final String DOCUMENT_TITLE = "portal.site.portletPreview.buttonManage";
  149.     private static final int MAX_COLUMNS = AppPropertiesService.getPropertyInt( PROPERTY_COLUMN_MAX, DEFAULT_COLUMN_MAX );
  150.     private static List<PageEventListener> _listEventListeners = new ArrayList<>( );
  151.     private ICacheKeyService _cksPage;
  152.     private ICacheKeyService _cksPortlet;
  153.     private PageCacheService _cachePages;
  154.     private PortletCacheService _cachePortlets;

  155.     /**
  156.      * Creates a new PageService object.
  157.      *
  158.      * @param pageCacheService
  159.      *            the page cache service
  160.      * @param portletCacheService
  161.      *            the portlet cache service
  162.      */
  163.     @Inject
  164.     public PageService( PageCacheService pageCacheService, PortletCacheService portletCacheService )
  165.     {
  166.         _cachePages = pageCacheService;
  167.         _cachePortlets = portletCacheService;
  168.         init( );
  169.     }

  170.     /**
  171.      * Initializes the service
  172.      */
  173.     private void init( )
  174.     {
  175.         _cachePages.initCache( );
  176.         _cachePortlets.initCache( );
  177.         ImageResourceManager.registerProvider( this );
  178.         addPageEventListener( this );
  179.     }

  180.     /**
  181.      * Returns the Content Service name
  182.      *
  183.      * @return The name as a String
  184.      */
  185.     public String getName( )
  186.     {
  187.         return CONTENT_SERVICE_NAME;
  188.     }

  189.     /**
  190.      * 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.
  191.      *
  192.      * @param request
  193.      *            The page ID
  194.      * @param nMode
  195.      *            The current mode.
  196.      * @return The HTML code of the page as a String.
  197.      * @throws SiteMessageException
  198.      *             If a message shouldbe displayed
  199.      */
  200.     @Override
  201.     public String getPage( HttpServletRequest request, int nMode ) throws SiteMessageException
  202.     {
  203.         String strPageId = request.getParameter( Parameters.PAGE_ID );

  204.         return getPage( strPageId, nMode, request );
  205.     }

  206.     /**
  207.      * 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.
  208.      *
  209.      * @param strIdPage
  210.      *            The page ID
  211.      * @param nMode
  212.      *            The current mode.
  213.      * @param request
  214.      *            The HttpRequest
  215.      * @return The HTML code of the page as a String.
  216.      * @throws SiteMessageException
  217.      *             occurs when a site message need to be displayed
  218.      */
  219.     @Override
  220.     public String getPage( String strIdPage, int nMode, HttpServletRequest request ) throws SiteMessageException
  221.     {
  222.         try
  223.         {
  224.             String strPage;

  225.             // The cache is enable !
  226.             if ( _cachePages.isCacheEnable( ) )
  227.             {
  228.                 strPage = getCachedPage( strIdPage, nMode, request );

  229.                 // redirection handling
  230.                 strPage = redirect( strPage );
  231.             }
  232.             else
  233.             {
  234.                 strPage = buildPageContent( strIdPage, nMode, request );
  235.             }

  236.             return strPage;
  237.         }
  238.         catch( NumberFormatException nfe )
  239.         {
  240.             AppLogService.error( "PageService.getPage() : {}", nfe.getLocalizedMessage( ), nfe );

  241.             throw new PageNotFoundException( );
  242.         }
  243.     }

  244.     private String redirect( String strPage )
  245.     {
  246.         if ( strPage.startsWith( REDIRECTION_KEY ) )
  247.         {
  248.             strPage = strPage.replaceFirst( REDIRECTION_KEY, "" );

  249.             try
  250.             {
  251.                 LocalVariables.getResponse( ).sendRedirect( strPage );
  252.             }
  253.             catch( IOException e )
  254.             {
  255.                 AppLogService.error( "Error on sendRedirect for {}", strPage );
  256.             }
  257.         }

  258.         return strPage;
  259.     }

  260.     private String getCachedPage( String strIdPage, int nMode, HttpServletRequest request ) throws SiteMessageException
  261.     {
  262.         // Get request paramaters and store them in a HashMap
  263.         Map<String, String> htParamRequest = readRequestParams( request, strIdPage );

  264.         LuteceUser user = SecurityService.getInstance( ).getRegisteredUser( request );

  265.         // we add the key in the memory key only if cache is enable
  266.         String strKey = getKey( htParamRequest, nMode, user );

  267.         // get page from cache
  268.         String strPage = (String) _cachePages.getFromCache( strKey );

  269.         if ( strPage == null )
  270.         {
  271.             // only one thread can evaluate the page
  272.             synchronized( strKey )
  273.             {
  274.                 // can be useful if an other thread had evaluate the
  275.                 // page
  276.                 strPage = (String) _cachePages.getFromCache( strKey );

  277.                 // ignore checkstyle, this double verification is useful
  278.                 // when page cache has been created when thread is
  279.                 // blocked on synchronized
  280.                 if ( strPage == null )
  281.                 {
  282.                     boolean bCanBeCached = true;

  283.                     AppLogService.debug( "Page generation {}", strKey );

  284.                     RedirectionResponseWrapper response = new RedirectionResponseWrapper( LocalVariables.getResponse( ) );

  285.                     LocalVariables.setLocal( LocalVariables.getConfig( ), LocalVariables.getRequest( ), response );
  286.                     request.setAttribute( ATTRIBUTE_CORE_CAN_PAGE_BE_CACHED, null );
  287.                     // The key is not in the cache, so we have to build
  288.                     // the page
  289.                     strPage = buildPageContent( strIdPage, nMode, request );

  290.                     // We check if the page contains portlets that can not be cached.
  291.                     if ( Boolean.FALSE.equals( request.getAttribute( ATTRIBUTE_CORE_CAN_PAGE_BE_CACHED ) ) )
  292.                     {
  293.                         bCanBeCached = false;
  294.                     }

  295.                     if ( response.getRedirectLocation( ) != null )
  296.                     {
  297.                         AppLogService.debug( "Redirection found {}", response.getRedirectLocation( ) );
  298.                         strPage = REDIRECTION_KEY + response.getRedirectLocation( );
  299.                     }

  300.                     // Add the page to the cache if the page can be
  301.                     // cached
  302.                     if ( bCanBeCached && ( nMode != MODE_ADMIN ) )
  303.                     {
  304.                         _cachePages.putInCache( strKey, strPage );
  305.                     }
  306.                 }
  307.                 else
  308.                 {
  309.                     AppLogService.debug( "Page read from cache after synchronisation {}", strKey );
  310.                 }
  311.             }
  312.         }
  313.         else
  314.         {
  315.             AppLogService.debug( "Page read from cache {}", strKey );
  316.         }

  317.         return strPage;
  318.     }

  319.     private Map<String, String> readRequestParams( HttpServletRequest request, String strIdPage )
  320.     {
  321.         Enumeration<?> enumParam = request.getParameterNames( );
  322.         Map<String, String> htParamRequest = new HashMap<>( );

  323.         while ( enumParam.hasMoreElements( ) )
  324.         {
  325.             String paramName = (String) enumParam.nextElement( );
  326.             htParamRequest.put( paramName, request.getParameter( paramName ) );
  327.         }

  328.         if ( !htParamRequest.containsKey( Parameters.PAGE_ID ) )
  329.         {
  330.             htParamRequest.put( Parameters.PAGE_ID, strIdPage );
  331.         }

  332.         if ( !htParamRequest.containsKey( Parameters.BASE_URL ) )
  333.         {
  334.             htParamRequest.put( Parameters.BASE_URL, AppPathService.getBaseUrl( request ) );
  335.         }

  336.         String strUserTheme = ThemesService.getUserTheme( request );

  337.         if ( strUserTheme != null )
  338.         {
  339.             htParamRequest.put( KEY_THEME, strUserTheme );
  340.         }
  341.         return htParamRequest;
  342.     }

  343.     /**
  344.      * Build the page content.
  345.      *
  346.      * @param strIdPage
  347.      *            The page ID
  348.      * @param nMode
  349.      *            The current mode.
  350.      * @param request
  351.      *            The HttpRequest
  352.      * @return The HTML code of the page as a String.
  353.      * @throws SiteMessageException
  354.      *             occurs when a site message need to be displayed
  355.      */
  356.     public String buildPageContent( String strIdPage, int nMode, HttpServletRequest request ) throws SiteMessageException
  357.     {
  358.         int nIdPage;
  359.         Page page;

  360.         nIdPage = Integer.parseInt( strIdPage );

  361.         boolean bPageExist = PageHome.checkPageExist( nIdPage );

  362.         if ( bPageExist )
  363.         {
  364.             page = PageHome.getPage( nIdPage );
  365.         }
  366.         else
  367.         {
  368.             // If there is a problem finding the page, returns the home page
  369.             nIdPage = PortalService.getRootPageId( );
  370.             page = PageHome.getPage( nIdPage );
  371.         }

  372.         PageData data = new PageData( );
  373.         data.setName( page.getName( ) );
  374.         data.setPagePath( PortalService.getPagePathContent( nIdPage, nMode, request ) );
  375.         data.setTheme( page.getCodeTheme( ) );
  376.         data.setMetaKeywords( page.getMetaKeywords( ) );
  377.         data.setMetaDescription( page.getMetaDescription( ) );
  378.         data.setDisplayDateUpdate( page.getDisplayDateUpdate( ) );
  379.         data.setDateUpdate( page.getDateUpdate( ) );

  380.         // Checks the page role (v1.1)
  381.         String strRole = page.getRole( );

  382.         if ( !strRole.equals( Page.ROLE_NONE ) && ( SecurityService.isAuthenticationEnable( ) ) && ( nMode != MODE_ADMIN ) )
  383.         {
  384.             LuteceUser user = SecurityService.getInstance( ).getRegisteredUser( request );

  385.             if ( ( user == null ) && ( !SecurityService.getInstance( ).isExternalAuthentication( ) ) )
  386.             {
  387.                 // The user is not registered and identify itself with the
  388.                 // Portal authentication
  389.                 String strAccessControledTemplate = SecurityService.getInstance( ).getAccessControledTemplate( );
  390.                 HashMap<String, Object> model = new HashMap<>( );
  391.                 String strLoginUrl = SecurityService.getInstance( ).getLoginPageUrl( );
  392.                 model.put( MARK_URL_LOGIN, strLoginUrl );

  393.                 HtmlTemplate tAccessControled = AppTemplateService.getTemplate( strAccessControledTemplate, request.getLocale( ), model );

  394.                 data.setContent( tAccessControled.getHtml( ) );

  395.                 return PortalService.buildPageContent( nIdPage, data, nMode, request );
  396.             }

  397.             if ( !SecurityService.getInstance( ).isUserInRole( request, strRole ) )
  398.             {
  399.                 // The user doesn't have the correct role
  400.                 String strAccessDeniedTemplate = SecurityService.getInstance( ).getAccessDeniedTemplate( );
  401.                 HtmlTemplate tAccessDenied = AppTemplateService.getTemplate( strAccessDeniedTemplate, request.getLocale( ) );
  402.                 data.setContent( tAccessDenied.getHtml( ) );

  403.                 return PortalService.buildPageContent( nIdPage, data, nMode, request );
  404.             }
  405.         }

  406.         // Added in v2.0
  407.         // Add the page authorization
  408.         if ( nMode == MODE_ADMIN )
  409.         {
  410.             AdminUser user = AdminUserService.getAdminUser( request );

  411.             if ( isAuthorizedAdminPage( nIdPage, PageResourceIdService.PERMISSION_VIEW, user ) )
  412.             {
  413.                 // Fill a PageData structure for those elements
  414.                 data.setContent( getPageContent( nIdPage, nMode, request ) );
  415.             }
  416.             else
  417.             {
  418.                 data.setContent( I18nService.getLocalizedString( PROPERTY_MESSAGE_PAGE_ACCESS_DENIED, user.getLocale( ) ) );
  419.             }
  420.         }
  421.         else
  422.         {
  423.             data.setContent( getPageContent( nIdPage, nMode, request ) );
  424.         }

  425.         if ( nIdPage == PortalService.getRootPageId( ) )
  426.         {
  427.             // This page is the home page.
  428.             data.setHomePage( true );
  429.         }

  430.         return PortalService.buildPageContent( nIdPage, data, nMode, request );
  431.     }

  432.     /**
  433.      * Build the page content.
  434.      *
  435.      * @param nIdPage
  436.      *            The page ID
  437.      * @param nMode
  438.      *            The current mode.
  439.      * @param request
  440.      *            The HttpRequest
  441.      * @return The HTML code of the page as a String.
  442.      * @throws SiteMessageException
  443.      *             occurs when a site message need to be displayed
  444.      */
  445.     @Override
  446.     public String getPageContent( int nIdPage, int nMode, HttpServletRequest request ) throws SiteMessageException
  447.     {
  448.         Locale locale = Optional.ofNullable( request ).map( HttpServletRequest::getLocale ).orElse( LocaleService.getDefault( ) );

  449.         String [ ] arrayContent = new String [ MAX_COLUMNS];

  450.         for ( int i = 0; i < MAX_COLUMNS; i++ )
  451.         {
  452.             arrayContent [i] = "";
  453.         }

  454.         Page page = PageHome.findByPrimaryKey( nIdPage );
  455.         Map<String, String> mapParams = getParams( request, nMode );
  456.         boolean bCanPageBeCached = Boolean.TRUE;
  457.         LuteceUser user = SecurityService.getInstance( ).getRegisteredUser( request );

  458.         for ( Portlet portlet : page.getPortlets( ) )
  459.         {
  460.             int nCol = portlet.getColumn( ) - 1;

  461.             if ( nCol < MAX_COLUMNS )
  462.             {
  463.                 arrayContent [nCol] += getPortletContent( request, portlet, mapParams, nMode );
  464.             }

  465.             // We check if the portlet can be cached
  466.             if ( ( user != null ) ? ( !portlet.canBeCachedForConnectedUsers( ) ) : ( !portlet.canBeCachedForAnonymousUsers( ) ) )
  467.             {
  468.                 bCanPageBeCached = false;
  469.             }
  470.         }

  471.         // Add columns outline in admin mode
  472.         if ( nMode == MODE_ADMIN )
  473.         {
  474.             for ( int i = 0; i < MAX_COLUMNS; i++ )
  475.             {
  476.                 arrayContent [i] = addColumnOutline( i + 1, arrayContent [i], locale );
  477.             }
  478.         }

  479.         // We save that the page that is generating can not be cached
  480.         if ( !bCanPageBeCached && request != null )
  481.         {
  482.             request.setAttribute( ATTRIBUTE_CORE_CAN_PAGE_BE_CACHED, false );
  483.         }

  484.         Map<String, Object> rootModel = new HashMap<>( );

  485.         for ( int j = 0; j < MAX_COLUMNS; j++ )
  486.         {
  487.             rootModel.put( "page_content_col" + ( j + 1 ), arrayContent [j] );
  488.         }

  489.         List<PageInclude> listIncludes = PageIncludeService.getIncludes( );
  490.         PageData data = new PageData( );

  491.         for ( PageInclude pic : listIncludes )
  492.         {
  493.             pic.fillTemplate( rootModel, data, nMode, request );
  494.         }

  495.         HtmlTemplate t = AppTemplateService.getTemplate( page.getTemplate( ), locale, rootModel );

  496.         return t.getHtml( );
  497.     }

  498.     /**
  499.      * Add the HTML code to display column outlines
  500.      *
  501.      * @param columnId
  502.      *            the column id
  503.      * @param content
  504.      *            the column content
  505.      * @param locale
  506.      *            the locale
  507.      * @return The column code
  508.      */
  509.     private String addColumnOutline( int columnId, String content, Locale locale )
  510.     {
  511.         Map<String, Object> model = new HashMap<>( 2 );
  512.         model.put( MARK_COLUMN_CONTENT, content );
  513.         model.put( MARK_COLUMN_ID, columnId );

  514.         HtmlTemplate template = AppTemplateService.getTemplate( TEMPLATE_COLUMN_OUTLINE, locale, model );

  515.         return template.getHtml( );
  516.     }

  517.     /**
  518.      * Get the portlet content
  519.      *
  520.      * @param request
  521.      *            The HTTP request
  522.      * @param portlet
  523.      *            The portlet
  524.      * @param mapRequestParams
  525.      *            request parameters
  526.      * @param nMode
  527.      *            The mode
  528.      * @return The content
  529.      * @throws SiteMessageException
  530.      *             If an error occurs
  531.      */
  532.     private String getPortletContent( HttpServletRequest request, Portlet portlet, Map<String, String> mapRequestParams, int nMode ) throws SiteMessageException
  533.     {
  534.         if ( ( request != null ) && !isPortletVisible( request, portlet, nMode ) )
  535.         {
  536.             return StringUtils.EMPTY;
  537.         }

  538.         if ( request != null )
  539.         {
  540.             String strPluginName = portlet.getPluginName( );
  541.             request.setAttribute( PARAMETER_PLUGIN_NAME, strPluginName );
  542.         }

  543.         String strPortletContent = StringUtils.EMPTY;

  544.         // Add the admin buttons for portlet management on admin mode
  545.         if ( nMode == MODE_ADMIN )
  546.         {
  547.             strPortletContent = ADMIN_PORTLET_OPEN_TAG + addAdminButtons( request, portlet );
  548.         }

  549.         String strKey = StringUtils.EMPTY;

  550.         LuteceUser user = null;

  551.         if ( SecurityService.isAuthenticationEnable( ) )
  552.         {
  553.             user = SecurityService.getInstance( ).getRegisteredUser( request );
  554.         }

  555.         boolean isCacheEnabled = nMode != MODE_ADMIN && _cachePortlets.isCacheEnable( );
  556.         boolean bCanBeCached = user != null ? portlet.canBeCachedForConnectedUsers( ) : portlet.canBeCachedForAnonymousUsers( );

  557.         if ( portlet.isContentGeneratedByXmlAndXsl( ) )
  558.         {
  559.             Map<String, String> mapParams = mapRequestParams;
  560.             Map<String, String> mapXslParams = portlet.getXslParams( );

  561.             if ( mapParams != null )
  562.             {
  563.                 if ( mapXslParams != null )
  564.                 {
  565.                     mapParams.putAll( mapXslParams );
  566.                 }
  567.             }
  568.             else
  569.             {
  570.                 mapParams = mapXslParams;
  571.             }

  572.             if ( isCacheEnabled && bCanBeCached )
  573.             {
  574.                 mapParams.put( PARAMETER_PORTLET, String.valueOf( portlet.getId( ) ) );
  575.                 strKey = _cksPortlet.getKey( mapParams, nMode, user );

  576.                 String strPortlet = (String) _cachePortlets.getFromCache( strKey );

  577.                 if ( strPortlet != null )
  578.                 {
  579.                     return strPortlet;
  580.                 }
  581.             }

  582.             Properties outputProperties = ModeHome.getOuputXslProperties( nMode );
  583.             String strXslUniqueId = XSL_UNIQUE_PREFIX + String.valueOf( portlet.getStyleId( ) );
  584.             XmlTransformerService xmlTransformerService = new XmlTransformerService( );
  585.             String strPortletXmlContent = portlet.getXml( request );
  586.             strPortletContent += xmlTransformerService.transformBySourceWithXslCache( strPortletXmlContent, portlet.getXslSource( nMode ), strXslUniqueId, mapParams, outputProperties );
  587.         }
  588.         else
  589.         {
  590.             if ( isCacheEnabled && bCanBeCached )
  591.             {
  592.                 mapRequestParams.put( PARAMETER_PORTLET, String.valueOf( portlet.getId( ) ) );
  593.                 strKey = _cksPortlet.getKey( mapRequestParams, nMode, user );

  594.                 String strPortlet = (String) _cachePortlets.getFromCache( strKey );

  595.                 if ( strPortlet != null )
  596.                 {
  597.                     return strPortlet;
  598.                 }
  599.             }

  600.             strPortletContent += portlet.getHtmlContent( request );
  601.         }

  602.         if ( isCacheEnabled && StringUtils.isNotEmpty( strKey ) )
  603.         {
  604.             _cachePortlets.putInCache( strKey, strPortletContent );
  605.         }
  606.        
  607.         if ( nMode == MODE_ADMIN )
  608.         {
  609.             strPortletContent += ADMIN_PORTLET_CLOSE_TAG;
  610.         }
  611.        
  612.         return strPortletContent;
  613.     }

  614.     private boolean isPortletVisible( HttpServletRequest request, Portlet portlet, int nMode )
  615.     {
  616.         if ( ( nMode != MODE_ADMIN ) && ( portlet.getStatus( ) == Portlet.STATUS_UNPUBLISHED ) )
  617.         {
  618.             return false;
  619.         }

  620.         String strRole = portlet.getRole( );
  621.         boolean bUserInRole = SecurityService.isAuthenticationEnable( ) ? SecurityService.getInstance( ).isUserInRole( request, strRole ) : true;

  622.         boolean [ ] conditions = new boolean [ ] {
  623.                 strRole.equals( Page.ROLE_NONE ), // No role is required so the portlet is visible for anyone
  624.                 !SecurityService.isAuthenticationEnable( ), // No authentication
  625.                 nMode == MODE_ADMIN, // We are in Admin mode, so all the portlet should be visible
  626.                 bUserInRole // The authentication is ON and the user get the role
  627.         };

  628.         return BooleanUtils.or( conditions );
  629.     }

  630.     /**
  631.      * 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
  632.      * manage an hashmap of string reference for cache keys to be able to get them back.
  633.      *
  634.      * @param mapParams
  635.      *            The Map params
  636.      * @param nMode
  637.      *            The current mode.
  638.      * @param user
  639.      *            Current Lutece user
  640.      * @return The HashMap key for articles pages as a String.
  641.      */
  642.     private String getKey( Map<String, String> mapParams, int nMode, LuteceUser user )
  643.     {
  644.         String strKey = _cksPage.getKey( mapParams, nMode, user );

  645.         return _cachePages.getKey( strKey );
  646.     }

  647.     /**
  648.      * Remove a page from the cache
  649.      *
  650.      * @param nIdPage
  651.      *            The page ID
  652.      */
  653.     private void invalidatePage( int nIdPage )
  654.     {
  655.         String strIdPage = String.valueOf( nIdPage );
  656.         invalidatePage( strIdPage );
  657.     }

  658.     /**
  659.      * @param cacheKeyService
  660.      *            the _cacheKeyService to set
  661.      */
  662.     public void setPageCacheKeyService( ICacheKeyService cacheKeyService )
  663.     {
  664.         _cksPage = cacheKeyService;
  665.     }

  666.     /**
  667.      * @param cacheKeyService
  668.      *            the _cacheKeyService to set
  669.      */
  670.     public void setPortletCacheKeyService( ICacheKeyService cacheKeyService )
  671.     {
  672.         _cksPortlet = cacheKeyService;
  673.     }

  674.     /**
  675.      * @param removalService
  676.      *            the removal listener service
  677.      */
  678.     public void setRoleRemovalService( RemovalListenerService removalService )
  679.     {
  680.         removalService.registerListener( new PageRoleRemovalListener( ) );
  681.         removalService.registerListener( new PortletRoleRemovalListener( ) );
  682.     }

  683.     /**
  684.      * Remove a page from the cache
  685.      *
  686.      * @param strIdPage
  687.      *            The page ID
  688.      */
  689.     private void invalidatePage( String strIdPage )
  690.     {
  691.         if ( _cachePages.isCacheEnable( ) )
  692.         {
  693.             String strKey = "[" + Parameters.PAGE_ID + ":" + strIdPage + "]";

  694.             for ( String strKeyTemp : (List<String>) _cachePages.getCache( ).getKeys( ) )
  695.             {
  696.                 if ( ( strKeyTemp.contains( strKey ) ) || ( WELCOME_PAGE_ID.equals( strIdPage ) && WELCOME_PAGE_CACHE_KEY.equals( strKeyTemp ) ) )
  697.                 {
  698.                     _cachePages.getCache( ).remove( strKeyTemp );

  699.                     if ( AppLogService.isDebugEnabled( ) )
  700.                     {
  701.                         AppLogService.debug( "Page (cache key : " + strKeyTemp + ") removed from the cache." );
  702.                     }
  703.                 }
  704.             }
  705.         }
  706.     }

  707.     // ///////////////////////////////////////////////////////////////////////////
  708.     // Events Listeners management
  709.     /**
  710.      * Add a new page event listener
  711.      *
  712.      * @param listener
  713.      *            An event listener to add
  714.      */
  715.     public static void addPageEventListener( PageEventListener listener )
  716.     {
  717.         _listEventListeners.add( listener );
  718.         AppLogService.info( "New Page Event Listener registered : {}", listener.getClass( ).getName( ) );
  719.     }

  720.     /**
  721.      * Notify an event to all listeners
  722.      *
  723.      * @param event
  724.      *            A page Event
  725.      */
  726.     private void notifyListeners( PageEvent event )
  727.     {
  728.         for ( PageEventListener listener : _listEventListeners )
  729.         {
  730.             listener.processPageEvent( event );
  731.         }
  732.     }

  733.     /**
  734.      * Returns the resource type Id
  735.      *
  736.      * @return The resource type Id
  737.      */
  738.     @Override
  739.     public String getResourceTypeId( )
  740.     {
  741.         return Page.IMAGE_RESOURCE_TYPE_ID;
  742.     }

  743.     /**
  744.      * Gets the image resource for a given resource
  745.      *
  746.      * @param nIdResource
  747.      *            The Resource id
  748.      * @return The image resource
  749.      */
  750.     @Override
  751.     public ImageResource getImageResource( int nIdResource )
  752.     {
  753.         return PageHome.getImageResource( nIdResource );
  754.     }

  755.     /**
  756.      * Create a page
  757.      *
  758.      * @param page
  759.      *            The page to create
  760.      */
  761.     @Override
  762.     public void createPage( Page page )
  763.     {
  764.         PageHome.create( page );

  765.         PageEvent event = new PageEvent( page, PageEvent.PAGE_CREATED );
  766.         notifyListeners( event );
  767.     }

  768.     /**
  769.      * Update a given page
  770.      *
  771.      * @param page
  772.      *            The page to update
  773.      */
  774.     @Override
  775.     public void updatePage( Page page )
  776.     {
  777.         PageHome.update( page );

  778.         PageEvent event = new PageEvent( page, PageEvent.PAGE_CONTENT_MODIFIED );
  779.         notifyListeners( event );
  780.     }

  781.     /**
  782.      * Remove a given page
  783.      *
  784.      * @param nPageId
  785.      *            The page Id
  786.      */
  787.     @Override
  788.     public void removePage( int nPageId )
  789.     {
  790.         Page page = PageHome.findByPrimaryKey( nPageId );
  791.         PageEvent event = new PageEvent( page, PageEvent.PAGE_DELETED );
  792.         PageHome.remove( nPageId );
  793.         notifyListeners( event );
  794.     }

  795.     /**
  796.      * Process a page event
  797.      *
  798.      * @param event
  799.      *            The event to process
  800.      */
  801.     @Override
  802.     public void processPageEvent( PageEvent event )
  803.     {
  804.         Page page = event.getPage( );
  805.         invalidatePage( page.getId( ) );
  806.     }

  807.     /**
  808.      * {@inheritDoc}
  809.      */
  810.     @Override
  811.     public void processPortletEvent( PortletEvent event )
  812.     {
  813.         invalidateContent( event.getPageId( ) );
  814.     }

  815.     /**
  816.      * Invalidate Page Content
  817.      *
  818.      * @param nPageId
  819.      *            The Page ID
  820.      */
  821.     @Override
  822.     public void invalidateContent( int nPageId )
  823.     {
  824.         Page page = PageHome.findByPrimaryKey( nPageId );
  825.         PageEvent event = new PageEvent( page, PageEvent.PAGE_CONTENT_MODIFIED );
  826.         notifyListeners( event );
  827.     }

  828.     /**
  829.      * Check that a given user is allowed to access a page for a given permission
  830.      *
  831.      * @param nIdPage
  832.      *            the id of the page to check
  833.      * @param strPermission
  834.      *            the permission needed
  835.      * @param user
  836.      *            The current user
  837.      * @return true if authorized, otherwise false
  838.      */
  839.     @Override
  840.     public boolean isAuthorizedAdminPage( int nIdPage, String strPermission, AdminUser user )
  841.     {
  842.         Page page = PageHome.findByPrimaryKey( nIdPage );

  843.         if ( page.getIdAuthorizationNode( ) != null )
  844.         {
  845.             String strAuthorizationNode = Integer.toString( page.getIdAuthorizationNode( ) );

  846.             return ( RBACService.isAuthorized( Page.RESOURCE_TYPE, strAuthorizationNode, strPermission, user ) );
  847.         }

  848.         return true;
  849.     }

  850.     /**
  851.      * Add the HTML code to display admin buttons under each portlet
  852.      *
  853.      * @param request
  854.      *            The Http request
  855.      * @param portlet
  856.      *            The portlet
  857.      * @return The buttons code
  858.      */
  859.     private String addAdminButtons( HttpServletRequest request, Portlet portlet )
  860.     {
  861.         AdminUser user = AdminUserService.getAdminUser( request );

  862.         if ( RBACService.isAuthorized( PortletType.RESOURCE_TYPE, portlet.getPortletTypeId( ), PortletResourceIdService.PERMISSION_MANAGE, user ) )
  863.         {
  864.             Locale locale = user.getLocale( );
  865.             Collection<PortletCustomAdminAction> listCustomActions = new ArrayList<>( );

  866.             // TODO : listCustomActions should be provided by PortletType
  867.             // FIXME : Delete plugin-document specifics
  868.             if ( portlet.getPortletTypeId( ).equals( DOCUMENT_LIST_PORTLET ) || portlet.getPortletTypeId( ).equals( DOCUMENT_PORTLET ) )
  869.             {
  870.                 PortletCustomAdminAction customAction = new PortletCustomAdminAction( );
  871.                 customAction.setActionUrl( DOCUMENT_ACTION_URL );
  872.                 customAction.setImageUrl( DOCUMENT_IMAGE_URL );
  873.                 customAction.setTitle( DOCUMENT_TITLE );
  874.                 listCustomActions.add( customAction );
  875.             }

  876.             Map<String, Object> model = new HashMap<>( );
  877.             model.put( MARK_PORTLET, portlet );
  878.             model.put( MARK_STATUS_PUBLISHED, Portlet.STATUS_PUBLISHED );
  879.             model.put( MARK_MAX_ORDER, PORTLET_MAX_ORDER );
  880.             model.put( MARK_STATUS_UNPUBLISHED, Portlet.STATUS_UNPUBLISHED );
  881.             model.put( MARK_CUSTOM_ACTIONS, listCustomActions );

  882.             HtmlTemplate template = AppTemplateService.getTemplate( TEMPLATE_ADMIN_BUTTONS, locale, model );

  883.             return template.getHtml( );
  884.         }

  885.         return StringUtils.EMPTY;
  886.     }

  887.     /**
  888.      * Gets the params map
  889.      *
  890.      * @param request
  891.      *            The HTTP request
  892.      * @param nMode
  893.      *            The mode
  894.      * @return the map
  895.      */
  896.     private Map<String, String> getParams( HttpServletRequest request, int nMode )
  897.     {
  898.         Map<String, String> mapModifyParam = new HashMap<>( );
  899.         String strParamName;

  900.         // Get request paramaters and store them in a HashMap
  901.         if ( request != null )
  902.         {
  903.             Enumeration<?> enumParam = request.getParameterNames( );

  904.             while ( enumParam.hasMoreElements( ) )
  905.             {
  906.                 strParamName = (String) enumParam.nextElement( );
  907.                 mapModifyParam.put( strParamName, request.getParameter( strParamName ) );
  908.             }

  909.             // Add selected locale
  910.             mapModifyParam.put( PARAMETER_USER_SELECTED_LOCALE, LocaleService.getUserSelectedLocale( request ).getLanguage( ) );
  911.         }

  912.         // Added in v1.3
  913.         // Add a path param for choose url to use in admin or normal mode
  914.         if ( nMode != MODE_ADMIN )
  915.         {
  916.             mapModifyParam.put( PARAMETER_SITE_PATH, AppPathService.getPortalUrl( ) );

  917.             if ( SecurityService.isAuthenticationEnable( ) )
  918.             {
  919.                 mapModifyParam.put( MARKER_IS_USER_AUTHENTICATED,
  920.                         ( SecurityService.getInstance( ).getRegisteredUser( request ) != null ) ? VALUE_TRUE : VALUE_FALSE );
  921.             }
  922.         }
  923.         else
  924.         {
  925.             mapModifyParam.put( PARAMETER_SITE_PATH, AppPathService.getAdminPortalUrl( ) );
  926.             mapModifyParam.put( MARKER_TARGET, TARGET_TOP );
  927.         }

  928.         if ( !mapModifyParam.containsKey( Parameters.PAGE_ID ) )
  929.         {
  930.             mapModifyParam.put( Parameters.PAGE_ID, Integer.toString( PortalService.getRootPageId( ) ) );
  931.         }

  932.         return mapModifyParam;
  933.     }

  934.     /**
  935.      * Management of the image associated to the page
  936.      *
  937.      * @param strPageId
  938.      *            The page identifier
  939.      * @return The url
  940.      */
  941.     public String getResourceImagePage( String strPageId )
  942.     {
  943.         String strResourceType = getResourceTypeId( );
  944.         UrlItem url = new UrlItem( Parameters.IMAGE_SERVLET );
  945.         url.addParameter( Parameters.RESOURCE_TYPE, strResourceType );
  946.         url.addParameter( Parameters.RESOURCE_ID, strPageId );

  947.         return url.getUrlWithEntity( );
  948.     }

  949.     /**
  950.      * Gets the page cache service.
  951.      *
  952.      * @return the page cache service
  953.      */
  954.     public PageCacheService getPageCacheService( )
  955.     {
  956.         return _cachePages;
  957.     }

  958.     /**
  959.      * Sets the cache page service
  960.      *
  961.      * @param pageCacheService
  962.      *            the page cache service
  963.      */
  964.     public void setPageCacheService( PageCacheService pageCacheService )
  965.     {
  966.         _cachePages = pageCacheService;
  967.     }

  968.     /**
  969.      * Gets the portlet cache service
  970.      *
  971.      * @return the porlet cache service
  972.      */
  973.     public PortletCacheService getPortletCacheService( )
  974.     {
  975.         return _cachePortlets;
  976.     }

  977.     /**
  978.      * Gets the portlet cache service
  979.      *
  980.      * @param portletCacheService
  981.      *            the portlet cache service
  982.      */
  983.     public void setPortletCacheService( PortletCacheService portletCacheService )
  984.     {
  985.         _cachePortlets = portletCacheService;
  986.     }

  987.     /**
  988.      * update authorization node of children page
  989.      *
  990.      * @param nIdParentPage
  991.      *            id of the parent page
  992.      * @param nIdNewAuthorizationNode
  993.      *            the new authorization id
  994.      */
  995.     public static void updateChildrenAuthorizationNode( int nIdParentPage, Integer nIdNewAuthorizationNode )
  996.     {
  997.         List<Integer> listPagesChildren = PageHome.getPagesWhichMustChangeAuthorizationNode( nIdParentPage );

  998.         if ( listPagesChildren != null )
  999.         {
  1000.             for ( Integer idPage : listPagesChildren )
  1001.             {
  1002.                 PageHome.updateAuthorizationNode( idPage, nIdNewAuthorizationNode );
  1003.                 updateChildrenAuthorizationNode( idPage, nIdNewAuthorizationNode );
  1004.             }
  1005.         }
  1006.     }

  1007. }