EditTicketXPage.java

/*
 * Copyright (c) 2002-2024, 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.plugins.workflow.modules.ticketing.web;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang.StringUtils;

import fr.paris.lutece.plugins.genericattributes.business.Entry;
import fr.paris.lutece.plugins.ticketing.business.search.IndexerActionHome;
import fr.paris.lutece.plugins.ticketing.business.search.TicketIndexer;
import fr.paris.lutece.plugins.ticketing.business.search.TicketIndexerException;
import fr.paris.lutece.plugins.ticketing.business.ticket.Ticket;
import fr.paris.lutece.plugins.ticketing.business.ticket.TicketHome;
import fr.paris.lutece.plugins.ticketing.service.TicketFormService;
import fr.paris.lutece.plugins.ticketing.service.upload.TicketAsynchronousUploadHandler;
import fr.paris.lutece.plugins.ticketing.web.TicketingConstants;
import fr.paris.lutece.plugins.ticketing.web.util.ModelUtils;
import fr.paris.lutece.plugins.ticketing.web.util.TicketIndexerActionUtil;
import fr.paris.lutece.plugins.ticketing.web.util.TicketUtils;
import fr.paris.lutece.plugins.ticketing.web.workflow.WorkflowCapableXPage;
import fr.paris.lutece.plugins.workflow.modules.ticketing.business.ticket.EditableTicket;
import fr.paris.lutece.plugins.workflow.modules.ticketing.service.authentication.EditTicketRequestAuthenticationService;
import fr.paris.lutece.plugins.workflow.modules.ticketing.service.ticket.EditableTicketService;
import fr.paris.lutece.plugins.workflow.modules.ticketing.service.ticket.IEditableTicketService;
import fr.paris.lutece.plugins.workflow.modules.ticketing.utils.TaskEditTicketConstants;
import fr.paris.lutece.plugins.workflow.modules.ticketing.utils.WorkflowTicketingUtils;
import fr.paris.lutece.portal.service.i18n.I18nService;
import fr.paris.lutece.portal.service.message.SiteMessage;
import fr.paris.lutece.portal.service.message.SiteMessageException;
import fr.paris.lutece.portal.service.message.SiteMessageService;
import fr.paris.lutece.portal.service.plugin.Plugin;
import fr.paris.lutece.portal.service.security.UserNotSignedException;
import fr.paris.lutece.portal.service.spring.SpringContextService;
import fr.paris.lutece.portal.service.template.AppTemplateService;
import fr.paris.lutece.portal.service.util.AppLogService;
import fr.paris.lutece.portal.service.util.AppPropertiesService;
import fr.paris.lutece.portal.service.workflow.WorkflowService;
import fr.paris.lutece.portal.web.constants.Messages;
import fr.paris.lutece.portal.web.xpages.XPage;
import fr.paris.lutece.portal.web.xpages.XPageApplication;
import fr.paris.lutece.util.html.HtmlTemplate;

/**
 *
 * This class represents a controller to edit a ticket
 *
 */
public class EditTicketXPage implements XPageApplication
{
    // XPage
    public static final String XPAGE = "editticket";

    /**
     * Generated serial id
     */
    private static final long serialVersionUID = 7677620731962218061L;

    // TEMPLATES
    private static final String TEMPLATE_EDIT_TICKET = "skin/plugins/ticketing/ticket/view_ticket_details.html";

    // Properties
    private static final String PROPERTY_XPAGE_EDIT_TICKET_PAGETITLE = "module.workflow.ticketing.edit_ticket.page_title";
    private static final String PROPERTY_XPAGE_EDIT_TICKET_PATHLABEL = "module.workflow.ticketing.edit_ticket.page_label";
    private static final String PROPERTY_URL_RETURN = "module.workflow.ticketing.task_edit_ticket.url_return";

    // MESSAGES
    private static final String MESSAGE_TICKET_ALREADY_EDITED = "module.workflow.ticketing.edit_ticket.message.ticket_already_edited";
    private static final String MESSAGE_EDITION_COMPLETE = "module.workflow.ticketing.edit_ticket.message.edition_complete";
    private static final String MESSAGE_TICKET_DELETED = "module.workflow.ticketing.externalUserResponse.message.ticket_closed";

    // Marks
    private static final String MARK_SIGNATURE = "signature";
    private static final String MARK_TIMESTAMP = "timestamp";
    private static final String MARK_ID_ACTION = "id_action";

    // Parameters
    private static final String PARAMETER_ACTION = "action";
    private static final String PARAMETER_USER_MESSAGE = "user_message";

    // ACTIONS
    private static final String ACTION_DO_MODIFY_TICKET = "do_modify_ticket";
    private static transient WorkflowService _workflowService = WorkflowService.getInstance( );

