AppointmentDeskJspBean.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.plugins.appointment.modules.desk.web;

import fr.paris.lutece.api.user.User;
import fr.paris.lutece.plugins.appointment.business.appointment.Appointment;
import fr.paris.lutece.plugins.appointment.business.form.Form;
import fr.paris.lutece.plugins.appointment.business.planning.WeekDefinition;
import fr.paris.lutece.plugins.appointment.business.slot.Slot;
import fr.paris.lutece.plugins.appointment.modules.desk.service.AppointmentDeskService;
import fr.paris.lutece.plugins.appointment.modules.desk.util.IncrementSlot;
import fr.paris.lutece.plugins.appointment.modules.desk.util.IncrementingType;
import fr.paris.lutece.plugins.appointment.service.AppointmentResourceIdService;
import fr.paris.lutece.plugins.appointment.service.AppointmentService;
import fr.paris.lutece.plugins.appointment.service.AppointmentUtilities;
import fr.paris.lutece.plugins.appointment.service.CommentService;
import fr.paris.lutece.plugins.appointment.service.FormService;
import fr.paris.lutece.plugins.appointment.service.SlotService;
import fr.paris.lutece.plugins.appointment.service.WeekDefinitionService;
import fr.paris.lutece.plugins.appointment.web.AppointmentFormJspBean;
import fr.paris.lutece.plugins.appointment.web.dto.AppointmentFilterDTO;
import fr.paris.lutece.plugins.appointment.web.dto.AppointmentFormDTO;
import fr.paris.lutece.plugins.appointment.web.dto.CommentDTO;
import fr.paris.lutece.portal.service.util.AppLogService;
import fr.paris.lutece.portal.service.util.AppPropertiesService;
import fr.paris.lutece.portal.service.admin.AccessDeniedException;
import fr.paris.lutece.portal.service.i18n.I18nService;
import fr.paris.lutece.portal.service.mailinglist.AdminMailingListService;
import fr.paris.lutece.portal.service.plugin.Plugin;
import fr.paris.lutece.portal.service.plugin.PluginService;
import fr.paris.lutece.portal.service.rbac.RBACService;
import fr.paris.lutece.portal.util.mvc.admin.annotations.Controller;
import fr.paris.lutece.portal.util.mvc.commons.annotations.Action;
import fr.paris.lutece.portal.util.mvc.commons.annotations.View;
import fr.paris.lutece.util.ReferenceList;
import fr.paris.lutece.util.date.DateUtil;
import net.sf.json.JSONObject;

import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;

/**
 * This class provides the user interface to manage AppointmentDesk features ( manage, create, modify, remove )
 */
@Controller( controllerJsp = "ManageAppointmentDesks.jsp", controllerPath = "jsp/admin/plugins/appointment/modules/desk/", right = AppointmentFormJspBean.RIGHT_MANAGEAPPOINTMENTFORM )
public class AppointmentDeskJspBean extends AbstractManageAppointmentDeskJspBean
{
    /**
     * 
     */
    private static final long serialVersionUID = -2761497602249048780L;

    // Templates
    private static final String TEMPLATE_MANAGE_APPOINTMENTDESKS = "/admin/plugins/appointment/modules/desk/manage_appointmentdesks.html";

    // Parameters
    private static final String PARAMETER_ID_FORM = "id_form";
    private static final String PARAMETER_CONTEXT = "context";
    private static final String PARAMETER_NUMB_DESK = "numb_desk";
    private static final String PARAMETER_DATE_DAY = "day";
    private static final String PARAMETER_DATA = "data";

    private static final String PARAMETER_ENDING_DATE = "ending_date";
    private static final String PARAMETER_STARTING_DATE = "starting_date";
    private static final String PARAMETER_STARTING_TIME = "starting_time";
    private static final String PARAMETER_ENDING_TIME = "ending_time";
    private static final String PARAMETER_INCREMENTING_VALUE = "incrementing_value";
    private static final String PARAMETER_TYPE = "type";
    // Properties for page titles
    private static final String PROPERTY_PAGE_TITLE_MANAGE_APPOINTMENTDESKS = "appointment-desk.manage_appointmentdesks.pageTitle";

    // Markers
    private static final String MARK_LOCALE = "language";
    private static final String MARK_MACRO_LOCALE = "locale";
    private static final String MARK_DATE_DAY = "day";
    private static final String MARK_ID_FORM = "idForm";
    private static final String MARK_LIST_COMMENTS = "list_comments";
    private static final String MARK_LIST_SLOT = "list_slot";
    private static final String MARK_LIST_APPOINTMENT = "list_appointment";
    private static final String MARK_LIST_TYPE = "list_types";
    private static final String MARK_FORM = "appointmentForm";
    private static final String MARK_ACTIVATE_EDIT_MODE = "activateEditMode";
    private static final String MARK_MAILING_LIST = "mailing_list";
    private static final String MARK_CONTEXT = "context";
    private static final String MARK_APPOINTMENT_DESK_ENABLED = "isDeskInstalled";

