PluginJspBean.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.web.system;

  35. import fr.paris.lutece.portal.business.portlet.PortletType;
  36. import fr.paris.lutece.portal.business.portlet.PortletTypeHome;
  37. import fr.paris.lutece.portal.service.admin.AccessDeniedException;
  38. import fr.paris.lutece.portal.service.admin.AdminUserService;
  39. import fr.paris.lutece.portal.service.database.AppConnectionService;
  40. import fr.paris.lutece.portal.service.i18n.I18nService;
  41. import fr.paris.lutece.portal.service.init.AppInfo;
  42. import fr.paris.lutece.portal.service.message.AdminMessage;
  43. import fr.paris.lutece.portal.service.message.AdminMessageService;
  44. import fr.paris.lutece.portal.service.plugin.Plugin;
  45. import fr.paris.lutece.portal.service.plugin.PluginService;
  46. import fr.paris.lutece.portal.service.security.SecurityTokenService;
  47. import fr.paris.lutece.portal.service.template.AppTemplateService;
  48. import fr.paris.lutece.portal.service.util.AppLogService;
  49. import fr.paris.lutece.portal.web.admin.AdminFeaturesPageJspBean;
  50. import fr.paris.lutece.util.ReferenceList;
  51. import fr.paris.lutece.util.html.HtmlTemplate;

  52. import java.util.ArrayList;
  53. import java.util.Collection;
  54. import java.util.HashMap;
  55. import java.util.Locale;
  56. import java.util.Map;

  57. import javax.servlet.ServletContext;
  58. import javax.servlet.http.HttpServletRequest;

  59. /**
  60.  * This class provides the user interface to manage the lutece plugins (install, enable, disable)
  61.  */
  62. public class PluginJspBean extends AdminFeaturesPageJspBean
  63. {
  64.     // //////////////////////////////////////////////////////////////////////////////
  65.     // Constants
  66.     public static final String RIGHT_MANAGE_PLUGINS = "CORE_PLUGINS_MANAGEMENT";
  67.     private static final long serialVersionUID = -9058113426951331118L;
  68.     private static final String TEMPLATE_MANAGE_PLUGINS = "admin/system/manage_plugins.html";
  69.     private static final String MARK_PLUGINS_LIST = "plugins_list";
  70.     private static final String MARK_CORE = "core";
  71.     private static final String MARK_POOLS_LIST = "pools_list";
  72.     private static final String MARK_FILTER_LIST = "filter_list";
  73.     private static final String MARK_CURRENT_FILTER = "current_filter";
  74.     private static final String PROPERTY_PLUGIN_MESSAGE = "portal.system.message.confirmDisable";
  75.     private static final String PROPERTY_PLUGIN_PORTLET_EXIST_MESSAGE = "portal.system.message.portletExist";
  76.     private static final String PROPERTY_PLUGIN_NO_CORE_COMPATIBILITY_MESSAGE = "portal.system.message.noCoreCompatibility";
  77.     private static final String PROPERTY_PLUGIN_INSTALL_ERROR = "portal.system.message.installError";
  78.     private static final String PARAM_PLUGIN_NAME = "plugin_name";
  79.     private static final String PARAM_PLUGIN_TYPE = "plugin_type";
  80.     private static final String PARAM_DB_POOL_NAME = "db_pool_name";
  81.     private static final String PARAM_PLUGIN_TYPE_ALL = "all";
  82.     private static final String PARAM_PLUGIN_TYPE_PORTLET = "portlet";
  83.     private static final String PARAM_PLUGIN_TYPE_APPLICATION = "application";
  84.     private static final String PARAM_PLUGIN_TYPE_FEATURE = "feature";
  85.     private static final String PARAM_PLUGIN_TYPE_INSERTSERVICE = "insertservice";
  86.     private static final String PARAM_PLUGIN_TYPE_CONTENTSERVICE = "contentservice";
  87.     private static final String PROPERTY_PLUGIN_TYPE_NAME_ALL = "portal.system.pluginType.name.all";
  88.     private static final String PROPERTY_PLUGIN_TYPE_NAME_APPLICATION = "portal.system.pluginType.name.application";
  89.     private static final String PROPERTY_PLUGIN_TYPE_NAME_PORTLET = "portal.system.pluginType.name.portlet";
  90.     private static final String PROPERTY_PLUGIN_TYPE_NAME_FEATURE = "portal.system.pluginType.name.feature";
  91.     private static final String PROPERTY_PLUGIN_TYPE_NAME_INSERTSERVICE = "portal.system.pluginType.name.insertService";
  92.     private static final String PROPERTY_PLUGIN_TYPE_NAME_CONTENTSERVICE = "portal.system.pluginType.name.contentService";
  93.     private static final String TEMPLATE_PLUGIN_DETAILS = "/admin/system/view_plugin.html";
  94.     private static final String JSP_UNINSTALL_PLUGIN = "jsp/admin/system/DoUninstallPlugin.jsp";

  95.     /**
  96.      * Returns the plugins management page
  97.      *
  98.      * @param request
  99.      *            The Http request
  100.      * @return Html page
  101.      */
  102.     public String getManagePlugins( HttpServletRequest request )
  103.     {
  104.         Locale locale = AdminUserService.getLocale( request );
  105.         String strPluginTypeFilter = request.getParameter( PARAM_PLUGIN_TYPE );
  106.         Collection<Plugin> listPlugins = PluginService.getPluginList( );
  107.         HashMap<String, Object> model = new HashMap<>( );
  108.         model.put( MARK_PLUGINS_LIST, filterPluginsList( listPlugins, strPluginTypeFilter ) );
  109.         model.put( MARK_CORE, PluginService.getCore( ) );
  110.         model.put( MARK_POOLS_LIST, getPoolsList( ) );
  111.         model.put( MARK_FILTER_LIST, getPluginTypeFilterList( locale ) );
  112.         model.put( MARK_CURRENT_FILTER, ( strPluginTypeFilter != null ) ? strPluginTypeFilter : "" );
  113.         model.put( SecurityTokenService.MARK_TOKEN, SecurityTokenService.getInstance( ).getToken( request, TEMPLATE_MANAGE_PLUGINS ) );

  114.         HtmlTemplate template = AppTemplateService.getTemplate( TEMPLATE_MANAGE_PLUGINS, locale, model );

  115.         return getAdminPage( template.getHtml( ) );
  116.     }

  117.     /**
  118.      * Install a plugin
  119.      *
  120.      * @param request
  121.      *            The Http request
  122.      * @param context
  123.      *            The servlet context
  124.      * @return the url of the page containing a log essage
  125.      * @throws AccessDeniedException
  126.      *             if the security token is invalid
  127.      */
  128.     public String doInstallPlugin( HttpServletRequest request, ServletContext context ) throws AccessDeniedException
  129.     {
  130.         String strPluginName = request.getParameter( PARAM_PLUGIN_NAME );
  131.         Plugin plugin = PluginService.getPlugin( strPluginName );

  132.         if ( !verifyCoreCompatibility( plugin ) )
  133.         {
  134.             Object [ ] args = {
  135.                     plugin.getMinCoreVersion( ), plugin.getMaxCoreVersion( )
  136.             };

  137.             return AdminMessageService.getMessageUrl( request, PROPERTY_PLUGIN_NO_CORE_COMPATIBILITY_MESSAGE, args, AdminMessage.TYPE_STOP );

  138.         }
  139.         if ( !SecurityTokenService.getInstance( ).validate( request, TEMPLATE_MANAGE_PLUGINS ) )
  140.         {
  141.             throw new AccessDeniedException( ERROR_INVALID_TOKEN );
  142.         }
  143.         try
  144.         {
  145.             plugin.install( );
  146.         }
  147.         catch( Exception e )
  148.         {
  149.             AppLogService.error( e.getMessage( ), e );

  150.             return AdminMessageService.getMessageUrl( request, PROPERTY_PLUGIN_INSTALL_ERROR, AdminMessage.TYPE_STOP );
  151.         }

  152.         return getHomeUrl( request );
  153.     }

  154.     /**
  155.      * Uninstall a plugin
  156.      *
  157.      * @param request
  158.      *            The Http request
  159.      * @param context
  160.      *            The servlet context
  161.      * @return the url of the page containing a log essage
  162.      * @throws AccessDeniedException
  163.      *             if the security token is invalid
  164.      */
  165.     public String doUninstallPlugin( HttpServletRequest request, ServletContext context ) throws AccessDeniedException
  166.     {
  167.         if ( !SecurityTokenService.getInstance( ).validate( request, JSP_UNINSTALL_PLUGIN ) )
  168.         {
  169.             throw new AccessDeniedException( ERROR_INVALID_TOKEN );
  170.         }
  171.         try
  172.         {
  173.             String strPluginName = request.getParameter( PARAM_PLUGIN_NAME );
  174.             Plugin plugin = PluginService.getPlugin( strPluginName );
  175.             plugin.uninstall( );
  176.         }
  177.         catch( Exception e )
  178.         {
  179.             AppLogService.error( e.getMessage( ), e );
  180.         }

  181.         return getHomeUrl( request );
  182.     }

  183.     /**
  184.      * Returns the page of confirmation for uninstalling a plugin
  185.      *
  186.      * @param request
  187.      *            The Http Request
  188.      * @return the HTML page
  189.      */
  190.     public String getConfirmUninstallPlugin( HttpServletRequest request )
  191.     {
  192.         String strPluginName = request.getParameter( PARAM_PLUGIN_NAME );
  193.         Plugin plugin = PluginService.getPlugin( strPluginName );
  194.         Collection<PortletType> listPortletTypes = plugin.getPortletTypes( );
  195.         String strMessageKey = PROPERTY_PLUGIN_MESSAGE;
  196.         Map<String, String> parameters = new HashMap<>( );
  197.         parameters.put( PARAM_PLUGIN_NAME, strPluginName );
  198.         parameters.put( SecurityTokenService.PARAMETER_TOKEN, SecurityTokenService.getInstance( ).getToken( request, JSP_UNINSTALL_PLUGIN ) );
  199.         String strAdminMessageUrl = AdminMessageService.getMessageUrl( request, strMessageKey, JSP_UNINSTALL_PLUGIN, AdminMessage.TYPE_CONFIRMATION,
  200.                 parameters );

  201.         for ( PortletType portletType : listPortletTypes )
  202.         {
  203.             String strPluginHomeClass = portletType.getHomeClass( );

  204.             if ( ( plugin.getType( ) & Plugin.PLUGIN_TYPE_PORTLET ) != 0 && isPortletExists( strPluginHomeClass ) )
  205.             {
  206.                 strMessageKey = PROPERTY_PLUGIN_PORTLET_EXIST_MESSAGE;
  207.                 strAdminMessageUrl = AdminMessageService.getMessageUrl( request, strMessageKey, AdminMessage.TYPE_CONFIRMATION );
  208.             }
  209.         }

  210.         return strAdminMessageUrl;
  211.     }

  212.     /**
  213.      * Defines the database connection pool to be used by the plugin
  214.      *
  215.      * @param request
  216.      *            The http request
  217.      * @return the URL to redirect after this action
  218.      * @throws AccessDeniedException
  219.      *             if the security token is invalid
  220.      */
  221.     public String doModifyPluginPool( HttpServletRequest request ) throws AccessDeniedException
  222.     {
  223.         if ( !SecurityTokenService.getInstance( ).validate( request, TEMPLATE_MANAGE_PLUGINS ) )
  224.         {
  225.             throw new AccessDeniedException( ERROR_INVALID_TOKEN );
  226.         }
  227.         String strPluginName = request.getParameter( PARAM_PLUGIN_NAME );
  228.         String strDbPoolName = request.getParameter( PARAM_DB_POOL_NAME );

  229.         try
  230.         {
  231.             Plugin plugin = PluginService.getPlugin( strPluginName );
  232.             plugin.updatePoolName( strDbPoolName );
  233.         }
  234.         catch( Exception e )
  235.         {
  236.             AppLogService.error( e.getMessage( ), e );
  237.         }

  238.         return getHomeUrl( request );
  239.     }

  240.     /**
  241.      * Displays a plugin's description
  242.      *
  243.      * @param request
  244.      *            The HTTP request
  245.      * @return The popup HTML code
  246.      */
  247.     public String getPluginDescription( HttpServletRequest request )
  248.     {
  249.         String strPluginName = request.getParameter( PARAM_PLUGIN_NAME );
  250.         Plugin plugin;
  251.         if ( PluginService.getCore( ).getName( ).equals( strPluginName ) )
  252.         {
  253.             plugin = PluginService.getCore( );
  254.         }
  255.         else
  256.         {
  257.             plugin = PluginService.getPlugin( strPluginName );
  258.         }

  259.         // set the locale for the feature labels
  260.         I18nService.localizeCollection( plugin.getRights( ), getLocale( ) );
  261.         // set the locale for the portlet types labels
  262.         I18nService.localizeCollection( plugin.getPortletTypes( ), getLocale( ) );
  263.         // set the locale for the link services labels
  264.         I18nService.localizeCollection( plugin.getInsertServices( ), getLocale( ) );

  265.         HtmlTemplate template = AppTemplateService.getTemplate( TEMPLATE_PLUGIN_DETAILS, getLocale( ), plugin );

  266.         return getAdminPage( template.getHtml( ) );
  267.     }

  268.     // ////////////////////////////////////////////////////////////////////////////////////////////////////////
  269.     // Private implementation

  270.     /**
  271.      * Return a filter list of plugins
  272.      *
  273.      * @param listPlugins
  274.      *            the COllection of plugins
  275.      * @param strPluginTypeFilter
  276.      *            The filter
  277.      * @return list The list of plugins
  278.      */
  279.     private Collection<Plugin> filterPluginsList( Collection<Plugin> listPlugins, String strPluginTypeFilter )
  280.     {
  281.         Collection<Plugin> list = new ArrayList<>( );
  282.         if ( strPluginTypeFilter == null )
  283.         {
  284.             return listPlugins;
  285.         }

  286.         for ( Plugin plugin : listPlugins )
  287.         {
  288.             boolean filter = false;
  289.             if ( strPluginTypeFilter.equals( PARAM_PLUGIN_TYPE_APPLICATION ) )
  290.             {
  291.                 // skip this plugin
  292.                 filter = ( plugin.getType( ) & Plugin.PLUGIN_TYPE_APPLICATION ) == 0;
  293.             }
  294.             else
  295.                 if ( strPluginTypeFilter.equals( PARAM_PLUGIN_TYPE_PORTLET ) )
  296.                 {
  297.                     // skip this plugin
  298.                     filter = ( plugin.getType( ) & Plugin.PLUGIN_TYPE_PORTLET ) == 0;
  299.                 }
  300.                 else
  301.                     if ( strPluginTypeFilter.equals( PARAM_PLUGIN_TYPE_FEATURE ) )
  302.                     {
  303.                         // skip this plugin
  304.                         filter = ( plugin.getType( ) & Plugin.PLUGIN_TYPE_FEATURE ) == 0;
  305.                     }
  306.                     else
  307.                         if ( strPluginTypeFilter.equals( PARAM_PLUGIN_TYPE_INSERTSERVICE ) )
  308.                         {
  309.                             // skip this plugin
  310.                             filter = ( plugin.getType( ) & Plugin.PLUGIN_TYPE_INSERTSERVICE ) == 0;
  311.                         }
  312.                         else
  313.                             if ( strPluginTypeFilter.equals( PARAM_PLUGIN_TYPE_CONTENTSERVICE ) )
  314.                             {
  315.                                 // skip this plugin
  316.                                 filter = ( plugin.getType( ) & Plugin.PLUGIN_TYPE_CONTENTSERVICE ) == 0;
  317.                             }

  318.             if ( !filter )
  319.             {
  320.                 list.add( plugin );
  321.             }
  322.         }
  323.         return list;
  324.     }

  325.     /**
  326.      * Create a ReferenceList containing all Plugin types
  327.      *
  328.      * @param locale
  329.      *            The Locale
  330.      * @return A ReferenceList containing all Plugin types
  331.      */
  332.     private ReferenceList getPluginTypeFilterList( Locale locale )
  333.     {
  334.         ReferenceList list = new ReferenceList( );
  335.         list.addItem( PARAM_PLUGIN_TYPE_ALL, I18nService.getLocalizedString( PROPERTY_PLUGIN_TYPE_NAME_ALL, locale ) );
  336.         list.addItem( PARAM_PLUGIN_TYPE_APPLICATION, I18nService.getLocalizedString( PROPERTY_PLUGIN_TYPE_NAME_APPLICATION, locale ) );
  337.         list.addItem( PARAM_PLUGIN_TYPE_PORTLET, I18nService.getLocalizedString( PROPERTY_PLUGIN_TYPE_NAME_PORTLET, locale ) );
  338.         list.addItem( PARAM_PLUGIN_TYPE_FEATURE, I18nService.getLocalizedString( PROPERTY_PLUGIN_TYPE_NAME_FEATURE, locale ) );
  339.         list.addItem( PARAM_PLUGIN_TYPE_INSERTSERVICE, I18nService.getLocalizedString( PROPERTY_PLUGIN_TYPE_NAME_INSERTSERVICE, locale ) );
  340.         list.addItem( PARAM_PLUGIN_TYPE_CONTENTSERVICE, I18nService.getLocalizedString( PROPERTY_PLUGIN_TYPE_NAME_CONTENTSERVICE, locale ) );

  341.         return list;
  342.     }

  343.     /**
  344.      * Return a list of pools
  345.      *
  346.      * @return listPools the list of pools
  347.      */
  348.     private ReferenceList getPoolsList( )
  349.     {
  350.         ReferenceList listPools = new ReferenceList( );
  351.         listPools.addItem( AppConnectionService.NO_POOL_DEFINED, " " );
  352.         AppConnectionService.getPoolList( listPools );

  353.         return listPools;
  354.     }

  355.     /**
  356.      * Returns the status of the existence of a portlet on the site
  357.      *
  358.      * @param strPluginHomeClass
  359.      *            The home class of the plugin
  360.      * @return The existence status as a boolean
  361.      */
  362.     private boolean isPortletExists( String strPluginHomeClass )
  363.     {
  364.         String strPortletTypeId = PortletTypeHome.getPortletTypeId( strPluginHomeClass );

  365.         return ( PortletTypeHome.getNbPortletTypeByPortlet( strPortletTypeId ) != 0 );
  366.     }

  367.     /**
  368.      * Verify the core compatibility for a plugin
  369.      *
  370.      * @param plugin
  371.      *            The plugin
  372.      * @return true if compatible with the current core version
  373.      */
  374.     private boolean verifyCoreCompatibility( Plugin plugin )
  375.     {
  376.         String strCoreVersion = AppInfo.getVersion( );

  377.         // Remove version qualifier (-SNAPSHOT, -RC-XX, ...)
  378.         int nPos = strCoreVersion.indexOf( '-' );

  379.         if ( nPos > 0 )
  380.         {
  381.             strCoreVersion = strCoreVersion.substring( 0, nPos );
  382.         }

  383.         String [ ] coreVersion = strCoreVersion.split( "\\." );

  384.         String strMinCoreVersion = ( plugin.getMinCoreVersion( ) == null ) ? "" : plugin.getMinCoreVersion( );
  385.         String strMaxCoreVersion = ( plugin.getMaxCoreVersion( ) == null ) ? "" : plugin.getMaxCoreVersion( );

  386.         // test the min core version
  387.         boolean bMin = ( strMinCoreVersion == null ) || strMinCoreVersion.trim( ).equals( "" );

  388.         if ( ( strMinCoreVersion != null ) && !strMinCoreVersion.trim( ).equals( "" ) )
  389.         {
  390.             String [ ] minCoreVersion = strMinCoreVersion.split( "\\." );

  391.             if ( checkCoreMinCompatibility( minCoreVersion, coreVersion ) )
  392.             {
  393.                 AppLogService.debug( "Min core version ok : {}", plugin.getMinCoreVersion( ) );
  394.                 bMin = true;
  395.             }
  396.         }

  397.         // test the max core version
  398.         boolean bMax = ( strMaxCoreVersion == null ) || strMaxCoreVersion.trim( ).equals( "" );

  399.         if ( ( strMaxCoreVersion != null ) && !strMaxCoreVersion.trim( ).equals( "" ) )
  400.         {
  401.             String [ ] maxCoreVersion = strMaxCoreVersion.split( "\\." );

  402.             if ( checkCoreMaxCompatibility( maxCoreVersion, coreVersion ) )
  403.             {
  404.                 AppLogService.debug( "Max core version ok : {}", plugin.getMaxCoreVersion( ) );
  405.                 bMax = true;
  406.             }
  407.         }

  408.         return bMin && bMax;
  409.     }

  410.     /**
  411.      * Checks the compatibility
  412.      *
  413.      * @param minCoreVersion
  414.      *            The min core version
  415.      * @param coreVersion
  416.      *            The current core version
  417.      * @return true if compatible with the current core version
  418.      */
  419.     private boolean checkCoreMinCompatibility( String [ ] minCoreVersion, String [ ] coreVersion )
  420.     {
  421.         for ( int i = 0; i < Math.min( minCoreVersion.length, coreVersion.length ); ++i )
  422.         {
  423.             if ( ( Integer.parseInt( minCoreVersion [i] ) ) < ( Integer.parseInt( coreVersion [i] ) ) )
  424.             {
  425.                 return true;
  426.             }

  427.             if ( ( Integer.parseInt( minCoreVersion [i] ) ) > ( Integer.parseInt( coreVersion [i] ) ) )
  428.             {
  429.                 return false;
  430.             }
  431.         }

  432.         return true; // inclusive
  433.     }

  434.     /**
  435.      * Checks the compatibility
  436.      *
  437.      * @param maxCoreVersion
  438.      *            The max core version
  439.      * @param coreVersion
  440.      *            The current core version
  441.      * @return true if compatible with the current core version
  442.      */
  443.     private boolean checkCoreMaxCompatibility( String [ ] maxCoreVersion, String [ ] coreVersion )
  444.     {
  445.         for ( int i = 0; i < Math.min( maxCoreVersion.length, coreVersion.length ); ++i )
  446.         {
  447.             if ( ( Integer.parseInt( maxCoreVersion [i] ) ) > ( Integer.parseInt( coreVersion [i] ) ) )
  448.             {
  449.                 return true;
  450.             }

  451.             if ( ( Integer.parseInt( maxCoreVersion [i] ) ) < ( Integer.parseInt( coreVersion [i] ) ) )
  452.             {
  453.                 return false;
  454.             }
  455.         }

  456.         return false; // exclusive
  457.     }
  458. }