    // SERVICES
    private transient IEditableTicketService _editableTicketService = SpringContextService.getBean( EditableTicketService.BEAN_NAME );
    private transient TicketFormService _ticketFormService = SpringContextService.getBean( TicketFormService.BEAN_NAME );

    /**
     * {@inheritDoc}
     */
    @Override
    public XPage getPage( HttpServletRequest request, int nMode, Plugin plugin ) throws UserNotSignedException, SiteMessageException
    {
        XPage page = null;

        String strUrlReturn = AppPropertiesService.getProperty( PROPERTY_URL_RETURN );

        if ( isRequestAuthenticated( request ) )
        {
            String strIdHistory = request.getParameter( TaskEditTicketConstants.PARAMETER_ID_HISTORY );
            String strIdTask = request.getParameter( TaskEditTicketConstants.PARAMETER_ID_TASK );
            String strIdAction = request.getParameter( TicketingConstants.PARAMETER_WORKFLOW_ID_ACTION );

            if ( StringUtils.isNotBlank( strIdHistory ) && StringUtils.isNumeric( strIdHistory ) && StringUtils.isNotBlank( strIdTask )
                    && StringUtils.isNumeric( strIdTask ) && StringUtils.isNotBlank( strIdAction ) && StringUtils.isNumeric( strIdAction ) )
            {
                int nIdHistory = Integer.parseInt( strIdHistory );
                int nIdTask = Integer.parseInt( strIdTask );
                int nIdAction = Integer.parseInt( strIdAction );

                page = getPage( request, nIdHistory, nIdTask, nIdAction, strUrlReturn );
            }
            else
            {
                setSiteMessage( request, Messages.MANDATORY_FIELDS, SiteMessage.TYPE_STOP, strUrlReturn );
            }
        }
        else
        {
            setSiteMessage( request, Messages.USER_ACCESS_DENIED, SiteMessage.TYPE_STOP, strUrlReturn );
        }

        return page;
    }

    /**
     * Get the page
     *
     * @param request
     *            teh request
     * @param nIdHistory
     *            the history id
     * @param nIdTask
     *            the task id
     * @param nIdAction
     *            the action id
     * @param strUrlReturn
     *            the URL to return
     * @return the page
     * @throws SiteMessageException
     *             if there is an exception
     */
    private XPage getPage( HttpServletRequest request, int nIdHistory, int nIdTask, int nIdAction, String strUrlReturn ) throws SiteMessageException
    {
        XPage page = null;

        EditableTicket editableTicket = _editableTicketService.find( nIdHistory, nIdTask );

        if ( ( editableTicket != null ) && !editableTicket.isEdited( ) )
        {
            if ( _editableTicketService.isStateValid( editableTicket, request.getLocale( ) ) )
            {
                try
                {
                    if ( doProcessWorkflowAction( request, nIdAction, editableTicket ) )
                    {
                        // Back to home page
                        setSiteMessage( request, MESSAGE_EDITION_COMPLETE, SiteMessage.TYPE_INFO, strUrlReturn );
                    }
                    else
                    {
                        page = getEditTicketPage( request, editableTicket );
                    }
                }
                catch( RuntimeException e )
                {
                    AppLogService.error( e );
                    setSiteMessage( request, WorkflowCapableXPage.ERROR_WORKFLOW_ACTION_ABORTED, SiteMessage.TYPE_STOP, strUrlReturn );
                }
            }
            else
            {
                setSiteMessage( request, Messages.USER_ACCESS_DENIED, SiteMessage.TYPE_STOP, strUrlReturn );
            }
        }
        else
            if ( editableTicket != null )
            {
                setSiteMessage( request, MESSAGE_TICKET_ALREADY_EDITED, SiteMessage.TYPE_INFO, strUrlReturn );
            }
            else
            {
                setSiteMessage( request, MESSAGE_TICKET_DELETED, SiteMessage.TYPE_INFO, strUrlReturn );
            }

        return page;
    }

