AdminMenuJspBean.java

/*
 * Copyright (c) 2002-2022, City of Paris
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  1. Redistributions of source code must retain the above copyright notice
 *     and the following disclaimer.
 *
 *  2. Redistributions in binary form must reproduce the above copyright notice
 *     and the following disclaimer in the documentation and/or other materials
 *     provided with the distribution.
 *
 *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
 *     contributors may be used to endorse or promote products derived from
 *     this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * License 1.0
 */
package fr.paris.lutece.portal.web.admin;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import fr.paris.lutece.portal.business.right.FeatureGroup;
import fr.paris.lutece.portal.business.right.FeatureGroupHome;
import fr.paris.lutece.portal.business.right.Right;
import fr.paris.lutece.portal.business.user.AdminUser;
import fr.paris.lutece.portal.business.user.AdminUserHome;
import fr.paris.lutece.portal.business.user.authentication.LuteceDefaultAdminUser;
import fr.paris.lutece.portal.business.user.menu.AccessibilityModeAdminUserMenuItemProvider;
import fr.paris.lutece.portal.business.user.menu.LanguageAdminUserMenuItemProvider;
import fr.paris.lutece.portal.service.admin.AccessDeniedException;
import fr.paris.lutece.portal.service.admin.AdminUserService;
import fr.paris.lutece.portal.service.dashboard.DashboardService;
import fr.paris.lutece.portal.service.dashboard.IDashboardComponent;
import fr.paris.lutece.portal.service.datastore.DatastoreService;
import fr.paris.lutece.portal.service.init.AppInfo;
import fr.paris.lutece.portal.service.message.AdminMessage;
import fr.paris.lutece.portal.service.message.AdminMessageService;
import fr.paris.lutece.portal.service.plugin.Plugin;
import fr.paris.lutece.portal.service.plugin.PluginService;
import fr.paris.lutece.portal.service.portal.PortalService;
import fr.paris.lutece.portal.service.security.SecurityTokenService;
import fr.paris.lutece.portal.service.spring.SpringContextService;
import fr.paris.lutece.portal.service.template.AppTemplateService;
import fr.paris.lutece.portal.service.user.menu.AdminUserMenuService;
import fr.paris.lutece.portal.service.util.AppLogService;
import fr.paris.lutece.portal.service.util.AppPathService;
import fr.paris.lutece.portal.service.util.AppPropertiesService;
import fr.paris.lutece.portal.web.constants.Markers;
import fr.paris.lutece.portal.web.constants.Messages;
import fr.paris.lutece.portal.web.constants.Parameters;
import fr.paris.lutece.portal.web.l10n.LocaleService;
import fr.paris.lutece.util.html.HtmlTemplate;
import fr.paris.lutece.util.password.IPassword;
import fr.paris.lutece.util.password.IPasswordFactory;

/**
 * This class provides the user interface to manage admin features ( manage, create, modify, remove)
 */
public class AdminMenuJspBean implements Serializable
{
    // ///////////////////////////////////////////////////////////////////////////////
    // Constants
    private static final String ERROR_INVALID_TOKEN = "Invalid security token";
    public static final String PROPERTY_LOGOUT_URL = "lutece.admin.logout.url";
    public static final String PROPERTY_MENU_DEFAULT_POS = "top";
    public static final String PROPERTY_MENU_DATASTORE_POS = "portal.site.site_property.menu.position";
    private static final long serialVersionUID = -8939026727319948581L;

    // Markers
    private static final String MARK_FEATURE_GROUP_LIST = "feature_group_list";
    private static final String MARK_USER = "user";
    private static final String MARK_ADMIN_URL = "admin_url";
    private static final String MARK_PROD_BASE_URL = "prod_base_url";
    private static final String MARK_ADMIN_LOGOUT_URL = "admin_logout_url";
    private static final String MARK_SITE_NAME = "site_name";
    private static final String MARK_MENU_POS = "menu_pos";
    private static final String MARK_DASHBOARD_ZONE = "dashboard_zone_";
    private static final String MARK_JAVASCRIPT_FILE = "javascript_file";
    private static final String MARK_JAVASCRIPT_FILES = "javascript_files";
    private static final String MARK_PLUGIN_NAME = "plugin_name";
    private static final String MARK_PLUGINS_LIST = "plugins_list";
    private static final String MARK_ADMIN_AVATAR = "adminAvatar";
    private static final String MARK_MINIMUM_PASSWORD_SIZE = "minimumPasswordSize";
    private static final String MARK_USER_MENU_ITEMS = "userMenuItems";
    private static final String MARK_LIST_LOGGER_INFO = "listLoggersInfo";

