XPageAppService.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.content;

  35. import fr.paris.lutece.portal.service.init.LuteceInitException;
  36. import fr.paris.lutece.portal.service.message.SiteMessage;
  37. import fr.paris.lutece.portal.service.message.SiteMessageException;
  38. import fr.paris.lutece.portal.service.message.SiteMessageService;
  39. import fr.paris.lutece.portal.service.portal.PortalService;
  40. import fr.paris.lutece.portal.service.security.LuteceUser;
  41. import fr.paris.lutece.portal.service.security.SecurityService;
  42. import fr.paris.lutece.portal.service.security.UserNotSignedException;
  43. import fr.paris.lutece.portal.service.spring.SpringContextService;
  44. import fr.paris.lutece.portal.service.template.AppTemplateService;
  45. import fr.paris.lutece.portal.service.util.AppException;
  46. import fr.paris.lutece.portal.service.util.AppLogService;
  47. import fr.paris.lutece.portal.web.xpages.XPage;
  48. import fr.paris.lutece.portal.web.xpages.XPageApplication;
  49. import fr.paris.lutece.portal.web.xpages.XPageApplicationEntry;
  50. import fr.paris.lutece.util.html.HtmlTemplate;
  51. import fr.paris.lutece.util.http.SecurityUtil;

  52. import org.apache.commons.collections.CollectionUtils;
  53. import org.springframework.beans.factory.NoSuchBeanDefinitionException;

  54. import java.util.Collection;
  55. import java.util.HashMap;
  56. import java.util.List;
  57. import java.util.Map;

  58. import javax.servlet.http.HttpServletRequest;
  59. import javax.servlet.http.HttpSession;

  60. /**
  61.  * This class delivers Extra pages (xpages) to web components. An XPage is a page where the content is provided by a specific class, but should be integrated
  62.  * into the portal struture and design. XPageApps are identified by a key name. To display an XPage into the portal just call the following url :<br>
  63.  * <code>
  64.  * Portal.jsp?page=<i>keyname</i>&amp;param1=value1&amp; ...&amp;paramN=valueN </code>
  65.  *
  66.  * @see fr.paris.lutece.portal.web.xpages.XPage
  67.  */
  68. public class XPageAppService extends ContentService
  69. {
  70.     public static final String PARAM_XPAGE_APP = "page";
  71.     private static final String ERROR_INSTANTIATION = "Error instantiating XPageApplication : ";
  72.     private static final String CONTENT_SERVICE_NAME = "XPageAppService";
  73.     private static final String MESSAGE_ERROR_APP_BODY = "portal.util.message.errorXpageApp";
  74.     private static final String ATTRIBUTE_XPAGE = "LUTECE_XPAGE_";
  75.     private static Map<String, XPageApplicationEntry> _mapApplications = new HashMap<>( );

  76.     /**
  77.      * Register an application by its entry defined in the plugin xml file
  78.      *
  79.      * @param entry
  80.      *            The application entry
  81.      * @throws LuteceInitException
  82.      *             If an error occured
  83.      */
  84.     public static void registerXPageApplication( XPageApplicationEntry entry ) throws LuteceInitException
  85.     {
  86.         try
  87.         {
  88.             if ( entry.getClassName( ) == null )
  89.             {
  90.                 String applicationBeanName = entry.getPluginName( ) + ".xpage." + entry.getId( );

  91.                 if ( !SpringContextService.getContext( ).containsBean( applicationBeanName ) )
  92.                 {
  93.                     throw new LuteceInitException( ERROR_INSTANTIATION + entry.getId( ) + " - Could not find bean named " + applicationBeanName,
  94.                             new NoSuchBeanDefinitionException( applicationBeanName ) );
  95.                 }
  96.             }
  97.             else
  98.             {
  99.                 // check that the class can be found
  100.                 Class.forName( entry.getClassName( ) ).newInstance( );
  101.             }

  102.             _mapApplications.put( entry.getId( ), entry );
  103.             AppLogService.info( "New XPage application registered : {} {}", entry::getId, ( ) -> ( entry.isEnabled( ) ? "" : " (disabled)" ) );
  104.         }
  105.         catch( ClassNotFoundException | InstantiationException | IllegalAccessException e )
  106.         {
  107.             throw new LuteceInitException( ERROR_INSTANTIATION + entry.getId( ) + " - " + e.getCause( ), e );
  108.         }
  109.     }

  110.     /**
  111.      * Returns the Content Service name
  112.      *
  113.      * @return The name as a String
  114.      */
  115.     @Override
  116.     public String getName( )
  117.     {
  118.         return CONTENT_SERVICE_NAME;
  119.     }

  120.     /**
  121.      * Analyzes request parameters to see if the request should be handled by the current Content Service
  122.      *
  123.      * @param request
  124.      *            The HTTP request
  125.      * @return true if this ContentService should handle this request
  126.      */
  127.     @Override
  128.     public boolean isInvoked( HttpServletRequest request )
  129.     {
  130.         String strXPage = request.getParameter( PARAM_XPAGE_APP );

  131.         return ( strXPage != null ) && ( strXPage.length( ) > 0 );
  132.     }

  133.     /**
  134.      * Gets the current cache status.
  135.      *
  136.      * @return true if enable, otherwise false
  137.      */
  138.     @Override
  139.     public boolean isCacheEnable( )
  140.     {
  141.         return false;
  142.     }

  143.     /**
  144.      * Reset the cache.
  145.      */
  146.     @Override
  147.     public void resetCache( )
  148.     {
  149.         // Do nothing
  150.     }

  151.     /**
  152.      * Gets the number of item currently in the cache.
  153.      *
  154.      * @return the number of item currently in the cache.
  155.      */
  156.     @Override
  157.     public int getCacheSize( )
  158.     {
  159.         return 0;
  160.     }

  161.     /**
  162.      * Build the XPage content.
  163.      *
  164.      * @param request
  165.      *            The HTTP request.
  166.      * @param nMode
  167.      *            The current mode.
  168.      * @return The HTML code of the page.
  169.      * @throws UserNotSignedException
  170.      *             The User Not Signed Exception
  171.      * @throws SiteMessageException
  172.      *             occurs when a site message need to be displayed
  173.      */
  174.     @Override
  175.     public String getPage( HttpServletRequest request, int nMode ) throws UserNotSignedException, SiteMessageException
  176.     {
  177.         // Gets XPage info from the lutece.properties
  178.         String strName = request.getParameter( PARAM_XPAGE_APP );

  179.         XPageApplicationEntry entry = getApplicationEntry( strName );

  180.         // TODO : Handle entry == null
  181.         if ( ( entry == null ) || ( !entry.isEnable( ) ) )
  182.         {
  183.             AppLogService.error( "The specified Xpage '{}' cannot be retrieved. Check installation of your Xpage application.",
  184.                     ( ) -> SecurityUtil.logForgingProtect( strName ) );
  185.             SiteMessageService.setMessage( request, MESSAGE_ERROR_APP_BODY, SiteMessage.TYPE_ERROR );

  186.             return null; // unreachable because SiteMessageService.setMessage throws
  187.         }

  188.         XPage page = null;
  189.         List<String> listRoles = entry.getRoles( );

  190.         if ( SecurityService.isAuthenticationEnable( ) && CollectionUtils.isNotEmpty( listRoles ) )
  191.         {
  192.             LuteceUser user = SecurityService.getInstance( ).getRegisteredUser( request );

  193.             if ( user == null )
  194.             {
  195.                 throw new UserNotSignedException( );
  196.             }

  197.             boolean bAutorized = SecurityService.getInstance( ).isUserInAnyRole( request, listRoles );

  198.             if ( bAutorized )
  199.             {
  200.                 XPageApplication application = getXPageSessionInstance( request, entry );
  201.                 page = application.getPage( request, nMode, entry.getPlugin( ) );
  202.             }
  203.             else
  204.             {
  205.                 // The user doesn't have the correct role
  206.                 String strAccessDeniedTemplate = SecurityService.getInstance( ).getAccessDeniedTemplate( );
  207.                 HtmlTemplate tAccessDenied = AppTemplateService.getTemplate( strAccessDeniedTemplate );
  208.                 page = new XPage( );
  209.                 page.setContent( tAccessDenied.getHtml( ) );
  210.             }
  211.         }
  212.         else
  213.         {
  214.             XPageApplication application = getXPageSessionInstance( request, entry );
  215.             page = application.getPage( request, nMode, entry.getPlugin( ) );
  216.         }

  217.         if ( page.isStandalone( ) || page.isSendRedirect() )
  218.         {
  219.             return page.getContent( );
  220.         }

  221.         PageData data = new PageData( );

  222.         data.setContent( page.getContent( ) );
  223.         data.setName( page.getTitle( ) );

  224.         // set the page path. Done by adding the extra-path information to the
  225.         // pathLabel.
  226.         String strXml = page.getXmlExtendedPathLabel( );

  227.         if ( strXml == null )
  228.         {
  229.             data.setPagePath( PortalService.getXPagePathContent( page.getPathLabel( ), 0, request ) );
  230.         }
  231.         else
  232.         {
  233.             data.setPagePath( PortalService.getXPagePathContent( page.getPathLabel( ), 0, strXml, request ) );
  234.         }

  235.         return PortalService.buildPageContent( data, nMode, request );
  236.     }

  237.     /**
  238.      * Gets Application entry by name
  239.      *
  240.      * @param strName
  241.      *            The application's name
  242.      * @return The entry
  243.      */
  244.     public static XPageApplicationEntry getApplicationEntry( String strName )
  245.     {
  246.         return _mapApplications.get( strName );
  247.     }

  248.     /**
  249.      * Gets applications list
  250.      *
  251.      * @return A collection of applications
  252.      */
  253.     public static Collection<XPageApplicationEntry> getXPageApplicationsList( )
  254.     {
  255.         return _mapApplications.values( );
  256.     }

  257.     /**
  258.      * Return an instance of the XPage attached to the current Http Session
  259.      *
  260.      * @param request
  261.      *            The HTTP request
  262.      * @param entry
  263.      *            The XPage entry
  264.      * @return The XPage instance
  265.      */
  266.     private static XPageApplication getXPageSessionInstance( HttpServletRequest request, XPageApplicationEntry entry )
  267.     {
  268.         HttpSession session = request.getSession( true );
  269.         String strAttribute = ATTRIBUTE_XPAGE + entry.getId( );
  270.         XPageApplication application = (XPageApplication) session.getAttribute( strAttribute );

  271.         if ( application == null )
  272.         {
  273.             application = getApplicationInstance( entry );
  274.             session.setAttribute( strAttribute, application );
  275.             AppLogService.debug( "New XPage instance of {} created and attached to session {}", entry.getClassName( ), session );
  276.         }

  277.         return application;
  278.     }

  279.     /**
  280.      * Get an XPage instance
  281.      *
  282.      * @param entry
  283.      *            The Xpage entry
  284.      * @return An instance of a given XPage
  285.      */
  286.     public static XPageApplication getApplicationInstance( XPageApplicationEntry entry )
  287.     {
  288.         XPageApplication application = null;

  289.         try
  290.         {
  291.             if ( entry.getClassName( ) == null )
  292.             {
  293.                 application = SpringContextService.getBean( entry.getPluginName( ) + ".xpage." + entry.getId( ) );
  294.             }
  295.             else
  296.             {
  297.                 application = (XPageApplication) Class.forName( entry.getClassName( ) ).newInstance( );
  298.             }
  299.         }
  300.         catch( Exception e )
  301.         {
  302.             throw new AppException( ERROR_INSTANTIATION + entry.getId( ) + " - " + e.getCause( ), e );
  303.         }

  304.         return application;
  305.     }
  306. }