    /**
     * Get the page to edit the ticket
     *
     * @param request
     *            the HTTP request
     * @param editableTicket
     *            the editable ticket
     * @return a XPage
     * @throws SiteMessageException
     *             a site message if there is a problem
     */
    private XPage getEditTicketPage( HttpServletRequest request, EditableTicket editableTicket ) throws SiteMessageException
    {
        XPage page = new XPage( );

        List<Integer> listIdEntries = _editableTicketService.buildListIdEntriesToEdit( request, editableTicket.getListEditableTicketFields( ) );

        Ticket ticket = WorkflowTicketingUtils.findTicketByIdHistory( editableTicket.getIdHistory( ) );

        List<Entry> listEntries = TicketFormService.getFilterInputs( ticket.getTicketCategory( ).getId( ), listIdEntries );

        String htmlForm = _ticketFormService.getHtmlForm( listEntries, request.getLocale( ), true, request );
        TicketAsynchronousUploadHandler.getHandler( ).removeSessionFiles( request.getSession( ).getId( ) );

        Map<String, Object> model = new HashMap<>( );
        model.put( TaskEditTicketConstants.MARK_EDITABLE_TICKET, editableTicket );
        model.put( TicketingConstants.MARK_TICKET, ticket );
        model.put( MARK_ID_ACTION, request.getParameter( TicketingConstants.PARAMETER_WORKFLOW_ID_ACTION ) );
        model.put( TaskEditTicketConstants.MARK_ENTRIES_HTML_FORM, htmlForm );
        model.put( MARK_SIGNATURE, request.getParameter( TicketingConstants.PARAMETER_SIGNATURE ) );
        model.put( MARK_TIMESTAMP, request.getParameter( TicketingConstants.PARAMETER_TIMESTAMP ) );

        request.setAttribute( TicketingConstants.ATTRIBUTE_IS_DISPLAY_FRONT, true );

        ModelUtils.storeReadOnlyHtmlResponses( request, model, ticket );

        HtmlTemplate template = AppTemplateService.getTemplate( TEMPLATE_EDIT_TICKET, request.getLocale( ), model );

        page.setTitle( I18nService.getLocalizedString( PROPERTY_XPAGE_EDIT_TICKET_PAGETITLE, request.getLocale( ) ) );
        page.setPathLabel( I18nService.getLocalizedString( PROPERTY_XPAGE_EDIT_TICKET_PATHLABEL, request.getLocale( ) ) );
        page.setContent( template.getHtml( ) );

        return page;
    }

    /**
     * Do process the workflow action
     *
     * @param request
     *            HttpServletRequest
     * @param nIdAction
     *            the action id
     * @param editableTicket
     *            editable ticket
     * @return {@code true} if the action is processed, {@code false} otherwise
     */
    private boolean doProcessWorkflowAction( HttpServletRequest request, int nIdAction, EditableTicket editableTicket )
    {
        boolean bIsActionProccessed = false;
        String strAction = request.getParameter( PARAMETER_ACTION );
        String userMessage = request.getParameter( PARAMETER_USER_MESSAGE );

        if ( StringUtils.isNotBlank( strAction ) && ( ACTION_DO_MODIFY_TICKET.equals( strAction ) ) && StringUtils.isNotBlank( userMessage ) )
        {
            TicketUtils.registerAdminUserFront( request );

            try
            {
                Ticket ticket = WorkflowTicketingUtils.findTicketByIdHistory( editableTicket.getIdHistory( ) );

                _workflowService.doProcessAction( ticket.getId( ), Ticket.TICKET_RESOURCE_TYPE, nIdAction, null, request, request.getLocale( ), false );

                bIsActionProccessed = true;

                // Immediate indexation of the Ticket
                immediateTicketIndexing( ticket.getId( ), request );
            }
            finally
            {
                TicketUtils.unregisterAdminUserFront( request );
            }

        }

        return bIsActionProccessed;
    }

    /**
     * Set the site message
     *
     * @param request
     *            the HTTP request
     * @param strMessage
     *            the message
     * @param nTypeMessage
     *            the message type
     * @param strUrlReturn
     *            the url return
     * @throws SiteMessageException
     *             the site message
     */
    private void setSiteMessage( HttpServletRequest request, String strMessage, int nTypeMessage, String strUrlReturn ) throws SiteMessageException
    {
        if ( StringUtils.isNotBlank( strUrlReturn ) )
        {
            SiteMessageService.setMessage( request, strMessage, nTypeMessage, strUrlReturn );
        }
        else
        {
            SiteMessageService.setMessage( request, strMessage, nTypeMessage );
        }
    }

    /**
     * Checks if the request is authenticated or not
     *
     * @param request
     *            the HTTP request
     * @return {@code true} if the request is authenticated, {@code false} otherwise
     */
    private boolean isRequestAuthenticated( HttpServletRequest request )
    {
        return EditTicketRequestAuthenticationService.getRequestAuthenticator( ).isRequestAuthenticated( request );
    }

    /**
     * Immediate indexation of a Ticket for the Frontoffice
     *
     * @param idTicket
     *            the id of the Ticket to index
     * @param request
     *            the HttpServletRequest
     */
    protected void immediateTicketIndexing( int idTicket, HttpServletRequest request )
    {
        Ticket ticket = TicketHome.findByPrimaryKey( idTicket );
        if ( ticket != null )
        {
            try
            {
                TicketIndexer ticketIndexer = new TicketIndexer( );
                ticketIndexer.indexTicket( ticket );
            }
            catch( TicketIndexerException ticketIndexerException )
            {
                // The indexation of the Ticket fail, we will store the Ticket in the table for the daemon
                IndexerActionHome.create( TicketIndexerActionUtil.createIndexerActionFromTicket( ticket ) );
            }
        }
    }
}