    // Templates
    private static final String TEMPLATE_ADMIN_HOME = "admin/user/admin_home.html";
    private static final String TEMPLATE_ADMIN_MENU_HEADER = "admin/user/admin_header.html";
    private static final String TEMPLATE_ADMIN_MENU_FOOTER = "admin/user/admin_footer.html";
    private static final String TEMPLATE_MODIFY_PASSWORD_DEFAULT_MODULE = "admin/user/modify_password_default_module.html";
    private static final String TEMPLATE_STYLESHEET_LINK = "admin/stylesheet_link.html";
    private static final String TEMPLATE_JAVASCRIPT_FILE = "admin/javascript_file.html";

    // Parameter
    private static final String PARAMETER_LANGUAGE = "language";

    // Properties
    private static final String PROPERTY_DEFAULT_FEATURE_ICON = "lutece.admin.feature.default.icon";
    private static final String PROPERTY_DASHBOARD_ZONES = "lutece.dashboard.zones.count";
    private static final int PROPERTY_DASHBOARD_ZONES_DEFAULT = 4;
    private static final String REFERER = "referer";

    // Jsp
    private static final String PROPERTY_JSP_URL_ADMIN_LOGOUT = "lutece.admin.logout.url";
    private static final String MESSAGE_CONTROL_PASSWORD_NO_CORRESPONDING = "portal.users.message.password.confirm.error";
    private static final String PASSWORD_ERROR = "portal.users.message.password.wrong.current";
    private static final String PASSWORD_CURRENT_ERROR = "portal.users.message.password.new.equals.current";
    private static final String MESSAGE_PASSWORD_REDIRECT = "portal.users.message.password.ok.redirect";
    private static final String LOGGER_ACCESS = "lutece.adminaccess";

    private static String _strStylesheets;
    private static boolean _bResetAdminStylesheets;
    private static String _strJavascripts;
    private boolean _bAdminAvatar = PluginService.isPluginEnable( "adminavatar" );
    private static Logger _loggerAccess = LogManager.getLogger( LOGGER_ACCESS );

    /**
     * Returns the Administration header menu
     *
     * @param request
     *            The HttpServletRequest
     * @return The html code of the header
     */
    public String getAdminMenuHeader( HttpServletRequest request )
    {
        Map<String, Object> model = new HashMap<>( );
        String strSiteName = PortalService.getSiteName( );
        AdminUser user = AdminUserService.getAdminUser( request );
        List<FeatureGroup> aFeaturesGroupList = getFeatureGroupsList( user );

        // Displays the menus according to the rights of the users
        model.put( MARK_SITE_NAME, strSiteName );
        model.put( MARK_MENU_POS, DatastoreService.getInstanceDataValue( PROPERTY_MENU_DATASTORE_POS, PROPERTY_MENU_DEFAULT_POS ) );
        model.put( MARK_FEATURE_GROUP_LIST, aFeaturesGroupList );
        model.put( MARK_ADMIN_URL, AppPathService.getBaseUrl( request ) + AppPathService.getAdminMenuUrl( ) );
        model.put( MARK_PROD_BASE_URL, AppPathService.getProdUrl( request ) );
        model.put( MARK_USER, user );
        if ( user.isAdmin( ) )
        {
        	model.put( MARK_LIST_LOGGER_INFO, AppLogService.getLoggersInfo( ) );
        }

        String strLogoutUrl = AppPropertiesService.getProperty( PROPERTY_LOGOUT_URL );
        model.put( MARK_ADMIN_LOGOUT_URL, ( strLogoutUrl == null ) ? "" : strLogoutUrl );

        int nZoneMax = AppPropertiesService.getPropertyInt( PROPERTY_DASHBOARD_ZONES, PROPERTY_DASHBOARD_ZONES_DEFAULT );
        setDashboardData( model, user, request, nZoneMax );

        model.put( MARK_ADMIN_AVATAR, _bAdminAvatar );
        AdminUserMenuService registry = SpringContextService.getBean( AdminUserMenuService.BEAN_NAME );
        model.put( MARK_USER_MENU_ITEMS, registry.getItems( request ) );

        HtmlTemplate template = AppTemplateService.getTemplate( TEMPLATE_ADMIN_MENU_HEADER, user.getLocale( ), model );

        return template.getHtml( );
    }

