PluginProbe ʕ •ᴥ•ʔ
Appointment Booking Plugin – LatePoint | Calendar & Scheduling for WordPress / 5.2.2
Appointment Booking Plugin – LatePoint | Calendar & Scheduling for WordPress v5.2.2
5.6.8 5.6.7 5.6.6 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 / helpers / calendar_helper.php
latepoint / lib / helpers Last commit date
activities_helper.php 1 year ago agent_helper.php 1 year ago auth_helper.php 9 months ago blocks_helper.php 1 year ago booking_helper.php 1 year ago bricks_helper.php 1 year ago bundles_helper.php 1 year ago calendar_helper.php 9 months ago carts_helper.php 1 year ago connector_helper.php 1 year ago csv_helper.php 9 months ago customer_helper.php 9 months ago customer_import_helper.php 9 months ago database_helper.php 9 months ago debug_helper.php 1 year ago defaults_helper.php 1 year ago elementor_helper.php 1 year ago email_helper.php 9 months ago encrypt_helper.php 1 year ago events_helper.php 1 year ago form_helper.php 9 months ago icalendar_helper.php 1 year ago image_helper.php 1 year ago invoices_helper.php 9 months ago license_helper.php 1 year ago location_helper.php 1 year ago marketing_systems_helper.php 1 year ago meeting_systems_helper.php 1 year ago menu_helper.php 9 months ago meta_helper.php 1 year ago migrations_helper.php 1 year ago money_helper.php 1 year ago notifications_helper.php 9 months ago order_intent_helper.php 9 months ago orders_helper.php 1 year ago otp_helper.php 9 months ago pages_helper.php 1 year ago params_helper.php 1 year ago payments_helper.php 1 year ago price_breakdown_helper.php 1 year ago process_jobs_helper.php 1 year ago processes_helper.php 1 year ago replacer_helper.php 1 year ago resource_helper.php 1 year ago roles_helper.php 9 months ago router_helper.php 1 year ago service_helper.php 1 year ago sessions_helper.php 1 year ago settings_helper.php 9 months ago short_links_systems_helper.php 9 months ago shortcodes_helper.php 9 months ago sms_helper.php 1 year ago steps_helper.php 9 months ago stripe_connect_helper.php 9 months ago styles_helper.php 9 months ago support_topics_helper.php 1 year ago time_helper.php 9 months ago timeline_helper.php 9 months ago transaction_helper.php 1 year ago transaction_intent_helper.php 1 year ago util_helper.php 9 months ago version_specific_updates_helper.php 9 months ago whatsapp_helper.php 1 year ago work_periods_helper.php 1 year ago wp_datetime.php 1 year ago wp_user_helper.php 1 year ago
calendar_helper.php
573 lines
1 <?php
2 /*
3 * Copyright (c) 2022 LatePoint LLC. All rights reserved.
4 */
5
6
7 class OsCalendarHelper {
8
9 public static function generate_dates_and_times_picker(OsBookingModel $booking, OsWpDateTime $target_date, $auto_search = false, array $calendar_settings = []) : string{
10 $is_recurring_supported = apply_filters('latepoint_is_feature_recurring_bookings_on', false);
11 $can_service_be_recurring = $is_recurring_supported && ($booking->service->get_meta_by_key('allow_recurring_bookings') == 'on');
12 ob_start(); ?>
13 <div class="os-dates-and-times-w <?php echo $auto_search ? 'auto-search is-searching' : '' ; ?> calendar-style-<?php echo OsStepsHelper::get_calendar_style(); ?>" data-route="<?php echo esc_attr(OsRouterHelper::build_route_name('steps', 'load_datepicker_month')); ?>" data-allow-recurring="<?php echo $can_service_be_recurring ? 'yes' : 'no'; ?>">
14 <div class="os-dates-w" data-time-pick-style="<?php echo esc_attr(OsStepsHelper::get_time_pick_style()); ?>">
15 <?php if($auto_search){ ?>
16 <div class="os-calendar-searching-info"><?php echo sprintf(esc_html__( 'Searching %s for available dates', 'latepoint' ), '<span></span>'); ?></div>
17 <?php } ?>
18 <div class="os-calendar-while-searching-wrapper">
19 <?php OsCalendarHelper::generate_calendar_for_datepicker_step( \LatePoint\Misc\BookingRequest::create_from_booking_model( $booking ), $target_date, $calendar_settings ); ?>
20 </div>
21 </div>
22
23 <div class="time-selector-w <?php echo OsStepsHelper::hide_unavailable_slots() ? 'hide-not-available-slots' : ''; ?> <?php echo 'time-system-' . esc_attr(OsTimeHelper::get_time_system()); ?> <?php echo ( OsSettingsHelper::is_on( 'show_booking_end_time' ) ) ? 'with-end-time' : 'without-end-time'; ?> style-<?php echo esc_attr(OsStepsHelper::get_time_pick_style()); ?>">
24 <div class="times-header">
25 <div class="th-line"></div>
26 <div class="times-header-label">
27 <?php esc_html_e( 'Pick a slot for', 'latepoint' ); ?> <span></span>
28 <?php do_action( 'latepoint_step_datepicker_appointment_time_header_label', $booking, $calendar_settings ); ?>
29 </div>
30 <div class="th-line"></div>
31 </div>
32 <div class="os-times-w">
33 <div class="timeslots"></div>
34 </div>
35 </div>
36 <?php do_action( 'latepoint_dates_and_times_picker_after', $booking, $target_date, $calendar_settings ); ?>
37 </div>
38 <?php
39 $html = ob_get_clean();
40 return $html;
41 }
42
43 /**
44 * Get list of statuses which should not appear on calendar
45 *
46 * @return array
47 */
48 public static function get_booking_statuses_hidden_from_calendar(): array {
49 $statuses = explode( ',', OsSettingsHelper::get_settings_value( 'calendar_hidden_statuses', '' ) );
50
51 /**
52 * Get list of statuses which bookings should not appear on calendar
53 *
54 * @param {array} $statuses array of status codes that will be hidden from calendar
55 * @returns {array} The filtered array of status codes
56 *
57 * @since 4.7.0
58 * @hook latepoint_get_booking_statuses_hidden_from_calendar
59 *
60 */
61 return apply_filters( 'latepoint_get_booking_statuses_hidden_from_calendar', $statuses );
62 }
63
64
65 /**
66 * Returns an array of booking status codes to be displayed on calendar
67 *
68 * @return {array} The array of statuses
69 */
70 public static function get_booking_statuses_to_display_on_calendar(): array {
71 $hidden_statuses = self::get_booking_statuses_hidden_from_calendar();
72 $all_statuses = OsBookingHelper::get_statuses_list();
73 $eligible_statuses = [];
74 foreach ( $all_statuses as $status_code => $status_label ) {
75 if ( ! in_array( $status_code, $hidden_statuses ) ) {
76 $eligible_statuses[] = $status_code;
77 }
78 }
79
80 /**
81 * Returns an array of booking status codes to be displayed on calendar
82 *
83 * @param {array} array of statuses
84 *
85 * @returns {array} The array of statuses
86 *
87 * @since 4.7.0
88 * @hook latepoint_get_booking_statuses_to_display_on_calendar
89 *
90 */
91 return apply_filters( 'latepoint_get_booking_statuses_to_display_on_calendar', $eligible_statuses );
92 }
93
94 public static function is_external_calendar_enabled( string $external_calendar_code ): bool {
95 return OsSettingsHelper::is_on( 'enable_' . $external_calendar_code );
96 }
97
98 public static function get_list_of_external_calendars( $enabled_only = false ) {
99 $external_calendars = [];
100
101 /**
102 * Returns an array of external calendars
103 *
104 * @param {array} array of calendars
105 * @param {bool} filter to return only calendars that are enabled
106 *
107 * @returns {array} The array of external calendars
108 *
109 * @since 4.7.0
110 * @hook latepoint_list_of_external_calendars
111 *
112 */
113 return apply_filters( 'latepoint_list_of_external_calendars', $external_calendars, $enabled_only );
114 }
115
116
117 /**
118 * @param \LatePoint\Misc\BookingRequest $booking_request
119 * @param DateTime $target_date
120 * @param array $settings
121 *
122 * @return void
123 */
124 public static function generate_calendar_for_datepicker_step( \LatePoint\Misc\BookingRequest $booking_request, DateTime $target_date, array $settings = [] ) {
125 $defaults = [
126 'exclude_booking_ids' => [],
127 'number_of_months_to_preload' => 1,
128 'timezone_name' => false,
129 'layout' => 'classic',
130 'highlight_target_date' => false,
131 'consider_cart_items' => false,
132 'output_target_date_in_header' => false
133 ];
134
135 $settings = OsUtilHelper::merge_default_atts( $defaults, $settings );
136
137 $weekdays = OsBookingHelper::get_weekdays_arr();
138 $today_date = new OsWpDateTime( 'today' );
139
140
141 ?>
142 <div class="os-current-month-label-w calendar-mobile-controls">
143 <div class="os-current-month-label">
144 <div class="current-month">
145 <?php if ( $settings['highlight_target_date'] && $settings['output_target_date_in_header'] ) {
146 echo esc_html( OsTimeHelper::get_nice_date_with_optional_year( $target_date->format( 'Y-m-d' ), false ) );
147 } else {
148 echo esc_html( OsUtilHelper::get_month_name_by_number( $target_date->format( 'n' ) ) );
149 } ?>
150 </div>
151 <div class="current-year"><?php echo esc_html( $target_date->format( 'Y' ) ); ?></div>
152 </div>
153 <div class="os-month-control-buttons-w">
154 <button type="button" class="os-month-prev-btn" data-route="<?php echo esc_attr( OsRouterHelper::build_route_name( 'steps', 'load_datepicker_month' ) ); ?>">
155 <i class="latepoint-icon latepoint-icon-arrow-left"></i></button>
156 <?php if ( $settings['layout'] == 'horizontal' ) {
157 echo '<button class="latepoint-btn latepoint-btn-outline os-month-today-btn" data-year="' . esc_attr( $today_date->format( 'Y' ) ) . '" data-month="' . esc_attr( $today_date->format( 'n' ) ) . '" data-date="' . esc_attr( $today_date->format( 'Y-m-d' ) ) . '">' . esc_html__( 'Today', 'latepoint' ) . '</button>';
158 } ?>
159 <button type="button" class="os-month-next-btn" data-route="<?php echo esc_attr( OsRouterHelper::build_route_name( 'steps', 'load_datepicker_month' ) ); ?>">
160 <i class="latepoint-icon latepoint-icon-arrow-right"></i></button>
161 </div>
162 </div>
163 <?php if ( $settings['layout'] == 'classic' ) { ?>
164 <div class="os-weekdays">
165 <?php
166 $start_of_week = OsSettingsHelper::get_start_of_week();
167
168 // Output the divs for each weekday
169 for ( $i = $start_of_week - 1; $i < $start_of_week - 1 + 7; $i ++ ) {
170 // Calculate the index within the range of 0-6
171 $index = $i % 7;
172
173 // Output the div for the current weekday
174 echo '<div class="weekday weekday-' . esc_attr( $index + 1 ) . '">' . esc_html( mb_substr($weekdays[ $index ], 0, 1) ) . '</div>';
175 }
176 ?>
177 </div>
178 <?php } ?>
179 <div class="os-months">
180 <?php
181 $month_settings = [
182 'active' => true,
183 'timezone_name' => $settings['timezone_name'],
184 'highlight_target_date' => $settings['highlight_target_date'],
185 'exclude_booking_ids' => $settings['exclude_booking_ids'],
186 'consider_cart_items' => $settings['consider_cart_items']
187 ];
188
189 // if it's not from admin - blackout dates that are not available to select due to date restrictions in settings
190 $month_settings['earliest_possible_booking'] = OsSettingsHelper::get_earliest_possible_booking_restriction($booking_request->service_id);
191 $month_settings['latest_possible_booking'] = OsSettingsHelper::get_latest_possible_booking_restriction($booking_request->service_id);
192
193 OsCalendarHelper::generate_single_month( $booking_request, $target_date, $month_settings );
194 for ( $i = 1; $i <= $settings['number_of_months_to_preload']; $i ++ ) {
195 $next_month_target_date = clone $target_date;
196 $next_month_target_date->modify( 'first day of next month' );
197 $month_settings['active'] = false;
198 $month_settings['highlight_target_date'] = false;
199 OsCalendarHelper::generate_single_month( $booking_request, $next_month_target_date, $month_settings );
200 }
201 ?>
202 </div><?php
203 /**
204 * Fired after a datepicker calendar months are generated
205 *
206 * @param {BookingRequest} $booking_request instance of a booking request
207 * @param {DateTime} $target_date target date that is being loaded
208 * @param {array} $settings array of settings for the calendar
209 *
210 * @since 5.1.7
211 * @hook latepoint_after_datepicker_months
212 *
213 */
214 do_action('latepoint_after_datepicker_months', $booking_request, $target_date, $settings);
215 }
216
217 public static function generate_single_month( \LatePoint\Misc\BookingRequest $booking_request, DateTime $target_date, array $settings = [] ) {
218 $defaults = [
219 'accessed_from_backend' => false,
220 'active' => false,
221 'layout' => 'classic',
222 'highlight_target_date' => false,
223 'timezone_name' => false,
224 'earliest_possible_booking' => false,
225 'latest_possible_booking' => false,
226 'exclude_booking_ids' => [],
227 'consider_cart_items' => false,
228 'hide_slot_availability_count' => OsStepsHelper::hide_slot_availability_count()
229 ];
230 $settings = OsUtilHelper::merge_default_atts( $defaults, $settings );
231
232
233 // set service to the first available if not set
234 // IMPORTANT, we have to have service in the booking request, otherwise we can't know duration and intervals
235 $service = new OsServiceModel();
236 $service = $service->where( [ 'id' => $booking_request->service_id ] )->set_limit( 1 )->get_results_as_models();
237 if ( $service ) {
238 if ( ! $booking_request->duration ) {
239 $booking_request->duration = $service->duration;
240 }
241 $selectable_time_interval = $service->get_timeblock_interval();
242 } else {
243 echo '<div class="latepoint-message latepoint-message-error">' . esc_html__( 'In order to generate the calendar, a service must be selected.', 'latepoint' ) . '</div>';
244
245 return;
246 }
247
248
249 # Get bounds for a month of a targetted day
250 $calendar_start = clone $target_date;
251 $calendar_start->modify( 'first day of this month' );
252 $calendar_end = clone $target_date;
253 $calendar_end->modify( 'last day of this month' );
254
255
256 // if it's a classic layout - it means we need to load some days from previous and next month, to fill in blank spaces on the grid
257 if ( $settings['layout'] == 'classic' ) {
258 $weekday_for_first_day_of_month = intval( $calendar_start->format( 'N' ) );
259 $weekday_for_last_day_of_month = intval( $calendar_end->format( 'N' ) );
260
261 $week_starts_on = OsSettingsHelper::get_start_of_week();
262 $week_ends_on = $week_starts_on > 1 ? $week_starts_on - 1 : 7;
263
264 if ( $weekday_for_first_day_of_month != $week_starts_on ) {
265 $days_to_subtract = ( $weekday_for_first_day_of_month - $week_starts_on + 7 ) % 7;
266 if($days_to_subtract > 0){
267 $calendar_start->modify( '-' . $days_to_subtract . ' days' );
268 }
269 }
270
271 if ( $weekday_for_last_day_of_month != $week_ends_on ) {
272 $days_to_add = ( $weekday_for_last_day_of_month > $week_ends_on ) ? abs( 7 - $weekday_for_last_day_of_month + $week_ends_on ) : ( $week_ends_on - $weekday_for_last_day_of_month );
273 if($days_to_add > 0){
274 $calendar_end->modify( '+' . $days_to_add . ' days' );
275 }
276 }
277 }
278
279 $now_datetime = OsTimeHelper::now_datetime_object();
280
281 // figure out when the earliest and latest bookings can be placed
282 try{
283 $earliest_possible_booking = ( $settings['earliest_possible_booking'] ) ? new OsWpDateTime( $settings['earliest_possible_booking'] ) : clone $now_datetime;
284 $latest_possible_booking = ( $settings['latest_possible_booking'] ) ? new OsWpDateTime( $settings['latest_possible_booking'] ) : clone $calendar_end;
285 }catch(Exception $e){
286
287 }
288 // make sure they are set correctly
289 if ( empty($earliest_possible_booking) ) {
290 $earliest_possible_booking = clone $now_datetime;
291 }
292 if ( empty($latest_possible_booking) ) {
293 $latest_possible_booking = clone $calendar_end;
294 }
295
296 $date_range_start = ( $calendar_start->format( 'Y-m-d' ) > $earliest_possible_booking->format( 'Y-m-d' ) ) ? clone $calendar_start : clone $earliest_possible_booking;
297 $date_range_end = ( $calendar_end->format( 'Y-m-d' ) < $latest_possible_booking->format( 'Y-m-d' ) ) ? clone $calendar_end : clone $latest_possible_booking;
298
299 // make sure date range is within the requested calendar range
300 if ( ( $date_range_start->format( 'Y-m-d' ) >= $calendar_start->format( 'Y-m-d' ) )
301 && ( $date_range_end->format( 'Y-m-d' ) <= $calendar_end->format( 'Y-m-d' ) )
302 && ( $date_range_start->format( 'Y-m-d' ) <= $date_range_end->format( 'Y-m-d' ) ) ) {
303 $daily_resources = OsResourceHelper::get_resources_grouped_by_day( $booking_request, $date_range_start, $date_range_end, [
304 'accessed_from_backend' => $settings['accessed_from_backend'],
305 'exclude_booking_ids' => $settings['exclude_booking_ids'],
306 'consider_cart_items' => $settings['consider_cart_items'],
307 'timezone_name' => $settings['timezone_name'],
308 ] );
309 } else {
310 $daily_resources = [];
311 }
312
313 $active_class = $settings['active'] ? 'active' : '';
314 $hide_single_slot_class = OsStepsHelper::hide_timepicker_when_one_slot_available() ? 'hide-if-single-slot' : '';
315 echo '<div class="os-monthly-calendar-days-w ' . esc_attr( $hide_single_slot_class . ' ' . $active_class ) . '" data-calendar-layout="' . esc_attr( $settings['layout'] ) . '" data-calendar-year="' . esc_attr( $target_date->format( 'Y' ) ) . '" data-calendar-month="' . esc_attr( $target_date->format( 'n' ) ) . '" data-calendar-month-label="' . esc_attr( OsUtilHelper::get_month_name_by_number( $target_date->format( 'n' ) ) ) . '">';
316 echo '<div class="os-monthly-calendar-days">';
317 // DAYS LOOP START
318 for ( $day_date = clone $calendar_start; $day_date <= $calendar_end; $day_date->modify( '+1 day' ) ) {
319 if ( ! isset( $daily_resources[ $day_date->format( 'Y-m-d' ) ] ) ) {
320 $daily_resources[ $day_date->format( 'Y-m-d' ) ] = [];
321 }
322
323 $is_today = ( $day_date->format( 'Y-m-d' ) == $now_datetime->format( 'Y-m-d' ) );
324 $is_day_in_past = ( $day_date->format( 'Y-m-d' ) < $now_datetime->format( 'Y-m-d' ) );
325 $is_target_month = ( $day_date->format( 'Ym' ) == $target_date->format( 'Ym' ) );
326 $is_next_month = ( $day_date->format( 'Ym' ) > $target_date->format( 'Ym' ) );
327 $is_prev_month = ( $day_date->format( 'Ym' ) < $target_date->format( 'Ym' ) );
328 $not_in_allowed_period = false;
329
330 if ( $day_date->format( 'Y-m-d' ) < $earliest_possible_booking->format( 'Y-m-d' ) ) {
331 $not_in_allowed_period = true;
332 }
333 if ( $day_date->format( 'Y-m-d' ) > $latest_possible_booking->format( 'Y-m-d' ) ) {
334 $not_in_allowed_period = true;
335 }
336
337 $work_minutes = [];
338
339 if($settings['accessed_from_backend'] || !$not_in_allowed_period ){
340 // only do this if is in allowed period or accessed from backend
341 foreach ( $daily_resources[ $day_date->format( 'Y-m-d' ) ] as $resource ) {
342 if ( $is_day_in_past && $not_in_allowed_period ) {
343 continue;
344 }
345 $work_minutes = array_merge( $work_minutes, $resource->work_minutes );
346 }
347 $work_minutes = array_unique( $work_minutes, SORT_NUMERIC );
348 sort( $work_minutes, SORT_NUMERIC );
349 }
350
351
352
353 $work_boundaries = OsResourceHelper::get_work_boundaries_for_resources( $daily_resources[ $day_date->format( 'Y-m-d' ) ] );
354 $total_work_minutes = $work_boundaries->end_time - $work_boundaries->start_time;
355
356 $booking_slots = OsResourceHelper::get_ordered_booking_slots_from_resources( $daily_resources[ $day_date->format( 'Y-m-d' ) ] );
357
358 $bookable_minutes = [];
359 if($settings['accessed_from_backend'] || !$not_in_allowed_period ) {
360 // only do this if is in allowed period or accessed from backend
361 foreach ( $booking_slots as $booking_slot ) {
362 if ( $booking_slot->can_accomodate( $booking_request->total_attendees ) ) {
363 $bookable_minutes[ $booking_slot->start_time ] = isset( $bookable_minutes[ $booking_slot->start_time ] ) ? max( $booking_slot->available_capacity(), $bookable_minutes[ $booking_slot->start_time ] ) : $booking_slot->available_capacity();
364 }
365 }
366 ksort( $bookable_minutes );
367 }
368 $bookable_minutes_with_capacity_data = '';
369 // this is a group service
370 if ( $service->is_group_service() && ! $settings['hide_slot_availability_count'] ) {
371 foreach ( $bookable_minutes as $minute => $available_capacity ) {
372 $bookable_minutes_with_capacity_data .= $minute . ':' . $available_capacity . ',';
373 }
374 } else {
375 foreach ( $bookable_minutes as $minute => $available_capacity ) {
376 $bookable_minutes_with_capacity_data .= $minute . ',';
377 }
378 }
379 $bookable_minutes_with_capacity_data = rtrim( $bookable_minutes_with_capacity_data, ',' );
380
381
382 $bookable_slots_count = count( $bookable_minutes );
383 // TODO use work minutes instead to calculate minimum gap
384 $minimum_slot_gap = \LatePoint\Misc\BookingSlot::find_minimum_gap_between_slots( $booking_slots );
385 $day_class = 'os-day os-day-current week-day-' . strtolower( $day_date->format( 'N' ) );
386 $tabbable = true;
387 if ( empty( $bookable_minutes ) ) {
388 $day_class .= ' os-not-available';
389 $tabbable = false;
390 }
391 if ( $is_today ) {
392 $day_class .= ' os-today';
393 }
394 if ( $is_day_in_past ) {
395 $day_class .= ' os-day-passed';
396 $tabbable = false;
397 }
398 if ( $is_target_month ) {
399 $day_class .= ' os-month-current';
400 }
401 if ( $is_next_month ) {
402 $day_class .= ' os-month-next';
403 }
404 if ( $is_prev_month ) {
405 $day_class .= ' os-month-prev';
406 }
407 if ( $not_in_allowed_period ) {
408 $day_class .= ' os-not-in-allowed-period';
409 $tabbable = false;
410 }
411 if ( count( $bookable_minutes ) == 1 && OsStepsHelper::hide_timepicker_when_one_slot_available() ) {
412 $day_class .= ' os-one-slot-only';
413 }
414 if ( ( $day_date->format( 'Y-m-d' ) == $target_date->format( 'Y-m-d' ) ) && $settings['highlight_target_date'] ) {
415 $day_class .= ' selected';
416 }
417 ?>
418
419 <div <?php if($tabbable) echo 'tabindex="0"'; ?> role="button" class="<?php echo esc_attr( $day_class ); ?>"
420 data-date="<?php echo esc_attr( $day_date->format( 'Y-m-d' ) ); ?>"
421 data-nice-date="<?php echo esc_attr( OsTimeHelper::get_nice_date_with_optional_year( $day_date->format( 'Y-m-d' ), false ) ); ?>"
422 data-service-duration="<?php echo esc_attr( $booking_request->duration ); ?>"
423 data-total-work-minutes="<?php echo esc_attr( $total_work_minutes ); ?>"
424 data-work-start-time="<?php echo esc_attr( $work_boundaries->start_time ); ?>"
425 data-work-end-time="<?php echo esc_attr( $work_boundaries->end_time ); ?>"
426 data-bookable-minutes="<?php echo esc_attr( $bookable_minutes_with_capacity_data ); ?>"
427 data-work-minutes="<?php echo esc_attr( implode( ',', $work_minutes ) ); ?>"
428 data-interval="<?php echo esc_attr( $selectable_time_interval ); ?>">
429 <?php if ( $settings['layout'] == 'horizontal' ) { ?>
430 <div
431 class="os-day-weekday"><?php echo substr(esc_html( OsBookingHelper::get_weekday_name_by_number( $day_date->format( 'N' ) ) ), 0, 1); ?></div><?php } ?>
432 <div class="os-day-box">
433 <?php
434 if ( $bookable_slots_count && ! $settings['hide_slot_availability_count'] ) {
435 // translators: %d is the number of slots available
436 echo '<div class="os-available-slots-tooltip">' . esc_html( sprintf( __( '%d Available', 'latepoint' ), $bookable_slots_count ) ) . '</div>';
437 } ?>
438 <div class="os-day-number"><?php echo esc_html( $day_date->format( 'j' ) ); ?></div>
439 <?php if ( ! $is_day_in_past && ! $not_in_allowed_period ) { ?>
440 <div class="os-day-status">
441 <?php
442 if ( $total_work_minutes > 0 && $bookable_slots_count ) {
443 $available_blocks_count = 0;
444 $not_available_started_count = 0;
445 $duration = $booking_request->duration;
446 $end_time = $work_boundaries->end_time - $duration;
447 $processed_count = 0;
448 $last_available_slot_time = false;
449 $bookable_ranges = [];
450 $loop_availability_status = false;
451 for ( $i = 0; $i < count( $booking_slots ); $i ++ ) {
452 if ( $booking_slots[ $i ]->can_accomodate( $booking_request->total_attendees ) ) {
453 // AVAILABLE SLOT
454 if ( $loop_availability_status && $i > 0 && ( ( $booking_slots[ $i ]->start_time - $booking_slots[ $i - 1 ]->start_time ) > $minimum_slot_gap ) ) {
455 // big gap between previous slot and this slot
456 $bookable_ranges[] = $booking_slots[ $i - 1 ]->start_time + $minimum_slot_gap;
457 $bookable_ranges[] = $booking_slots[ $i ]->start_time;
458 }
459 if ( ! $loop_availability_status ) {
460 $bookable_ranges[] = $booking_slots[ $i ]->start_time;
461 }
462 $last_available_slot_time = $booking_slots[ $i ]->start_time;
463 $loop_availability_status = true;
464 } else {
465 // NOT AVAILABLE
466 // a different resource but with the same start time, so that if its available (checked in next loop iteration) - we don't block this slot
467 if ( isset( $booking_slots[ $i + 1 ] ) && $booking_slots[ $i + 1 ]->start_time == $booking_slots[ $i ]->start_time ) {
468 continue;
469 }
470 // check if last available slot had the same start time as current one, if so - we don't block this slot and move to the next one
471 if ( $last_available_slot_time == $booking_slots[ $i ]->start_time && isset( $booking_slots[ $i - 1 ] ) && $booking_slots[ $i - 1 ]->start_time == $booking_slots[ $i ]->start_time ) {
472 continue;
473 }
474 // if last available slot exists and previous slot was also available
475 if ( $last_available_slot_time && $loop_availability_status ) {
476 $bookable_ranges[] = $last_available_slot_time + $minimum_slot_gap;
477 }
478 $loop_availability_status = false;
479 }
480 }
481 if ( $bookable_ranges ) {
482 for ( $i = 0; $i < count( $bookable_ranges ); $i += 2 ) {
483 $left = ( $bookable_ranges[ $i ] - $work_boundaries->start_time ) / $total_work_minutes * 100;
484 $width = isset( $bookable_ranges[ $i + 1 ] ) ? ( ( $bookable_ranges[ $i + 1 ] - $bookable_ranges[ $i ] ) / $total_work_minutes * 100 ) : ( ( $work_boundaries->end_time - $bookable_ranges[ $i ] ) / $total_work_minutes * 100 );
485 echo '<div class="day-available" style="left:' . esc_attr( $left ) . '%;width:' . esc_attr( $width ) . '%;"></div>';
486 }
487 }
488 }
489 ?>
490 </div>
491 <?php } ?>
492 </div>
493 </div>
494
495 <?php
496
497 // DAYS LOOP END
498 }
499 echo '</div></div>';
500 }
501
502 // Used on holiday/custom schedule generator lightbox
503 public static function generate_monthly_calendar_days_only( $target_date_string = 'today', $highlight_target_date = false, bool $is_active = false ) {
504 $target_date = new OsWpDateTime( $target_date_string );
505 $calendar_start = clone $target_date;
506 $calendar_start->modify( 'first day of this month' );
507 $calendar_end = clone $target_date;
508 $calendar_end->modify( 'last day of this month' );
509
510 $weekday_for_first_day_of_month = $calendar_start->format( 'N' ) - 1;
511 $weekday_for_last_day_of_month = $calendar_end->format( 'N' ) - 1;
512
513
514 if ( $weekday_for_first_day_of_month > 0 ) {
515 $calendar_start->modify( '-' . $weekday_for_first_day_of_month . ' days' );
516 }
517
518 if ( $weekday_for_last_day_of_month < 6 ) {
519 $days_to_add = 6 - $weekday_for_last_day_of_month;
520 if($days_to_add > 0){
521 $calendar_end->modify( '+' . $days_to_add . ' days' );
522 }
523 }
524
525 $active_class = $is_active ? 'active' : '';
526
527 echo '<div class="os-monthly-calendar-days-w '.$active_class.'" data-calendar-year="' . esc_attr( $target_date->format( 'Y' ) ) . '" data-calendar-month="' . esc_attr( $target_date->format( 'n' ) ) . '" data-calendar-month-label="' . esc_attr( OsUtilHelper::get_month_name_by_number( $target_date->format( 'n' ) ) ) . '">';
528 echo '<div class="os-monthly-calendar-days">';
529 for ( $day_date = clone $calendar_start; $day_date <= $calendar_end; $day_date->modify( '+1 day' ) ) {
530 $is_today = ( $day_date->format( 'Y-m-d' ) == OsTimeHelper::today_date() ) ? true : false;
531 $is_day_in_past = ( $day_date->format( 'Y-m-d' ) < OsTimeHelper::today_date() ) ? true : false;
532 $day_class = 'os-day os-day-current week-day-' . strtolower( $day_date->format( 'N' ) );
533
534 if ( $day_date->format( 'm' ) > $target_date->format( 'm' ) ) {
535 $day_class .= ' os-month-next';
536 }
537 if ( $day_date->format( 'm' ) < $target_date->format( 'm' ) ) {
538 $day_class .= ' os-month-prev';
539 }
540
541 if ( $is_today ) {
542 $day_class .= ' os-today';
543 }
544 if ( $highlight_target_date && ( $day_date->format( 'Y-m-d' ) == $target_date->format( 'Y-m-d' ) ) ) {
545 $day_class .= ' selected';
546 }
547 if ( $is_day_in_past ) {
548 $day_class .= ' os-day-passed';
549 } ?>
550 <div class="<?php echo esc_attr( $day_class ); ?>" data-date="<?php echo esc_attr( $day_date->format( 'Y-m-d' ) ); ?>">
551 <div class="os-day-box">
552 <div class="os-day-number"><?php echo esc_html( $day_date->format( 'j' ) ); ?></div>
553 </div>
554 </div><?php
555 }
556 echo '</div></div>';
557 }
558
559 public static function generate_calendar_quick_actions_link( OsWpDateTime $day_date, array $settings = [] ) : string {
560 $defaults = [
561 'agent_id' => 0,
562 'location_id' => 0,
563 'service_id' => 0,
564 'start_time' => 600
565 ];
566
567 $settings = OsUtilHelper::merge_default_atts( $defaults, $settings );
568
569 return '<a href="#" data-os-after-call="latepoint_init_calendar_quick_actions" data-os-lightbox-classes="width-400" class="day-action-trigger" data-os-output-target="lightbox" data-os-params="'.OsUtilHelper::build_os_params(['target_date' => $day_date->format('Y-m-d'), 'start_time' => $settings['start_time'], 'agent_id' => $settings['agent_id'], 'location_id' => $settings['location_id'], 'service_id' => $settings['service_id']]).'" data-os-action="'.OsRouterHelper::build_route_name('calendars', 'quick_actions').'"><i class="latepoint-icon latepoint-icon-more-horizontal"></i></a>';
570 }
571
572
573 }