<?php if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } if ( ! class_exists( 'OsCalendarsController' ) ) : class OsCalendarsController extends OsController { private $booking; function __construct() { parent::__construct(); $this->views_folder = LATEPOINT_VIEWS_ABSPATH . 'calendars/'; $this->vars['page_header'] = OsMenuHelper::get_menu_items_by_id( 'calendar' ); $this->vars['breadcrumbs'][] = array( 'label' => __( 'Appointments', 'latepoint' ), 'link' => OsRouterHelper::build_link( [ 'calendars', 'pending_approval' ] ) ); } public function delete_blocked_period(){ if ( filter_var( $this->params['id'], FILTER_VALIDATE_INT ) ) { $this->check_nonce( 'delete_blocked_period_' . $this->params['id'] ); $off_period = new OsOffPeriodModel( $this->params['id'] ); if ( $off_period->delete() ) { $status = LATEPOINT_STATUS_SUCCESS; $response_html = __( 'Blocked Period Removed', 'latepoint' ); } else { $status = LATEPOINT_STATUS_ERROR; $response_html = __( 'Error Removing Blocked Period', 'latepoint' ); } } else { $status = LATEPOINT_STATUS_ERROR; $response_html = __( 'Error Removing Blocked Period', 'latepoint' ); } if ( $this->get_return_format() == 'json' ) { $this->send_json( array( 'status' => $status, 'message' => $response_html ) ); } } public function apply_period_block() { $blocked_period_settings = $this->params['blocked_period_settings']; $blocked_period_id = $blocked_period_settings['id'] ?? false; $this->check_nonce( 'save_custom_day_schedule_'.($blocked_period_id ? $blocked_period_id : 'new') ); try { $day_date = new OsWpDateTime( $blocked_period_settings['date'] ); if ( $blocked_period_settings['full_day_off'] == 'yes' ) { $work_period_obj = new OsWorkPeriodModel(); $work_period_obj->custom_date = $day_date->format( 'Y-m-d' ); $work_period_obj->week_day = $day_date->format( 'N' ); $work_period_obj->agent_id = $blocked_period_settings['agent_id']; $work_period_obj->service_id = $blocked_period_settings['service_id']; $work_period_obj->location_id = $blocked_period_settings['location_id']; $work_period_obj->start_time = 0; $work_period_obj->end_time = 0; if ( $work_period_obj->save() ) { // delete old blocked period if($blocked_period_id){ $blocked_period = new OsOffPeriodModel($blocked_period_id); if(!$blocked_period->is_new_record()) $blocked_period->delete(); } $status = LATEPOINT_STATUS_SUCCESS; $response_html = sprintf( esc_html__( '%s marked as a day off', 'latepoint' ), OsTimeHelper::get_readable_date( $day_date ) ); } else { throw new Exception( implode( ',', $work_period_obj->get_error_messages() ?? esc_html__( 'Error', 'latepoint' ) ) ); } } else { $blocked_period = $blocked_period_id ? new OsOffPeriodModel($blocked_period_id) : new OsOffPeriodModel(); $blocked_period->summary = sanitize_text_field($blocked_period_settings['summary'] ?? esc_html__('Off Duty', 'latepoint')); $blocked_period->start_date = $day_date->format( 'Y-m-d' ); $blocked_period->end_date = $day_date->format( 'Y-m-d' ); $work_periods = $this->params['work_periods'] ?? []; if ( empty( $work_periods ) ) { throw new Exception( esc_html__( 'Invalid period', 'latepoint' ) ); } $work_period = reset( $work_periods ); $start_ampm = $work_period['start_time']['ampm'] ?? false; $end_ampm = $work_period['end_time']['ampm'] ?? false; $blocked_period->start_time = OsTimeHelper::convert_time_to_minutes( $work_period['start_time']['formatted_value'], $start_ampm ); $blocked_period->end_time = OsTimeHelper::convert_time_to_minutes( $work_period['end_time']['formatted_value'], $end_ampm ); $blocked_period->agent_id = $blocked_period_settings['agent_id']; $blocked_period->service_id = $blocked_period_settings['service_id']; $blocked_period->location_id = $blocked_period_settings['location_id']; if ( $blocked_period->save() ) { $status = LATEPOINT_STATUS_SUCCESS; $response_html = sprintf( esc_html__( 'Period blocked for %s', 'latepoint' ), OsTimeHelper::get_readable_date( $day_date ) ); } else { throw new Exception( implode( ',', $blocked_period->get_error_messages() ?? esc_html__( 'Error', 'latepoint' ) ) ); } } } catch ( Exception $e ) { $status = LATEPOINT_STATUS_ERROR; $response_html = $e->getMessage(); } wp_send_json( array( 'status' => $status, 'message' => $response_html ) ); } public function quick_actions() { if(!empty($this->params['blocked_period_id'])){ $blocked_period = new OsOffPeriodModel(sanitize_text_field($this->params['blocked_period_id'])); if($blocked_period->is_new_record()){ wp_send_json( array( 'status' => LATEPOINT_STATUS_ERROR, 'message' => __('Invalid Blocked Period', 'latepoint') ) ); } $start_date = new OsWpDateTime($blocked_period->start_date); }else{ $blocked_period = new OsOffPeriodModel(); $start_date = new OsWpDateTime(sanitize_text_field( $this->params['target_date'] )); $blocked_period->start_date = $start_date->format( 'Y-m-d' ); $blocked_period->start_time = sanitize_text_field( $this->params['start_time'] ?? 600 ); $blocked_period->agent_id = sanitize_text_field( $this->params['agent_id'] ?? 0 ); $blocked_period->service_id = sanitize_text_field( $this->params['service_id'] ?? 0 ); $blocked_period->location_id = sanitize_text_field( $this->params['location_id'] ?? 0 ); } $this->vars['blocked_period'] = $blocked_period; $this->vars['start_date'] = $start_date; $this->vars['readable_start_date'] = OsTimeHelper::get_readable_date($start_date); $this->vars['agents_list'] = OsAgentHelper::get_agents_list( true, [], true ); $this->vars['services_list'] = OsServiceHelper::get_services_list( true, [], true ); $this->vars['locations_list'] = OsLocationHelper::get_locations_list( true, [], true ); $response_html = $this->render( $this->views_folder . __FUNCTION__, 'none' ); wp_send_json( array( 'status' => LATEPOINT_STATUS_SUCCESS, 'message' => $response_html ) ); } public function view() { $this->vars['page_header'] = ''; $today_date = new OsWpDateTime( 'today' ); $services = ( new OsServiceModel() )->filter_allowed_records()->should_be_active()->get_results_as_models(); $locations = ( new OsLocationModel() )->filter_allowed_records()->should_be_active()->get_results_as_models(); $agents = ( new OsAgentModel() )->filter_allowed_records()->should_be_active()->get_results_as_models(); // extract groups from services $service_categories = new OsServiceCategoryModel(); $service_categories = $service_categories->get_results_as_models(); $categorized_services_list = []; // uncategorized $categorized_services_list['category_none'] = [ 'name' => __( 'Uncategorized', 'latepoint' ), 'items' => [] ]; if ( $service_categories ) { foreach ( $service_categories as $service_category ) { $categorized_services_list[ 'category_' . $service_category->id ] = [ 'name' => $service_category->name, 'items' => [] ]; } } foreach ( $services as $service ) { if ( $service->category_id ) { $categorized_services_list[ 'category_' . $service->category_id ]['items'][] = $service; } else { $categorized_services_list['category_none']['items'][] = $service; } } $this->vars['categorized_services_list'] = $categorized_services_list; $default_calendar_settings = [ 'view' => OsAuthHelper::get_current_user()->get_wp_user_meta( 'latepoint_calendar_view', 'day' ), 'target_date_string' => $today_date->format( 'Y-m-d' ), 'show_service_ids' => OsUtilHelper::get_array_of_ids_from_array_of_models( $services ), 'show_agent_ids' => OsUtilHelper::get_array_of_ids_from_array_of_models( $agents ), 'show_location_ids' => OsUtilHelper::get_array_of_ids_from_array_of_models( $locations ), 'overlay_service_availability' => LATEPOINT_VALUE_OFF, 'availability_service_id' => '', 'selected_agent_id' => '' // used for weekly calendar only ]; $calendar_settings = ! empty( $this->params['calendar_settings'] ) ? array_merge( $default_calendar_settings, $this->params['calendar_settings'] ) : $default_calendar_settings; if ( $default_calendar_settings['view'] != $calendar_settings['view'] ) { // update default view for this user OsAuthHelper::get_current_user()->update_wp_user_meta( 'latepoint_calendar_view', $calendar_settings['view'] ); } if ( $agents ) { // show only agents that offer services and locations that are selected to be shown $connected_agent_ids = OsConnectorHelper::get_connected_object_ids( 'agent_id', [ 'service_id' => $calendar_settings['show_service_ids'], 'location_id' => $calendar_settings['show_location_ids'] ] ); $connected_agents = []; $connected_show_agents_ids = []; foreach ( $agents as $agent ) { if ( ! in_array( $agent->id, $connected_agent_ids ) || ! in_array( $agent->id, $calendar_settings['show_agent_ids'] ) ) { continue; } $connected_agents[] = $agent; $connected_show_agents_ids[] = $agent->id; } $calendar_settings['show_agent_ids'] = $connected_show_agents_ids; $agents = $connected_agents; if ( $agents && ( empty( $calendar_settings['selected_agent_id'] ) || ! in_array( $calendar_settings['selected_agent_id'], $connected_show_agents_ids ) ) ) { $calendar_settings['selected_agent_id'] = $agents[0]->id; } } // CHECK IF BOOKABLE RESOURCES EXIST if ( empty( $services ) || empty( $agents ) || empty( $locations ) ) { if ( $this->get_return_format() == 'json' ) { $response_html = $this->render( $this->views_folder . 'missing_resources.php', 'none' ); $this->send_json( [ 'status' => LATEPOINT_STATUS_SUCCESS, 'message' => $response_html ] ); } else { $this->format_render( 'missing_resources' ); exit(); } } $selected_service_for_availability = false; if ( $calendar_settings['overlay_service_availability'] == LATEPOINT_VALUE_ON && ! empty( $calendar_settings['availability_service_id'] ) ) { foreach ( $services as $service ) { if ( $service->id == $calendar_settings['availability_service_id'] ) { $selected_service_for_availability = clone $service; break; } } } if ( $calendar_settings['view'] == 'list' ) { $per_page = OsSettingsHelper::get_number_of_records_per_page(); $bookings = new OsBookingModel(); $bookings = $bookings->get_upcoming_bookings( $calendar_settings['show_agent_ids'], false, $calendar_settings['show_service_ids'], $calendar_settings['show_location_ids'], $per_page ); $this->vars['bookings'] = $bookings; $calendar_start = ( new OsWpDateTime( 'now' ) ); $calendar_end = false; $top_date_label = OsUtilHelper::translate_months( $calendar_start->format( 'F' ), false ); } else { switch ( $calendar_settings['view'] ) { case 'day': $calendar_start = new OsWpDateTime( $calendar_settings['target_date_string'] ); $calendar_end = new OsWpDateTime( $calendar_settings['target_date_string'] ); $this->vars['day_view_calendar_min_height'] = OsSettingsHelper::get_day_calendar_min_height(); $prev_target_date = ( new OsWpDateTime( $calendar_settings['target_date_string'] ) )->modify( '-1 day' ); $next_target_date = ( new OsWpDateTime( $calendar_settings['target_date_string'] ) )->modify( '+1 day' ); $top_date_label = OsUtilHelper::translate_months( $calendar_start->format( 'F j' ), false ); break; case 'week': $calendar_start = ( new OsWpDateTime( $calendar_settings['target_date_string'] ) )->modify( 'monday this week' ); $calendar_end = ( new OsWpDateTime( $calendar_settings['target_date_string'] ) )->modify( 'sunday this week' ); if($calendar_start->format( 'M' ) == $calendar_end->format( 'M' )){ $top_date_label = OsUtilHelper::translate_months( $calendar_start->format( 'F' ), false ) . ' ' . $calendar_start->format( 'j' ) . ' - ' . $calendar_end->format( 'j' ); }else{ $top_date_label = OsUtilHelper::translate_months( $calendar_start->format( 'M' ), false ) . ' ' . $calendar_start->format( 'j' ) . ' - ' . OsUtilHelper::translate_months( $calendar_end->format( 'M' ), false ) . ' ' . $calendar_end->format( 'j' ); } $prev_target_date = ( new OsWpDateTime( $calendar_settings['target_date_string'] ) )->modify( '-7 days' ); $next_target_date = ( new OsWpDateTime( $calendar_settings['target_date_string'] ) )->modify( '+7 days' ); break; case 'month': $calendar_start = ( new OsWpDateTime( $calendar_settings['target_date_string'] ) )->modify( 'first day of this month' ); $calendar_end = ( new OsWpDateTime( $calendar_settings['target_date_string'] ) )->modify( 'last day of this month' ); $top_date_label = OsUtilHelper::translate_months( $calendar_start->format( 'F' ), false ); $prev_target_date = ( new OsWpDateTime( $calendar_settings['target_date_string'] ) )->modify( 'first day of previous month' ); $next_target_date = ( new OsWpDateTime( $calendar_settings['target_date_string'] ) )->modify( 'first day of next month' ); break; } $booking_request = new \LatePoint\Misc\BookingRequest(); $booking_request->agent_id = $calendar_settings['show_agent_ids']; $booking_request->service_id = $calendar_settings['show_service_ids']; $booking_request->location_id = $calendar_settings['show_location_ids']; if ( $selected_service_for_availability ) { $booking_request->service_id = $selected_service_for_availability->id; $booking_request->duration = $selected_service_for_availability->duration; // TODO add capacity and duration select box and POST params if multiple durations in a service $timeblock_interval = $selected_service_for_availability->get_timeblock_interval(); } else { $timeblock_interval = OsSettingsHelper::get_default_timeblock_interval(); } $resources = OsResourceHelper::get_resources_grouped_by_day( $booking_request, $calendar_start, $calendar_end ); // if user wants to overlay availability for a specific service - we need to create a separate set of resources // for the work boundaries, since the original one is only querying that service and not all other "shown" services, // we want to show start and work time for all shown services, not just the selected one for the availability overlay // we need to check if there is a single "shown" service - in this case we don't need a separate call for work boundaries if ( count( $calendar_settings['show_service_ids'] ) > 1 && $selected_service_for_availability ) { $booking_request_for_shown_services = clone $booking_request; $booking_request_for_shown_services->service_id = $calendar_settings['show_service_ids']; $resources_for_work_boundaries = OsResourceHelper::get_resources_grouped_by_day( $booking_request_for_shown_services, $calendar_start, $calendar_end ); $work_boundaries = OsResourceHelper::get_work_boundaries_for_groups_of_resources( $resources_for_work_boundaries ); } else { $work_boundaries = OsResourceHelper::get_work_boundaries_for_groups_of_resources( $resources ); } $work_time_periods_grouped_by_date_and_agent = []; $bookings_grouped_by_date_and_agent = []; foreach ( $agents as $agent ) { for ( $day_date = clone $calendar_start; $day_date <= $calendar_end; $day_date->modify( '+1 day' ) ) { // fill in all days and agents bookings with blanks $bookings_grouped_by_date_and_agent[ $day_date->format( 'Y-m-d' ) ][ $agent->id ] = []; $work_time_periods_grouped_by_date_and_agent[ $day_date->format( 'Y-m-d' ) ][ $agent->id ] = []; $work_boundaries_grouped_by_date_and_agent[ $day_date->format( 'Y-m-d' ) ][ $agent->id ] = OsResourceHelper::get_work_boundaries_for_groups_of_resources( [ $resources[ $day_date->format( 'Y-m-d' ) ] ] ); } } $filter = new \LatePoint\Misc\Filter( [ 'date_from' => $calendar_start->format( 'Y-m-d' ), 'date_to' => $calendar_end->format( 'Y-m-d' ), 'service_id' => $calendar_settings['show_service_ids'], 'agent_id' => $calendar_settings['show_agent_ids'], 'location_id' => $calendar_settings['show_location_ids'], 'statuses' => OsCalendarHelper::get_booking_statuses_to_display_on_calendar() ] ); $filter = OsRolesHelper::filter_allowed_records_from_arguments_or_filter( $filter ); // loop bookings to fill in array $bookings = OsBookingHelper::get_bookings( $filter, true ); foreach ( $bookings as $booking ) { $bookings_grouped_by_date_and_agent[ $booking->start_date ][ $booking->agent_id ][] = $booking; } // loop resources to fill in work periods array foreach ( $resources as $day => $daily_resources ) { foreach ( $daily_resources as $daily_resource ) { $work_time_periods_grouped_by_date_and_agent[ $day ][ $daily_resource->agent_id ] = array_merge( $work_time_periods_grouped_by_date_and_agent[ $day ][ $daily_resource->agent_id ], $daily_resource->work_time_periods ); } } $this->vars['bookings_grouped_by_date_and_agent'] = $bookings_grouped_by_date_and_agent; $this->vars['work_boundaries_grouped_by_date_and_agent'] = $work_boundaries_grouped_by_date_and_agent; $this->vars['timeblock_interval'] = $timeblock_interval; $this->vars['booking_request'] = $booking_request; $this->vars['work_time_periods_grouped_by_date_and_agent'] = $work_time_periods_grouped_by_date_and_agent; $this->vars['prev_target_date'] = $prev_target_date; $this->vars['next_target_date'] = $next_target_date; $this->vars['resources'] = $resources; $this->vars['work_boundaries'] = $work_boundaries; $this->vars['work_total_minutes'] = $work_boundaries->end_time - $work_boundaries->start_time; } $top_date_year = ! empty( $calendar_start ) ? $calendar_start->format( 'Y' ) : ''; $this->vars['calendar_start'] = $calendar_start; $this->vars['calendar_end'] = $calendar_end; $this->vars['date_format'] = OsSettingsHelper::get_readable_date_format( true ); $this->vars['target_date'] = new OsWpDateTime( $calendar_settings['target_date_string'] ); $this->vars['today_date'] = $today_date; $this->vars['top_date_label'] = $top_date_label; $this->vars['agents'] = $agents; $this->vars['services'] = $services; $this->vars['locations'] = $locations; $this->vars['calendar_settings'] = $calendar_settings; if ( $this->get_return_format() == 'json' ) { $response_html = $this->render( $this->views_folder . 'scopes/_' . $calendar_settings['view'], 'none' ); $this->send_json( [ 'status' => LATEPOINT_STATUS_SUCCESS, 'message' => $response_html, 'top_date_label' => $top_date_label, 'top_date_year' => $top_date_year ] ); } else { $this->format_render( __FUNCTION__ ); } } public function load_monthly_calendar_days_only() { $target_date = new OsWpDateTime( $this->params['target_date_string'] ); $this->vars['target_date'] = $target_date; $this->set_layout( 'none' ); $this->format_render( __FUNCTION__ ); } } endif;