    /**
     * Returns the Administration footer menu
     *
     * @param request
     *            The HttpServletRequest
     * @return The html code of the header
     */
    public String getAdminMenuFooter( HttpServletRequest request )
    {
        Map<String, Object> model = new HashMap<>( );
        String strFooterVersion = AppInfo.getVersion( );
        String strFooterSiteName = PortalService.getSiteName( );
        AdminUser user = AdminUserService.getAdminUser( request );
        Locale locale = ( user != null ) ? user.getLocale( ) : LocaleService.getDefault( );
        model.put( Markers.VERSION, strFooterVersion );
        model.put( MARK_SITE_NAME, strFooterSiteName );
        model.put( MARK_JAVASCRIPT_FILES, getAdminJavascripts( ) );

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

        traceAdminAccess( request );

        return template.getHtml( );
    }

    /**
     * Returns the html code of the menu of the users
     * 
     * @param request
     *            The Http request
     * @return The html code of the users menu
     */
    public String getAdminMenuUser( HttpServletRequest request )
    {
        AdminUser user = AdminUserService.getAdminUser( request );

        Map<String, Object> model = new HashMap<>( );

        setDashboardData( model, user, request );

        HtmlTemplate template = AppTemplateService.getTemplate( TEMPLATE_ADMIN_HOME, user.getLocale( ), model );

        return template.getHtml( );
    }

    /**
     * Add dashboard data to the template's model
     * 
     * @param model
     *            The template's model
     * @param user
     *            The Admin User
     * @param request
     *            HttpServletRequest
     */
    private void setDashboardData( Map<String, Object> model, AdminUser user, HttpServletRequest request )
    {
        List<IDashboardComponent> listDashboards = DashboardService.getInstance( ).getDashboards( user, request );
        int nZoneMax = AppPropertiesService.getPropertyInt( PROPERTY_DASHBOARD_ZONES, PROPERTY_DASHBOARD_ZONES_DEFAULT );

        if ( CollectionUtils.isNotEmpty( listDashboards ) )
        {
            int nColumnCount = DashboardService.getInstance( ).getColumnCount( );

            // Personnalized dashboards for the nColumnCount first zones
            for ( int i = 1; i <= nColumnCount; i++ )
            {
                model.put( MARK_DASHBOARD_ZONE + i, DashboardService.getInstance( ).getDashboardData( listDashboards, user, i, request ) );
            }

            // Default dashboards for the nColumnCount to nZoneMax zones
            for ( int i = nColumnCount + 1; i < nZoneMax; i++ )
            {
                model.put( MARK_DASHBOARD_ZONE + i, DashboardService.getInstance( ).getDashboardData( user, i, request ) );
            }
        }
        else
        {
            for ( int i = 1; i < nZoneMax; i++ )
            {
                model.put( MARK_DASHBOARD_ZONE + i, DashboardService.getInstance( ).getDashboardData( user, i, request ) );
            }
        }
    }

    /**
     * Add a specific dashboard data to the template's model
     * 
     * @param model
     *            The template's model
     * @param user
     *            The Admin User
     * @param request
     *            HttpServletRequest
     * @param nDashboardZone
     *            the dashboard zone
     */
    private void setDashboardData( Map<String, Object> model, AdminUser user, HttpServletRequest request, int nDashboardZone )
    {
        model.put( MARK_DASHBOARD_ZONE + nDashboardZone, DashboardService.getInstance( ).getDashboardData( user, nDashboardZone, request ) );
    }