    // Properties

    // Validations

    // Views
    private static final String VIEW_MANAGE_APPOINTMENTDESKS = "manageAppointmentDesks";

    // Actions
    private static final String ACTION_CLOSE_APPOINTMENTDESK = "closeAppointmentDesk";
    private static final String ACTION_OPEN_APPOINTMENTDESK = "openAppointmentDesk";
    private static final String ACTION_INCREMENT_MAX_CAPACITY = "incrementMaxCapacity";

    // Infos
    private static final String JSON_KEY_ERROR = "error";
    private static final String JSON_KEY_SUCCESS = "success";

    private static final String PROPERTY_MESSAGE_ERROR_PARSING_JSON = "module.appointment.desk.error.parsing.json";
    private static final String PROPERTY_MESSAGE_ERROR_ACCESS_DENIED = "module.appointment.desk.error.access.denied";
    // Session variable to store working values
    private int _nMaxCapacity;
    private String _strContext;

    /**
     * Build the Manage View
     * 
     * @param request
     *            The HTTP request
     * @return The page
     */
    @View( value = VIEW_MANAGE_APPOINTMENTDESKS, defaultView = true )
    public String getManageAppointmentDesks( HttpServletRequest request )
    {
        Plugin moduleAppointmentDesk = getPlugin( );
        String strIdForm = request.getParameter( PARAMETER_ID_FORM );
        int nIdForm = Integer.parseInt( strIdForm );
        String strDayDate = request.getParameter( PARAMETER_DATE_DAY );
        String strContext = request.getParameter( PARAMETER_CONTEXT );
        _strContext = ( StringUtils.isNotEmpty( strContext ) ) ? strContext : _strContext;
        boolean activateEditMode = true;
        LocalDate dateDay = null;
        User user = getUser( );
        if ( !RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE, strIdForm, AppointmentResourceIdService.PERMISSION_MODIFY_ADVANCED_SETTING_FORM,
                user ) )
        {
            activateEditMode = false;
        }
        Form form = FormService.findFormLightByPrimaryKey( nIdForm );
        HashMap<LocalDate, WeekDefinition> mapWeekDefinition = WeekDefinitionService.findAllWeekDefinition( nIdForm );
        if ( StringUtils.isNotEmpty( strDayDate ) )
        {
            Date dateofDay = DateUtil.formatDate( strDayDate, getLocale( ) );
            dateDay = dateofDay.toInstant( ).atZone( ZoneId.systemDefault( ) ).toLocalDate( );
        }
        else
        {
            dateDay = LocalDate.now( );
            strDayDate = DateUtil.getDateString( Date.from( dateDay.atStartOfDay( ).atZone( ZoneId.systemDefault( ) ).toInstant( ) ), getLocale( ) );
        }
        int appointmentDesk = 0;
        List<Slot> listSlot = SlotService.buildListSlot( nIdForm, mapWeekDefinition, dateDay, dateDay );
        if ( !listSlot.isEmpty( ) )
        {

            Slot slot = listSlot.stream( ).max( Comparator.comparing( Slot::getMaxCapacity ) ).orElseThrow( NoSuchElementException::new );
            appointmentDesk = slot.getMaxCapacity( );

        }
        Map<String, Object> model = getModel( );
        java.sql.Date dateSqlDaey = java.sql.Date.valueOf( dateDay );
        List<CommentDTO> listComment = CommentService.buildCommentDTO( CommentService.findListCommentsInclusive( dateSqlDaey, dateSqlDaey, nIdForm ) );

        AppointmentFilterDTO filter = new AppointmentFilterDTO( );
        filter.setIdForm( nIdForm );
        filter.setStartingDateOfSearch( java.sql.Date.valueOf( dateDay ) );
        filter.setEndingDateOfSearch( java.sql.Date.valueOf( dateDay ) );
        filter.setStatus( 0 );
        List<Appointment> listAppt = AppointmentService.findListAppointmentsByFilter( filter );
        _nMaxCapacity = appointmentDesk;

