StyleSheetJspBean.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.stylesheet;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.apache.commons.fileupload.FileItem;
import org.xml.sax.InputSource;
import fr.paris.lutece.portal.business.portalcomponent.PortalComponentHome;
import fr.paris.lutece.portal.business.portlet.PortletType;
import fr.paris.lutece.portal.business.portlet.PortletTypeHome;
import fr.paris.lutece.portal.business.style.Mode;
import fr.paris.lutece.portal.business.style.ModeHome;
import fr.paris.lutece.portal.business.style.Style;
import fr.paris.lutece.portal.business.style.StyleHome;
import fr.paris.lutece.portal.business.stylesheet.StyleSheet;
import fr.paris.lutece.portal.business.stylesheet.StyleSheetHome;
import fr.paris.lutece.portal.service.admin.AccessDeniedException;
import fr.paris.lutece.portal.service.fileupload.FileUploadService;
import fr.paris.lutece.portal.service.i18n.I18nService;
import fr.paris.lutece.portal.service.message.AdminMessage;
import fr.paris.lutece.portal.service.message.AdminMessageService;
import fr.paris.lutece.portal.service.security.SecurityTokenService;
import fr.paris.lutece.portal.service.template.AppTemplateService;
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.admin.AdminFeaturesPageJspBean;
import fr.paris.lutece.portal.web.constants.Messages;
import fr.paris.lutece.portal.web.constants.Parameters;
import fr.paris.lutece.portal.web.upload.MultipartHttpServletRequest;
import fr.paris.lutece.portal.web.util.LocalizedPaginator;
import fr.paris.lutece.util.ReferenceList;
import fr.paris.lutece.util.file.FileUtil;
import fr.paris.lutece.util.html.AbstractPaginator;
import fr.paris.lutece.util.html.HtmlTemplate;
import fr.paris.lutece.util.sort.AttributeComparator;
/**
* This class provides the user interface to manage StyleSheet features
*/
public class StyleSheetJspBean extends AdminFeaturesPageJspBean
{
// //////////////////////////////////////////////////////////////////////////
// Constants
// Right
/**
* Right to manage stylesheets
*/
public static final String RIGHT_MANAGE_STYLESHEET = "CORE_STYLESHEET_MANAGEMENT";
/**
* Serial version UID
*/
private static final long serialVersionUID = 8176263113722225633L;
// Markers
private static final String MARK_MODE_ID = "mode_id";
private static final String MARK_MODE_LIST = "mode_list";
private static final String MARK_STYLESHEET_LIST = "stylesheet_list";
private static final String MARK_STYLE_LIST = "style_list";
private static final String MARK_STYLESHEET = "stylesheet";
private static final String MARK_PAGINATOR = "paginator";
private static final String MARK_NB_ITEMS_PER_PAGE = "nb_items_per_page";
private static final String MARK_PORTAL_COMPONENT_NAME = "portal_component_name";
private static final String MARK_PORTLET_TYPE_NAME = "portlet_type_name";
private static final String MARK_STYLE_DESCRIPTION = "style_description";
// Templates files path
private static final String TEMPLATE_MANAGE_STYLESHEETS = "admin/stylesheet/manage_stylesheets.html";
private static final String TEMPLATE_CREATE_STYLESHEET = "admin/stylesheet/create_stylesheet.html";
private static final String TEMPLATE_MODIFY_STYLESHEET = "admin/stylesheet/modify_stylesheet.html";
private static final String TEMPLATE_STYLE_SELECT_OPTION = "admin/stylesheet/style_select_option.html";
// Properties
private static final String PROPERTY_PATH_XSL = "path.stylesheet";
private static final String PROPERTY_STYLESHEETS_PER_PAGE = "paginator.stylesheet.itemsPerPage";
private static final String MESSAGE_STYLESHEET_ALREADY_EXISTS = "portal.style.message.stylesheetAlreadyExists";
private static final String MESSAGE_STYLESHEET_NOT_VALID = "portal.style.message.stylesheetNotValid";
private static final String MESSAGE_CONFIRM_DELETE_STYLESHEET = "portal.style.message.stylesheetConfirmDelete";
private static final String LABEL_ALL = "portal.util.labelAll";
private static final String JSP_DO_REMOVE_STYLESHEET = "jsp/admin/style/DoRemoveStyleSheet.jsp";
private static final String JSP_REMOVE_STYLE = "RemoveStyle.jsp";
private int _nItemsPerPage;
private String _strCurrentPageIndex;
/**
* Displays the stylesheets list
*
* @return the html code for displaying the stylesheets list
* @param request
* The request
*/
public String getManageStyleSheet( HttpServletRequest request )
{
// Parameters processing
String strModeId = request.getParameter( Parameters.MODE_ID );
strModeId = ( strModeId != null ) ? strModeId : "-1";
int nModeId = Integer.parseInt( strModeId );
ReferenceList listModes = ModeHome.getModes( );
String strComboItem = I18nService.getLocalizedString( LABEL_ALL, getLocale( ) );
listModes.addItem( -1, strComboItem );
List<StyleSheet> listStyleSheets = (List<StyleSheet>) StyleSheetHome.getStyleSheetList( nModeId );
String strSortedAttributeName = request.getParameter( Parameters.SORTED_ATTRIBUTE_NAME );
String strAscSort = null;
if ( strSortedAttributeName != null )
{
strAscSort = request.getParameter( Parameters.SORTED_ASC );
boolean bIsAscSort = Boolean.parseBoolean( strAscSort );
Collections.sort( listStyleSheets, new AttributeComparator( strSortedAttributeName, bIsAscSort ) );
}
int defaultItemsPerPage = AppPropertiesService.getPropertyInt( PROPERTY_STYLESHEETS_PER_PAGE, 50 );
_strCurrentPageIndex = AbstractPaginator.getPageIndex( request, AbstractPaginator.PARAMETER_PAGE_INDEX, _strCurrentPageIndex );
_nItemsPerPage = AbstractPaginator.getItemsPerPage( request, AbstractPaginator.PARAMETER_ITEMS_PER_PAGE, _nItemsPerPage, defaultItemsPerPage );
String strURL = getHomeUrl( request );
if ( strSortedAttributeName != null )
{
strURL += ( "?" + Parameters.SORTED_ATTRIBUTE_NAME + "=" + strSortedAttributeName );
}
if ( strAscSort != null )
{
strURL += ( "&" + Parameters.SORTED_ASC + "=" + strAscSort );
}
LocalizedPaginator<StyleSheet> paginator = new LocalizedPaginator<>( listStyleSheets, _nItemsPerPage, strURL, AbstractPaginator.PARAMETER_PAGE_INDEX,
_strCurrentPageIndex, getLocale( ) );
Map<String, Object> model = new HashMap<>( );
model.put( MARK_MODE_ID, strModeId );
model.put( MARK_NB_ITEMS_PER_PAGE, "" + _nItemsPerPage );
model.put( MARK_PAGINATOR, paginator );
model.put( MARK_STYLESHEET_LIST, paginator.getPageItems( ) );
model.put( MARK_MODE_LIST, listModes );
HtmlTemplate template = AppTemplateService.getTemplate( TEMPLATE_MANAGE_STYLESHEETS, getLocale( ), model );
return getAdminPage( template.getHtml( ) );
}
/**
* Returns the create form of a new stylesheet with the upload field
*
* @param request
* the http request
* @return the html code for the create form of a new stylesheet
*/
public String getCreateStyleSheet( HttpServletRequest request )
{
String strModeId = request.getParameter( Parameters.MODE_ID );
Map<String, Object> model = new HashMap<>( );
model.put( MARK_STYLE_LIST, getStyleList( ) );
model.put( MARK_MODE_LIST, ModeHome.getModes( ) );
model.put( MARK_MODE_ID, strModeId );
model.put( SecurityTokenService.MARK_TOKEN, SecurityTokenService.getInstance( ).getToken( request, TEMPLATE_CREATE_STYLESHEET ) );
HtmlTemplate template = AppTemplateService.getTemplate( TEMPLATE_CREATE_STYLESHEET, getLocale( ), model );
return getAdminPage( template.getHtml( ) );
}
/**
* Processes the creation form of a new stylesheet by recovering the parameters in the http request
*
* @param request
* the http request
* @return The Jsp URL of the process result
* @throws AccessDeniedException
* if the security token is invalid
*/
public String doCreateStyleSheet( HttpServletRequest request ) throws AccessDeniedException
{
StyleSheet stylesheet = new StyleSheet( );
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
String strErrorUrl = getData( multipartRequest, stylesheet );
if ( strErrorUrl != null )
{
return strErrorUrl;
}
if ( !SecurityTokenService.getInstance( ).validate( multipartRequest, TEMPLATE_CREATE_STYLESHEET ) )
{
throw new AccessDeniedException( ERROR_INVALID_TOKEN );
}
// insert in the table stylesheet of the database
StyleSheetHome.create( stylesheet );
// create a local file
localStyleSheetFile( stylesheet );
// Displays the list of the stylesheet files
return getHomeUrl( request );
}
/**
* Reads stylesheet's data
*
* @param multipartRequest
* The request
* @param stylesheet
* The style sheet
* @return An error message URL or null if no error
*/
private String getData( MultipartHttpServletRequest multipartRequest, StyleSheet stylesheet )
{
String strErrorUrl = null;
String strDescription = multipartRequest.getParameter( Parameters.STYLESHEET_NAME );
String strStyleId = multipartRequest.getParameter( Parameters.STYLES );
String strModeId = multipartRequest.getParameter( Parameters.MODE_STYLESHEET );
FileItem fileSource = multipartRequest.getFile( Parameters.STYLESHEET_SOURCE );
byte [ ] baXslSource = fileSource.get( );
String strFilename = FileUploadService.getFileNameOnly( fileSource );
// Mandatory fields
if ( strDescription.equals( "" ) || ( strFilename == null ) || strFilename.equals( "" ) )
{
return AdminMessageService.getMessageUrl( multipartRequest, Messages.MANDATORY_FIELDS, AdminMessage.TYPE_STOP );
}
// test the existence of style or mode already associate with this stylesheet
int nStyleId = Integer.parseInt( strStyleId );
int nModeId = Integer.parseInt( strModeId );
int nCount = StyleSheetHome.getStyleSheetNbPerStyleMode( nStyleId, nModeId );
// Do not create a stylesheet of there is already one
if ( ( nCount >= 1 ) && ( stylesheet.getId( ) == 0 /* creation */ ) )
{
return AdminMessageService.getMessageUrl( multipartRequest, MESSAGE_STYLESHEET_ALREADY_EXISTS, AdminMessage.TYPE_STOP );
}
// Check the XML validity of the XSL stylesheet
if ( isValid( baXslSource ) != null )
{
Object [ ] args = {
isValid( baXslSource )
};
return AdminMessageService.getMessageUrl( multipartRequest, MESSAGE_STYLESHEET_NOT_VALID, args, AdminMessage.TYPE_STOP );
}
stylesheet.setDescription( strDescription );
stylesheet.setStyleId( Integer.parseInt( strStyleId ) );
stylesheet.setModeId( Integer.parseInt( strModeId ) );
stylesheet.setSource( baXslSource );
stylesheet.setFile( strFilename );
return strErrorUrl;
}
/**
* Returns the form to update a stylesheet whose identifer is stored in the http request
*
* @param request
* The http request
* @return The html code
*/
public String getModifyStyleSheet( HttpServletRequest request )
{
String strStyleSheetId = request.getParameter( Parameters.STYLESHEET_ID );
int nId = Integer.parseInt( strStyleSheetId );
Map<String, Object> model = new HashMap<>( );
model.put( MARK_STYLE_LIST, getStyleList( ) );
model.put( MARK_MODE_LIST, ModeHome.getModes( ) );
model.put( MARK_STYLESHEET, StyleSheetHome.findByPrimaryKey( nId ) );
model.put( SecurityTokenService.MARK_TOKEN, SecurityTokenService.getInstance( ).getToken( request, TEMPLATE_MODIFY_STYLESHEET ) );
HtmlTemplate template = AppTemplateService.getTemplate( TEMPLATE_MODIFY_STYLESHEET, getLocale( ), model );
return getAdminPage( template.getHtml( ) );
}
/**
* Return a ReferenceList with id style for code and a concatenation of portal name + portlet type name + style description for name.
*
* @return The {@link ReferenceList}
*/
public ReferenceList getStyleList( )
{
Collection<Style> stylesList = StyleHome.getStylesList( );
ReferenceList stylesListWithLabels = new ReferenceList( );
for ( Style style : stylesList )
{
HashMap<String, Object> model = new HashMap<>( );
model.put( MARK_PORTAL_COMPONENT_NAME, PortalComponentHome.findByPrimaryKey( style.getPortalComponentId( ) ).getName( ) );
PortletType portletType = PortletTypeHome.findByPrimaryKey( style.getPortletTypeId( ) );
model.put( MARK_PORTLET_TYPE_NAME,
( ( portletType != null ) ? ( I18nService.getLocalizedString( portletType.getNameKey( ), getLocale( ) ) ) : "" ) );
model.put( MARK_STYLE_DESCRIPTION, style.getDescription( ) );
HtmlTemplate template = AppTemplateService.getTemplate( TEMPLATE_STYLE_SELECT_OPTION, getLocale( ), model );
stylesListWithLabels.addItem( style.getId( ), template.getHtml( ) );
}
return stylesListWithLabels;
}
/**
* Processes the updating form of a stylesheet whose new parameters are stored in the http request
*
* @param request
* The http request
* @return The Jsp URL of the process result
* @throws AccessDeniedException
* if the security token is invalid
*/
public String doModifyStyleSheet( HttpServletRequest request ) throws AccessDeniedException
{
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
int nId = Integer.parseInt( multipartRequest.getParameter( Parameters.STYLESHEET_ID ) );
StyleSheet stylesheet = StyleSheetHome.findByPrimaryKey( nId );
String strErrorUrl = getData( multipartRequest, stylesheet );
if ( strErrorUrl != null )
{
return strErrorUrl;
}
if ( !SecurityTokenService.getInstance( ).validate( multipartRequest, TEMPLATE_MODIFY_STYLESHEET ) )
{
throw new AccessDeniedException( ERROR_INVALID_TOKEN );
}
// Remove the old local file
removeOldLocalStyleSheet( nId );
// Update the stylesheet in database
StyleSheetHome.update( stylesheet );
// Recreate the local file
localStyleSheetFile( stylesheet );
// Displays the management stylesheet page
return getHomeUrl( request );
}
/**
* Returns the confirm of removing the style whose identifier is in the http request
*
* @param request
* The Http request
* @return the html code for the remove confirmation page
*/
public String getRemoveStyleSheet( HttpServletRequest request )
{
String strId = request.getParameter( Parameters.STYLESHEET_ID );
StyleSheet stylesheet = StyleSheetHome.findByPrimaryKey( Integer.parseInt( strId ) );
Object [ ] args = {
stylesheet.getDescription( )
};
Map<String, Object> parameters = new HashMap<>( );
parameters.put( Parameters.STYLESHEET_ID, strId );
parameters.put( Parameters.STYLE_ID, stylesheet.getStyleId( ) );
parameters.put( SecurityTokenService.PARAMETER_TOKEN, SecurityTokenService.getInstance( ).getToken( request, JSP_DO_REMOVE_STYLESHEET ) );
return AdminMessageService.getMessageUrl( request, MESSAGE_CONFIRM_DELETE_STYLESHEET, args, null, JSP_DO_REMOVE_STYLESHEET, null,
AdminMessage.TYPE_CONFIRMATION, parameters );
}
/**
* Processes the deletion of a stylesheet
*
* @param request
* the http request
* @return The Jsp URL of the process result
* @throws AccessDeniedException
* if the security token is invalid
*/
public String doRemoveStyleSheet( HttpServletRequest request ) throws AccessDeniedException
{
if ( !SecurityTokenService.getInstance( ).validate( request, JSP_DO_REMOVE_STYLESHEET ) )
{
throw new AccessDeniedException( ERROR_INVALID_TOKEN );
}
int nId = Integer.parseInt( request.getParameter( Parameters.STYLESHEET_ID ) );
int nIdStyle = Integer.parseInt( request.getParameter( Parameters.STYLE_ID ) );
StyleSheet stylesheet = StyleSheetHome.findByPrimaryKey( nId );
String strFile = stylesheet.getFile( );
StyleSheetHome.remove( nId );
// removal of the XSL file
int nModeId = stylesheet.getModeId( );
Mode mode = ModeHome.findByPrimaryKey( nModeId );
String strPathStyleSheet = AppPathService.getPath( PROPERTY_PATH_XSL ) + mode.getPath( );
File fileToDelete = new File( strPathStyleSheet, strFile );
FileUtil.deleteFile( fileToDelete );
return JSP_REMOVE_STYLE + "?" + Parameters.STYLE_ID + "=" + nIdStyle;
}
// ////////////////////////////////////////////////////////////////////////////////
// Private implementation
/**
* Use parsing for validate the modify xsl file
*
* @param baXslSource
* The XSL source
* @return the message exception when the validation is false
*/
private String isValid( byte [ ] baXslSource )
{
String strError = null;
try
{
SAXParserFactory factory = SAXParserFactory.newInstance( );
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
SAXParser analyzer = factory.newSAXParser( );
InputSource is = new InputSource( new ByteArrayInputStream( baXslSource ) );
analyzer.getXMLReader( ).parse( is );
}
catch( Exception e )
{
strError = e.getMessage( );
}
return strError;
}
/**
* Create and Update the local download file
*
* @param stylesheet
* The style sheet
*/
private void localStyleSheetFile( StyleSheet stylesheet )
{
int nModeId = stylesheet.getModeId( );
Mode mode = ModeHome.findByPrimaryKey( nModeId );
String strPathStyleSheet = AppPathService.getPath( PROPERTY_PATH_XSL ) + mode.getPath( );
String strFileName = stylesheet.getFile( );
String strFilePath = strPathStyleSheet + strFileName;
File file = new File( strFilePath );
FileUtil.deleteFile( file );
try ( FileOutputStream fos = new FileOutputStream( file ) )
{
fos.write( stylesheet.getSource( ) );
}
catch( IOException e )
{
AppLogService.error( e.getMessage( ), e );
}
}
/**
* remove the xsl file from the tmp directory
*
* @param nId
* the identifier of the file
*/
private void removeOldLocalStyleSheet( int nId )
{
// Remove the file which been modify
StyleSheet stylesheet = StyleSheetHome.findByPrimaryKey( nId );
int nMode = stylesheet.getModeId( );
Mode mode = ModeHome.findByPrimaryKey( nMode );
String strPathStyleSheet = AppPathService.getPath( PROPERTY_PATH_XSL ) + mode.getPath( );
String strOldFileName = stylesheet.getFile( );
String strOldFilePath = strPathStyleSheet + strOldFileName;
File oldFile = new File( strOldFilePath );
FileUtil.deleteFile( oldFile );
}
}