    /**
     * Returns an array that contains all feature groups corresponding to the user
     *
     * @param user
     *            The Admin user
     * @return An array of FeatureGroup objects
     */
    private List<FeatureGroup> getFeatureGroupsList( AdminUser user )
    {
        // structure that will be returned
        ArrayList<FeatureGroup> aOutFeatureGroupList = new ArrayList<>( );

        // get the list of user's features
        Map<String, Right> featuresMap = user.getRights( );
        List<Right> features = new ArrayList<>( featuresMap.values( ) );

        List<Right> rightsToDelete = new ArrayList<>( );

        // delete features which have a null URL : these features does not have to be displayed in the menu
        for ( Right right : features )
        {
            if ( right.getUrl( ) == null )
            {
                rightsToDelete.add( right );
            }
        }

        features.removeAll( rightsToDelete );

        Collections.sort( features );

        // for each group, load the features
        for ( FeatureGroup featureGroup : FeatureGroupHome.getFeatureGroupsList( ) )
        {
            ArrayList<Right> aLeftFeatures = new ArrayList<>( );

            for ( Right right : features )
            {
                right.setLocale( user.getLocale( ) );
                right.setIconUrl( getFeatureIcon( right ) );

                String strFeatureGroup = right.getFeatureGroup( );

                if ( featureGroup.getId( ).equalsIgnoreCase( strFeatureGroup ) )
                {
                    featureGroup.addFeature( right );
                }
                else
                {
                    aLeftFeatures.add( right );
                }
            }

            if ( !featureGroup.isEmpty( ) )
            {
                featureGroup.setLocale( user.getLocale( ) );
                aOutFeatureGroupList.add( featureGroup );
            }

            features = aLeftFeatures;
        }

        // add the features with no group to the last group
        if ( CollectionUtils.isNotEmpty( aOutFeatureGroupList ) )
        {
            FeatureGroup lastFeatureGroup = aOutFeatureGroupList.get( aOutFeatureGroupList.size( ) - 1 );

            for ( Right right : features )
            {
                lastFeatureGroup.addFeature( right );
            }
        }

        return aOutFeatureGroupList;
    }

    /**
     * Change the current language of the user
     *
     * @param request
     *            The HTTP request
     * @return The forward Url
     * @throws AccessDeniedException
     *             if the security token is invalid
     */
    public String doChangeLanguage( HttpServletRequest request ) throws AccessDeniedException
    {
        if ( !SecurityTokenService.getInstance( ).validate( request, LanguageAdminUserMenuItemProvider.TEMPLATE ) )
        {
            throw new AccessDeniedException( ERROR_INVALID_TOKEN );
        }
        String strLanguage = request.getParameter( PARAMETER_LANGUAGE );
        AdminUser user = AdminUserService.getAdminUser( request );
        Locale locale = new Locale( strLanguage );
        user.setLocale( locale );
        AppPathService.getBaseUrl( request );

        return AppPathService.getBaseUrl( request ) + AppPathService.getAdminMenuUrl( );
    }

    /**
     * Gets the feature icon
     * 
     * @param right
     *            The right
     * @return The icon
     */
    private String getFeatureIcon( Right right )
    {
        String strIconUrl = AppPropertiesService.getProperty( PROPERTY_DEFAULT_FEATURE_ICON );

        if ( ( right.getIconUrl( ) != null ) && ( !right.getIconUrl( ).equals( "" ) ) )
        {
            strIconUrl = right.getIconUrl( );
        }
        else
        {
            String strPluginName = right.getPluginName( );
            Plugin plugin = PluginService.getPlugin( strPluginName );

            if ( plugin != null )
            {
                strIconUrl = plugin.getIconUrl( );
            }
        }

        return strIconUrl;
    }