        model.put( MARK_ACTIVATE_EDIT_MODE, activateEditMode );
        model.put( MARK_CONTEXT, _strContext );
        model.put( MARK_LIST_COMMENTS, listComment );
        model.put( PARAMETER_NUMB_DESK, appointmentDesk );
        model.put( MARK_LIST_SLOT, listSlot );
        model.put( MARK_LIST_APPOINTMENT, listAppt );
        model.put( MARK_LOCALE, getLocale( ) );
        model.put( MARK_DATE_DAY, strDayDate );
        model.put( MARK_ID_FORM, nIdForm );
        model.put( MARK_FORM, form );
        model.put( MARK_MACRO_LOCALE, getLocale( ) );
        model.put( MARK_LIST_TYPE, getListTypes( ) );
        model.put( MARK_MAILING_LIST, AdminMailingListService.getMailingLists( getUser( ) ) );
        model.put( AppointmentUtilities.MARK_PERMISSION_ADD_COMMENT, String.valueOf(
                RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE, strIdForm, AppointmentResourceIdService.PERMISSION_ADD_COMMENT_FORM, user ) ) );
        model.put( AppointmentUtilities.MARK_PERMISSION_MODERATE_COMMENT, String.valueOf( RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE, strIdForm,
                AppointmentResourceIdService.PERMISSION_MODERATE_COMMENT_FORM, user ) ) );
        model.put( AppointmentUtilities.MARK_PERMISSION_ACCESS_CODE, user.getAccessCode( ) );
        model.put( MARK_APPOINTMENT_DESK_ENABLED, ( moduleAppointmentDesk != null ) && moduleAppointmentDesk.isInstalled( ) );

        return getPage( PROPERTY_PAGE_TITLE_MANAGE_APPOINTMENTDESKS, TEMPLATE_MANAGE_APPOINTMENTDESKS, model );
    }

    /**
     * Process the data capture form of a new appointmentdesk
     *
     * @param request
     *            The Http Request
     * @return The Jsp URL of the process result
     * @throws AccessDeniedException
     * @throws JsonProcessingException
     * @throws JsonMappingException
     */
    @Action( ACTION_CLOSE_APPOINTMENTDESK )
    public String docloseAppointmentDesk( HttpServletRequest request )
    {
        JSONObject json = new JSONObject( );
        String strJson = request.getParameter( PARAMETER_DATA );
        ObjectMapper mapper = new ObjectMapper( );
        mapper.registerModule( new JavaTimeModule( ) );
        mapper.configure( DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false );
        AppLogService.debug( "appointmentDesk - Received strJson : " + strJson );

        List<Slot> listSlots;
        try
        {
            listSlots = mapper.readValue( strJson, new TypeReference<List<Slot>>( )
            {
            } );

        }
        catch( JsonProcessingException e )
        {

            AppLogService.error( PROPERTY_MESSAGE_ERROR_PARSING_JSON + e.getMessage( ), e );
            json.element( JSON_KEY_ERROR, I18nService.getLocalizedString( PROPERTY_MESSAGE_ERROR_PARSING_JSON, getLocale( ) ) );

            return json.toString( );
        }

        if ( !RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE, Integer.toString( listSlots.get( 0 ).getIdForm( ) ),
                AppointmentResourceIdService.PERMISSION_MODIFY_ADVANCED_SETTING_FORM, (User) getUser( ) ) )
        {
            AppLogService.error( AppointmentResourceIdService.PERMISSION_MODIFY_ADVANCED_SETTING_FORM,
                    new AccessDeniedException( AppointmentResourceIdService.PERMISSION_MODIFY_ADVANCED_SETTING_FORM ) );
            json.element( JSON_KEY_ERROR, I18nService.getLocalizedString( PROPERTY_MESSAGE_ERROR_ACCESS_DENIED, getLocale( ) ) );

            return json.toString( );
        }

        AppointmentDeskService.closeAppointmentDesk( listSlots );

        json.element( JSON_KEY_SUCCESS, JSON_KEY_SUCCESS );
        return json.toString( );

    }

    /**
     * Process the change form of a appointmentdesk
     *
     * @param request
     *            The Http request
     * @return The Jsp URL of the process result
     * @throws AccessDeniedException
     * @throws JsonProcessingException
     * @throws JsonMappingException
     */
    @Action( ACTION_OPEN_APPOINTMENTDESK )
    public String doOpenAppointmentDesk( HttpServletRequest request )
    {
        JSONObject json = new JSONObject( );
        String strJson = request.getParameter( PARAMETER_DATA );
        AppLogService.debug( "appointmentDesk - Received strJson : " + strJson );
        ObjectMapper mapper = new ObjectMapper( );
        mapper.registerModule( new JavaTimeModule( ) );
        mapper.configure( DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false );

        List<Slot> listSlots;
        try
        {

            listSlots = mapper.readValue( strJson, new TypeReference<List<Slot>>( )
            {
            } );

        }
        catch( JsonProcessingException e )
        {

            AppLogService.error( PROPERTY_MESSAGE_ERROR_PARSING_JSON + e.getMessage( ), e );
            json.element( JSON_KEY_ERROR, I18nService.getLocalizedString( PROPERTY_MESSAGE_ERROR_PARSING_JSON, getLocale( ) ) );

            return json.toString( );
        }

        if ( !RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE, Integer.toString( listSlots.get( 0 ).getIdForm( ) ),
                AppointmentResourceIdService.PERMISSION_MODIFY_ADVANCED_SETTING_FORM, (User) getUser( ) ) )
        {
            AppLogService.error( AppointmentResourceIdService.PERMISSION_MODIFY_ADVANCED_SETTING_FORM,
                    new AccessDeniedException( AppointmentResourceIdService.PERMISSION_MODIFY_ADVANCED_SETTING_FORM ) );
            json.element( JSON_KEY_ERROR, I18nService.getLocalizedString( PROPERTY_MESSAGE_ERROR_ACCESS_DENIED, getLocale( ) ) );

            return json.toString( );

        }

        AppointmentDeskService.openAppointmentDesk( listSlots, _nMaxCapacity );

        json.element( JSON_KEY_SUCCESS, JSON_KEY_SUCCESS );
        return json.toString( );

    }

    @Action( ACTION_INCREMENT_MAX_CAPACITY )
    public String doIncrementMaxCapacity( HttpServletRequest request ) throws AccessDeniedException
    {
        IncrementSlot incrementSlot = new IncrementSlot( );
        String strIdForm = request.getParameter( PARAMETER_ID_FORM );
        if ( !RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE, strIdForm, AppointmentResourceIdService.PERMISSION_MODIFY_ADVANCED_SETTING_FORM,
                (User) getUser( ) ) )
        {
            throw new AccessDeniedException( AppointmentResourceIdService.PERMISSION_MODIFY_ADVANCED_SETTING_FORM );
        }
        populate( request, incrementSlot );

        AppointmentDeskService.incrementMaxCapacity( incrementSlot );

        return getManageAppointmentDesks( request );
    }

    /**
     * List of all the available type
     * 
     * @return the list of the type
     */
    private ReferenceList getListTypes( )
    {
        ReferenceList refListType = new ReferenceList( );
        refListType.addItem( IncrementingType.FULLTIME.getValue( ), I18nService.getLocalizedString( IncrementingType.FULLTIME.getKey( ), getLocale( ) ) );
        refListType.addItem( IncrementingType.HALFTIMEMORNING.getValue( ),
                I18nService.getLocalizedString( IncrementingType.HALFTIMEMORNING.getKey( ), getLocale( ) ) );
        refListType.addItem( IncrementingType.HALFTIMEAFTERNOON.getValue( ),
                I18nService.getLocalizedString( IncrementingType.HALFTIMEAFTERNOON.getKey( ), getLocale( ) ) );
        refListType.addItem( IncrementingType.LACE.getValue( ), I18nService.getLocalizedString( IncrementingType.LACE.getKey( ), getLocale( ) ) );

        return refListType;
    }

    /**
     * Populate a bean using parameters in http request
     * 
     * @param request
     *            http request
     * @param bean
     *            bean to populate
     */
    private void populate( HttpServletRequest request, IncrementSlot incrementSlot )
    {

        incrementSlot.setIdForm( Integer.parseInt( request.getParameter( PARAMETER_ID_FORM ) ) );
        incrementSlot.setEndingTime( request.getParameter( PARAMETER_ENDING_TIME ) );
        incrementSlot.setStartingTime( request.getParameter( PARAMETER_STARTING_TIME ) );
        incrementSlot.setIncrementingValue( Integer.parseInt( request.getParameter( PARAMETER_INCREMENTING_VALUE ) ) );
        incrementSlot.setEndingDate( DateUtil.formatDate( request.getParameter( PARAMETER_ENDING_DATE ), getLocale( ) ).toInstant( )
                .atZone( ZoneId.systemDefault( ) ).toLocalDate( ) );

        incrementSlot.setStartingDate( DateUtil.formatDate( request.getParameter( PARAMETER_STARTING_DATE ), getLocale( ) ).toInstant( )
                .atZone( ZoneId.systemDefault( ) ).toLocalDate( ) );

        int type = Integer.parseInt( request.getParameter( PARAMETER_TYPE ) );
        incrementSlot.setType( IncrementingType.valueOf( type ) );
    }
}