PluginProbe ʕ •ᴥ•ʔ
LatePoint – Calendar Booking Plugin for Appointments and Events / trunk
LatePoint – Calendar Booking Plugin for Appointments and Events vtrunk
5.6.5 5.6.4 5.6.3 5.6.2 5.6.1 5.6.0 5.5.2 5.5.1 5.5.0 5.4.2 trunk 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.1.8 5.1.9 5.1.91 5.1.92 5.1.93 5.1.94 5.2.0 5.2.1 5.2.10 5.2.11 5.2.2 5.2.3 5.2.4 5.2.5 5.2.6 5.2.7 5.2.8 5.2.9 5.3.0 5.3.1 5.3.2 5.4.0 5.4.1
latepoint / lib / controllers / calendars_controller.php
latepoint / lib / controllers Last commit date
activities_controller.php 1 month ago auth_controller.php 3 months ago booking_form_settings_controller.php 3 months ago bookings_controller.php 17 hours ago calendars_controller.php 3 months ago carts_controller.php 17 hours ago controller.php 3 months ago customer_cabinet_controller.php 2 months ago customers_controller.php 17 hours ago dashboard_controller.php 2 months ago default_agent_controller.php 3 months ago events_controller.php 3 months ago form_fields_controller.php 1 week ago integrations_controller.php 3 months ago invoices_controller.php 17 hours ago manage_booking_by_key_controller.php 3 months ago manage_order_by_key_controller.php 3 months ago notifications_controller.php 3 months ago orders_controller.php 17 hours ago pro_controller.php 2 weeks ago process_jobs_controller.php 3 months ago processes_controller.php 1 month ago razorpay_connect_controller.php 1 week ago search_controller.php 3 months ago services_controller.php 3 months ago settings_controller.php 2 months ago steps_controller.php 2 weeks ago stripe_connect_controller.php 1 week ago support_topics_controller.php 3 months ago todos_controller.php 3 months ago transactions_controller.php 17 hours ago wizard_controller.php 1 week ago
calendars_controller.php
433 lines
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) {
3 exit; // Exit if accessed directly.
4 }
5
6
7 if ( ! class_exists( 'OsCalendarsController' ) ) :
8
9
10 class OsCalendarsController extends OsController {
11
12 private $booking;
13
14 function __construct() {
15 parent::__construct();
16 $this->views_folder = LATEPOINT_VIEWS_ABSPATH . 'calendars/';
17 $this->vars['page_header'] = OsMenuHelper::get_menu_items_by_id( 'calendar' );
18 $this->vars['breadcrumbs'][] = array(
19 'label' => __( 'Appointments', 'latepoint' ),
20 'link' => OsRouterHelper::build_link( [ 'calendars', 'pending_approval' ] ),
21 );
22 }
23
24 public function delete_blocked_period() {
25
26 if ( filter_var( $this->params['id'], FILTER_VALIDATE_INT ) ) {
27 $this->check_nonce( 'delete_blocked_period_' . $this->params['id'] );
28 $off_period = new OsOffPeriodModel( $this->params['id'] );
29 if ( $off_period->delete() ) {
30 $status = LATEPOINT_STATUS_SUCCESS;
31 $response_html = __( 'Blocked Period Removed', 'latepoint' );
32 } else {
33 $status = LATEPOINT_STATUS_ERROR;
34 $response_html = __( 'Error Removing Blocked Period', 'latepoint' );
35 }
36 } else {
37 $status = LATEPOINT_STATUS_ERROR;
38 $response_html = __( 'Error Removing Blocked Period', 'latepoint' );
39 }
40 if ( $this->get_return_format() == 'json' ) {
41 $this->send_json(
42 array(
43 'status' => $status,
44 'message' => $response_html,
45 )
46 );
47 }
48 }
49
50 public function apply_period_block() {
51 $blocked_period_settings = $this->params['blocked_period_settings'];
52 $blocked_period_id = $blocked_period_settings['id'] ?? false;
53 $this->check_nonce( 'save_custom_day_schedule_' . ( $blocked_period_id ? $blocked_period_id : 'new' ) );
54
55 try {
56 $day_date = new OsWpDateTime( $blocked_period_settings['date'] );
57 if ( $blocked_period_settings['full_day_off'] == 'yes' ) {
58
59 $work_period_obj = new OsWorkPeriodModel();
60
61 $work_period_obj->custom_date = $day_date->format( 'Y-m-d' );
62 $work_period_obj->week_day = $day_date->format( 'N' );
63
64 $work_period_obj->agent_id = $blocked_period_settings['agent_id'];
65 $work_period_obj->service_id = $blocked_period_settings['service_id'];
66 $work_period_obj->location_id = $blocked_period_settings['location_id'];
67
68 $work_period_obj->start_time = 0;
69 $work_period_obj->end_time = 0;
70
71 if ( $work_period_obj->save() ) {
72 // delete old blocked period
73 if ( $blocked_period_id ) {
74 $blocked_period = new OsOffPeriodModel( $blocked_period_id );
75 if ( ! $blocked_period->is_new_record() ) {
76 $blocked_period->delete();
77 }
78 }
79 $status = LATEPOINT_STATUS_SUCCESS;
80 $response_html = sprintf( esc_html__( '%s marked as a day off', 'latepoint' ), OsTimeHelper::get_readable_date( $day_date ) );
81 } else {
82 throw new Exception( implode( ',', $work_period_obj->get_error_messages() ?? esc_html__( 'Error', 'latepoint' ) ) );
83 }
84 } else {
85 $blocked_period = $blocked_period_id ? new OsOffPeriodModel( $blocked_period_id ) : new OsOffPeriodModel();
86 $blocked_period->summary = sanitize_text_field( $blocked_period_settings['summary'] ?? esc_html__( 'Off Duty', 'latepoint' ) );
87 $blocked_period->start_date = $day_date->format( 'Y-m-d' );
88 $blocked_period->end_date = $day_date->format( 'Y-m-d' );
89
90 $work_periods = $this->params['work_periods'] ?? [];
91 if ( empty( $work_periods ) ) {
92 throw new Exception( esc_html__( 'Invalid period', 'latepoint' ) );
93 }
94 $work_period = reset( $work_periods );
95
96 $start_ampm = $work_period['start_time']['ampm'] ?? false;
97 $end_ampm = $work_period['end_time']['ampm'] ?? false;
98
99 $blocked_period->start_time = OsTimeHelper::convert_time_to_minutes( $work_period['start_time']['formatted_value'], $start_ampm );
100 $blocked_period->end_time = OsTimeHelper::convert_time_to_minutes( $work_period['end_time']['formatted_value'], $end_ampm );
101
102 $blocked_period->agent_id = $blocked_period_settings['agent_id'];
103 $blocked_period->service_id = $blocked_period_settings['service_id'];
104 $blocked_period->location_id = $blocked_period_settings['location_id'];
105
106
107 if ( $blocked_period->save() ) {
108 $status = LATEPOINT_STATUS_SUCCESS;
109 $response_html = sprintf( esc_html__( 'Period blocked for %s', 'latepoint' ), OsTimeHelper::get_readable_date( $day_date ) );
110 } else {
111 throw new Exception( implode( ',', $blocked_period->get_error_messages() ?? esc_html__( 'Error', 'latepoint' ) ) );
112 }
113 }
114 } catch ( Exception $e ) {
115 $status = LATEPOINT_STATUS_ERROR;
116 $response_html = $e->getMessage();
117 }
118 wp_send_json(
119 array(
120 'status' => $status,
121 'message' => $response_html,
122 )
123 );
124 }
125
126 public function quick_actions() {
127 if ( ! empty( $this->params['blocked_period_id'] ) ) {
128 $blocked_period = new OsOffPeriodModel( sanitize_text_field( $this->params['blocked_period_id'] ) );
129 if ( $blocked_period->is_new_record() ) {
130 wp_send_json(
131 array(
132 'status' => LATEPOINT_STATUS_ERROR,
133 'message' => __( 'Invalid Blocked Period', 'latepoint' ),
134 )
135 );
136 }
137 $start_date = new OsWpDateTime( $blocked_period->start_date );
138 } else {
139 $blocked_period = new OsOffPeriodModel();
140
141 $start_date = new OsWpDateTime( sanitize_text_field( $this->params['target_date'] ) );
142 $blocked_period->start_date = $start_date->format( 'Y-m-d' );
143 $blocked_period->start_time = sanitize_text_field( $this->params['start_time'] ?? 600 );
144 $blocked_period->agent_id = sanitize_text_field( $this->params['agent_id'] ?? 0 );
145 $blocked_period->service_id = sanitize_text_field( $this->params['service_id'] ?? 0 );
146 $blocked_period->location_id = sanitize_text_field( $this->params['location_id'] ?? 0 );
147
148 }
149
150 $this->vars['blocked_period'] = $blocked_period;
151 $this->vars['start_date'] = $start_date;
152 $this->vars['readable_start_date'] = OsTimeHelper::get_readable_date( $start_date );
153
154 $this->vars['agents_list'] = OsAgentHelper::get_agents_list( true, [], true );
155 $this->vars['services_list'] = OsServiceHelper::get_services_list( true, [], true );
156 $this->vars['locations_list'] = OsLocationHelper::get_locations_list( true, [], true );
157
158 $response_html = $this->render( $this->views_folder . __FUNCTION__, 'none' );
159 wp_send_json(
160 array(
161 'status' => LATEPOINT_STATUS_SUCCESS,
162 'message' => $response_html,
163 )
164 );
165 }
166
167 public function view() {
168 $this->vars['page_header'] = '';
169
170 $today_date = new OsWpDateTime( 'today' );
171
172
173 $services = ( new OsServiceModel() )->filter_allowed_records()->should_be_active()->get_results_as_models();
174 $locations = ( new OsLocationModel() )->filter_allowed_records()->should_be_active()->get_results_as_models();
175 $agents = ( new OsAgentModel() )->filter_allowed_records()->should_be_active()->get_results_as_models();
176
177 // extract groups from services
178 $service_categories = new OsServiceCategoryModel();
179 $service_categories = $service_categories->get_results_as_models();
180 $categorized_services_list = [];
181 // uncategorized
182 $categorized_services_list['category_none'] = [
183 'name' => __( 'Uncategorized', 'latepoint' ),
184 'items' => [],
185 ];
186
187 if ( $service_categories ) {
188 foreach ( $service_categories as $service_category ) {
189 $categorized_services_list[ 'category_' . $service_category->id ] = [
190 'name' => $service_category->name,
191 'items' => [],
192 ];
193 }
194 }
195 foreach ( $services as $service ) {
196 if ( $service->category_id ) {
197 $categorized_services_list[ 'category_' . $service->category_id ]['items'][] = $service;
198 } else {
199 $categorized_services_list['category_none']['items'][] = $service;
200 }
201 }
202
203 $this->vars['categorized_services_list'] = $categorized_services_list;
204
205 $default_calendar_settings = [
206 'view' => OsAuthHelper::get_current_user()->get_wp_user_meta( 'latepoint_calendar_view', 'day' ),
207 'target_date_string' => $today_date->format( 'Y-m-d' ),
208 'show_service_ids' => OsUtilHelper::get_array_of_ids_from_array_of_models( $services ),
209 'show_agent_ids' => OsUtilHelper::get_array_of_ids_from_array_of_models( $agents ),
210 'show_location_ids' => OsUtilHelper::get_array_of_ids_from_array_of_models( $locations ),
211 'overlay_service_availability' => LATEPOINT_VALUE_OFF,
212 'availability_service_id' => '',
213 'selected_agent_id' => '', // used for weekly calendar only
214 ];
215 $calendar_settings = ! empty( $this->params['calendar_settings'] ) ? array_merge( $default_calendar_settings, $this->params['calendar_settings'] ) : $default_calendar_settings;
216 if ( $default_calendar_settings['view'] != $calendar_settings['view'] ) {
217 // update default view for this user
218 OsAuthHelper::get_current_user()->update_wp_user_meta( 'latepoint_calendar_view', $calendar_settings['view'] );
219 }
220
221
222 if ( $agents ) {
223 // show only agents that offer services and locations that are selected to be shown
224 $connected_agent_ids = OsConnectorHelper::get_connected_object_ids(
225 'agent_id',
226 [
227 'service_id' => $calendar_settings['show_service_ids'],
228 'location_id' => $calendar_settings['show_location_ids'],
229 ]
230 );
231 $connected_agents = [];
232 $connected_show_agents_ids = [];
233 foreach ( $agents as $agent ) {
234 if ( ! in_array( $agent->id, $connected_agent_ids ) || ! in_array( $agent->id, $calendar_settings['show_agent_ids'] ) ) {
235 continue;
236 }
237 $connected_agents[] = $agent;
238 $connected_show_agents_ids[] = $agent->id;
239 }
240 $calendar_settings['show_agent_ids'] = $connected_show_agents_ids;
241 $agents = $connected_agents;
242 if ( $agents && ( empty( $calendar_settings['selected_agent_id'] ) || ! in_array( $calendar_settings['selected_agent_id'], $connected_show_agents_ids ) ) ) {
243 $calendar_settings['selected_agent_id'] = $agents[0]->id;
244 }
245 }
246
247 // CHECK IF BOOKABLE RESOURCES EXIST
248 if ( empty( $services ) || empty( $agents ) || empty( $locations ) ) {
249 if ( $this->get_return_format() == 'json' ) {
250 $response_html = $this->render( $this->views_folder . 'missing_resources.php', 'none' );
251 $this->send_json(
252 [
253 'status' => LATEPOINT_STATUS_SUCCESS,
254 'message' => $response_html,
255 ]
256 );
257 } else {
258 $this->format_render( 'missing_resources' );
259 exit();
260 }
261 }
262
263 $selected_service_for_availability = false;
264 if ( $calendar_settings['overlay_service_availability'] == LATEPOINT_VALUE_ON && ! empty( $calendar_settings['availability_service_id'] ) ) {
265 foreach ( $services as $service ) {
266 if ( $service->id == $calendar_settings['availability_service_id'] ) {
267 $selected_service_for_availability = clone $service;
268 break;
269 }
270 }
271 }
272
273
274 if ( $calendar_settings['view'] == 'list' ) {
275 $per_page = OsSettingsHelper::get_number_of_records_per_page();
276 $bookings = new OsBookingModel();
277 $bookings = $bookings->get_upcoming_bookings( $calendar_settings['show_agent_ids'], false, $calendar_settings['show_service_ids'], $calendar_settings['show_location_ids'], $per_page );
278 $this->vars['bookings'] = $bookings;
279
280 $calendar_start = ( new OsWpDateTime( 'now' ) );
281 $calendar_end = false;
282
283 $top_date_label = OsUtilHelper::translate_months( $calendar_start->format( 'F' ), false );
284 } else {
285 switch ( $calendar_settings['view'] ) {
286 case 'day':
287 $calendar_start = new OsWpDateTime( $calendar_settings['target_date_string'] );
288 $calendar_end = new OsWpDateTime( $calendar_settings['target_date_string'] );
289 $this->vars['day_view_calendar_min_height'] = OsSettingsHelper::get_day_calendar_min_height();
290 $prev_target_date = ( new OsWpDateTime( $calendar_settings['target_date_string'] ) )->modify( '-1 day' );
291 $next_target_date = ( new OsWpDateTime( $calendar_settings['target_date_string'] ) )->modify( '+1 day' );
292 $top_date_label = OsUtilHelper::translate_months( $calendar_start->format( 'F j' ), false );
293 break;
294 case 'week':
295 $calendar_start = ( new OsWpDateTime( $calendar_settings['target_date_string'] ) )->modify( 'monday this week' );
296 $calendar_end = ( new OsWpDateTime( $calendar_settings['target_date_string'] ) )->modify( 'sunday this week' );
297 if ( $calendar_start->format( 'M' ) == $calendar_end->format( 'M' ) ) {
298 $top_date_label = OsUtilHelper::translate_months( $calendar_start->format( 'F' ), false ) . ' ' . $calendar_start->format( 'j' ) . ' - ' . $calendar_end->format( 'j' );
299 } else {
300 $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' );
301 }
302 $prev_target_date = ( new OsWpDateTime( $calendar_settings['target_date_string'] ) )->modify( '-7 days' );
303 $next_target_date = ( new OsWpDateTime( $calendar_settings['target_date_string'] ) )->modify( '+7 days' );
304 break;
305 case 'month':
306 $calendar_start = ( new OsWpDateTime( $calendar_settings['target_date_string'] ) )->modify( 'first day of this month' );
307 $calendar_end = ( new OsWpDateTime( $calendar_settings['target_date_string'] ) )->modify( 'last day of this month' );
308 $top_date_label = OsUtilHelper::translate_months( $calendar_start->format( 'F' ), false );
309 $prev_target_date = ( new OsWpDateTime( $calendar_settings['target_date_string'] ) )->modify( 'first day of previous month' );
310 $next_target_date = ( new OsWpDateTime( $calendar_settings['target_date_string'] ) )->modify( 'first day of next month' );
311 break;
312 }
313 $booking_request = new \LatePoint\Misc\BookingRequest();
314 $booking_request->agent_id = $calendar_settings['show_agent_ids'];
315 $booking_request->service_id = $calendar_settings['show_service_ids'];
316 $booking_request->location_id = $calendar_settings['show_location_ids'];
317
318 if ( $selected_service_for_availability ) {
319 $booking_request->service_id = $selected_service_for_availability->id;
320 $booking_request->duration = $selected_service_for_availability->duration; // TODO add capacity and duration select box and POST params if multiple durations in a service
321 $timeblock_interval = $selected_service_for_availability->get_timeblock_interval();
322 } else {
323 $timeblock_interval = OsSettingsHelper::get_default_timeblock_interval();
324 }
325
326
327 $resources = OsResourceHelper::get_resources_grouped_by_day( $booking_request, $calendar_start, $calendar_end, [ 'accessed_from_backend' => true ] );
328
329 // if user wants to overlay availability for a specific service - we need to create a separate set of resources
330 // for the work boundaries, since the original one is only querying that service and not all other "shown" services,
331 // we want to show start and work time for all shown services, not just the selected one for the availability overlay
332 // we need to check if there is a single "shown" service - in this case we don't need a separate call for work boundaries
333 if ( count( $calendar_settings['show_service_ids'] ) > 1 && $selected_service_for_availability ) {
334 $booking_request_for_shown_services = clone $booking_request;
335 $booking_request_for_shown_services->service_id = $calendar_settings['show_service_ids'];
336 $resources_for_work_boundaries = OsResourceHelper::get_resources_grouped_by_day( $booking_request_for_shown_services, $calendar_start, $calendar_end );
337 $work_boundaries = OsResourceHelper::get_work_boundaries_for_groups_of_resources( $resources_for_work_boundaries );
338 } else {
339 $work_boundaries = OsResourceHelper::get_work_boundaries_for_groups_of_resources( $resources );
340 }
341
342 $work_time_periods_grouped_by_date_and_agent = [];
343 $bookings_grouped_by_date_and_agent = [];
344 foreach ( $agents as $agent ) {
345 for ( $day_date = clone $calendar_start; $day_date <= $calendar_end; $day_date->modify( '+1 day' ) ) {
346 // fill in all days and agents bookings with blanks
347 $bookings_grouped_by_date_and_agent[ $day_date->format( 'Y-m-d' ) ][ $agent->id ] = [];
348 $work_time_periods_grouped_by_date_and_agent[ $day_date->format( 'Y-m-d' ) ][ $agent->id ] = [];
349 $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' ) ] ] );
350 }
351 }
352 $filter = new \LatePoint\Misc\Filter(
353 [
354 'date_from' => $calendar_start->format( 'Y-m-d' ),
355 'date_to' => $calendar_end->format( 'Y-m-d' ),
356 'service_id' => $calendar_settings['show_service_ids'],
357 'agent_id' => $calendar_settings['show_agent_ids'],
358 'location_id' => $calendar_settings['show_location_ids'],
359 'statuses' => OsCalendarHelper::get_booking_statuses_to_display_on_calendar(),
360 ]
361 );
362 $filter = OsRolesHelper::filter_allowed_records_from_arguments_or_filter( $filter );
363
364 // loop bookings to fill in array
365 $bookings = OsBookingHelper::get_bookings( $filter, true );
366 foreach ( $bookings as $booking ) {
367 $bookings_grouped_by_date_and_agent[ $booking->start_date ][ $booking->agent_id ][] = $booking;
368 }
369
370 // loop resources to fill in work periods array
371 foreach ( $resources as $day => $daily_resources ) {
372 foreach ( $daily_resources as $daily_resource ) {
373 $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 );
374 }
375 }
376
377 $this->vars['bookings_grouped_by_date_and_agent'] = $bookings_grouped_by_date_and_agent;
378 $this->vars['work_boundaries_grouped_by_date_and_agent'] = $work_boundaries_grouped_by_date_and_agent;
379 $this->vars['timeblock_interval'] = $timeblock_interval;
380
381 $this->vars['booking_request'] = $booking_request;
382 $this->vars['work_time_periods_grouped_by_date_and_agent'] = $work_time_periods_grouped_by_date_and_agent;
383 $this->vars['prev_target_date'] = $prev_target_date;
384 $this->vars['next_target_date'] = $next_target_date;
385 $this->vars['resources'] = $resources;
386 $this->vars['work_boundaries'] = $work_boundaries;
387 $this->vars['work_total_minutes'] = $work_boundaries->end_time - $work_boundaries->start_time;
388 }
389
390 $top_date_year = ! empty( $calendar_start ) ? $calendar_start->format( 'Y' ) : '';
391
392 $this->vars['calendar_start'] = $calendar_start;
393 $this->vars['calendar_end'] = $calendar_end;
394
395 $this->vars['date_format'] = OsSettingsHelper::get_readable_date_format( true, true );
396
397 $this->vars['target_date'] = new OsWpDateTime( $calendar_settings['target_date_string'] );
398 $this->vars['today_date'] = $today_date;
399 $this->vars['top_date_label'] = $top_date_label;
400
401 $this->vars['agents'] = $agents;
402 $this->vars['services'] = $services;
403 $this->vars['locations'] = $locations;
404
405 $this->vars['calendar_settings'] = $calendar_settings;
406
407 if ( $this->get_return_format() == 'json' ) {
408 $response_html = $this->render( $this->views_folder . 'scopes/_' . $calendar_settings['view'], 'none' );
409 $this->send_json(
410 [
411 'status' => LATEPOINT_STATUS_SUCCESS,
412 'message' => $response_html,
413 'top_date_label' => $top_date_label,
414 'top_date_year' => $top_date_year,
415 ]
416 );
417 } else {
418 $this->format_render( __FUNCTION__ );
419 }
420 }
421
422
423 public function load_monthly_calendar_days_only() {
424 $target_date = new OsWpDateTime( $this->params['target_date_string'] );
425 $this->vars['target_date'] = $target_date;
426
427 $this->set_layout( 'none' );
428 $this->format_render( __FUNCTION__ );
429 }
430 }
431
432 endif;
433