    /**
     * Display the modification form for user password. This is used only by the default module. For other modules, custom implementation should be provided.
     * 
     * @param request
     *            the http request
     * @return the form allowing the modification of the user's password
     */
    public String getModifyDefaultAdminUserPassword( HttpServletRequest request )
    {
        AdminUser user = AdminUserService.getAdminUser( request );
        Locale locale = user.getLocale( );
        Map<String, Object> model = new HashMap<>( );
        model.put( MARK_MINIMUM_PASSWORD_SIZE, AdminUserService.getIntegerSecurityParameter( AdminUserService.DSKEY_PASSWORD_MINIMUM_LENGTH ) );
        model.put( SecurityTokenService.MARK_TOKEN, SecurityTokenService.getInstance( ).getToken( request, TEMPLATE_MODIFY_PASSWORD_DEFAULT_MODULE ) );
        HtmlTemplate template = AppTemplateService.getTemplate( TEMPLATE_MODIFY_PASSWORD_DEFAULT_MODULE, locale, model );

        return template.getHtml( );
    }

    /**
     * Perform the user password modification. This is used only by the default module. For other modules, custom implementation should be provided.
     * 
     * @param request
     *            the http request
     * @return the form allowing the modification of the user's password
     * @throws AccessDeniedException
     *             if the security token is invalid
     */
    public String doModifyDefaultAdminUserPassword( HttpServletRequest request ) throws AccessDeniedException
    {
        AdminUser user = AdminUserService.getAdminUser( request );

        String strCurrentPassword = request.getParameter( Parameters.PASSWORD_CURRENT );
        String strNewPassword = request.getParameter( Parameters.NEW_PASSWORD );
        String strConfirmNewPassword = request.getParameter( Parameters.CONFIRM_NEW_PASSWORD );

        LuteceDefaultAdminUser userStored = AdminUserHome.findLuteceDefaultAdminUserByPrimaryKey( user.getUserId( ) );

        IPassword password = userStored.getPassword( );

        // Mandatory Fields
        if ( StringUtils.isEmpty( strCurrentPassword ) || StringUtils.isEmpty( strNewPassword ) || StringUtils.isEmpty( strConfirmNewPassword ) )
        {
            return AdminMessageService.getMessageUrl( request, Messages.MANDATORY_FIELDS, AdminMessage.TYPE_STOP );
        }

        // Test the difference between the two fields of new password
        if ( !strNewPassword.equals( strConfirmNewPassword ) )
        {
            return AdminMessageService.getMessageUrl( request, MESSAGE_CONTROL_PASSWORD_NO_CORRESPONDING, AdminMessage.TYPE_STOP );
        }

        String strUrl = AdminUserService.checkPassword( request, strNewPassword, user.getUserId( ) );

        if ( StringUtils.isNotEmpty( strUrl ) )
        {
            return strUrl;
        }

        // Test of the value of the current password
        if ( !password.check( strCurrentPassword ) )
        {
            return AdminMessageService.getMessageUrl( request, PASSWORD_ERROR, AdminMessage.TYPE_STOP );
        }

        // Test of control of difference between the new password and the current one
        if ( strCurrentPassword.equals( strNewPassword ) )
        {
            return AdminMessageService.getMessageUrl( request, PASSWORD_CURRENT_ERROR, AdminMessage.TYPE_STOP );
        }
        if ( !SecurityTokenService.getInstance( ).validate( request, TEMPLATE_MODIFY_PASSWORD_DEFAULT_MODULE ) )
        {
            throw new AccessDeniedException( ERROR_INVALID_TOKEN );
        }

        // Successful tests
        IPasswordFactory passwordFactory = SpringContextService.getBean( IPasswordFactory.BEAN_NAME );
        userStored.setPassword( passwordFactory.getPasswordFromCleartext( strNewPassword ) );
        userStored.setPasswordReset( Boolean.FALSE );
        userStored.setPasswordMaxValidDate( AdminUserService.getPasswordMaxValidDate( ) );
        AdminUserHome.update( userStored );
        AdminUserHome.insertNewPasswordInHistory( userStored.getPassword( ), userStored.getUserId( ) );

        return AdminMessageService.getMessageUrl( request, MESSAGE_PASSWORD_REDIRECT, AppPropertiesService.getProperty( PROPERTY_JSP_URL_ADMIN_LOGOUT ),
                AdminMessage.TYPE_INFO );
    }

    /**
     * Change the mode accessibility
     * 
     * @param request
     *            {@link HttpServletRequest}
     * @return The forward Url
     * @throws AccessDeniedException
     *             if the security token is invalid
     */
    public String doModifyAccessibilityMode( HttpServletRequest request ) throws AccessDeniedException
    {
        if ( !SecurityTokenService.getInstance( ).validate( request, AccessibilityModeAdminUserMenuItemProvider.TEMPLATE ) )
        {
            throw new AccessDeniedException( ERROR_INVALID_TOKEN );
        }
        AdminUser user = AdminUserService.getAdminUser( request );

        if ( user != null )
        {
            boolean bIsAccessible = !user.getAccessibilityMode( );
            user.setAccessibilityMode( bIsAccessible );
            AdminUserHome.update( user );
        }

        String strReferer = request.getHeader( REFERER );

        if ( StringUtils.isNotBlank( strReferer ) )
        {
            return strReferer;
        }

        return AppPathService.getBaseUrl( request ) + AppPathService.getAdminMenuUrl( );
    }

    /**
     * Return the stylesheets block to include in the footer
     * 
     * @return the stylesheets files block to include in the footer
     * @since 5.1
     */
    public String getAdminStyleSheets( )
    {
        loadStylesheets( );
        return _strStylesheets;
    }

    private static synchronized void loadStylesheets( )
    {
        if ( _strStylesheets == null || _bResetAdminStylesheets )
        {
            List<Plugin> listPlugins = new ArrayList<>( );
            listPlugins.add( PluginService.getCore( ) );
            listPlugins.addAll( PluginService.getPluginList( ) );

            Map<String, Object> model = new HashMap<>( );
            model.put( MARK_PLUGINS_LIST, listPlugins );

            _strStylesheets = AppTemplateService.getTemplate( TEMPLATE_STYLESHEET_LINK, LocaleService.getDefault( ), model ).getHtml( );
            _bResetAdminStylesheets = false;
        }
    }

    public static void resetAdminStylesheets( )
    {
        _bResetAdminStylesheets = true;
    }

    /**
     * Return the javascript files block to include in the footer
     * 
     * @return the javascript files block to include in the footer
     * @since 5.1
     */
    private static synchronized String getAdminJavascripts( )
    {
        if ( _strJavascripts == null )
        {
            StringBuilder sbJavascripts = new StringBuilder( );
            List<Plugin> listPlugins = new ArrayList<>( );
            listPlugins.add( PluginService.getCore( ) );
            listPlugins.addAll( PluginService.getPluginList( ) );

            for ( Plugin plugin : listPlugins )
            {
                if ( plugin.getAdminJavascriptFiles( ) != null )
                {
                    for ( String strJavascript : plugin.getAdminJavascriptFiles( ) )
                    {
                        Map<String, Object> model = new HashMap<>( );
                        model.put( MARK_JAVASCRIPT_FILE, strJavascript );
                        model.put( MARK_PLUGIN_NAME, plugin.getName( ) );
                        sbJavascripts.append( AppTemplateService.getTemplate( TEMPLATE_JAVASCRIPT_FILE, LocaleService.getDefault( ), model ).getHtml( ) );
                    }
                }
            }

            _strJavascripts = sbJavascripts.toString( );
        }

        return _strJavascripts;
    }

    /**
     * Trace in a log file URL accessed by the current admin user
     * 
     * @param request
     *            The HTTP request
     */
    private void traceAdminAccess( HttpServletRequest request )
    {
        AdminUser user = AdminUserService.getAdminUser( request );
        if ( user != null )
        {
            StringBuilder sbAccessLog = new StringBuilder( );
            sbAccessLog.append( "USER id:" ).append( user.getUserId( ) ).append( ", name: " ).append( user.getFirstName( ) ).append( " " )
                    .append( user.getLastName( ) ).append( ", ip: " ).append( request.getRemoteAddr( ) ).append( ", url: " ).append( request.getScheme( ) )
                    .append( "://" ).append( request.getServerName( ) ).append( ':' ).append( request.getServerPort( ) ).append( request.getRequestURI( ) );
            String strQuery = request.getQueryString( );
            if ( strQuery != null )
            {
                sbAccessLog.append( "?" ).append( strQuery );
            }
            _loggerAccess.info( sbAccessLog.toString( ) );

        }
    }
}