PluginProbe
Booktics – Appointment Booking Calendar for Service Businesses / 1.0.29
Booktics – Appointment Booking Calendar for Service Businesses v1.0.29
1.0.29 1.0.28 1.0.27 1.0.26 1.0.25 1.0.24 1.0.23 1.0.22 1.0.21 1.0.20 1.0.19 trunk 1.0.0 1.0.1 1.0.10 1.0.11 1.0.12 1.0.13 1.0.14 1.0.15 1.0.16 1.0.17 1.0.18 1.0.2 1.0.3 All 31 releases
← All changes | utils/global-helper.php +1154 -723 1.0.1 → 1.0.29 View file →
@@ -1,723 +1,1154 @@
1 -<?php
2 -
3 -/**
4 - * Get timezone list.
5 - *
6 - * @return array
7 - * @since 1.0.0
8 - */
9 -if ( ! function_exists( 'booktics_get_timezone_list' ) ) {
10 - function booktics_get_timezone_list() {
11 - $tzlist = wp_timezone_choice( 'UTC' );
12 - $doc = new DOMDocument();
13 - $doc->loadHTML( $tzlist );
14 -
15 - $elements = $doc->getElementsByTagName( 'option' );
16 - $timezones = array();
17 -
18 - if ( ! empty( $elements ) ) {
19 - foreach ( $elements as $element ) {
20 - $data = array(
21 - 'value' => $element->getAttribute( 'value' ),
22 - 'name' => $element->textContent,
23 - // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
24 - );
25 -
26 - $timezones[] = $data;
27 - }
28 - }
29 -
30 - return $timezones;
31 - }
32 -}
33 -
34 -if ( ! function_exists( 'booktics_get_auth_redirect_uri' ) ) {
35 - /**
36 - * Get auth redirect uri
37 - *
38 - * @param string $auth
39 - *
40 - * @return string
41 - */
42 - function booktics_get_auth_redirect_uri( $auth = '' ) {
43 - return site_url( 'booktics-integration/' . $auth );
44 - }
45 -}
46 -
47 -
48 -if ( ! function_exists( 'booktics_get_google_auth' ) ) {
49 - /**
50 - * Get google auth
51 - *
52 - * @param integer $user_id
53 - *
54 - * @return array
55 - */
56 - function booktics_get_google_auth( $user_id = 0 ) {
57 - return get_user_meta( $user_id, 'booktics_google_auth', true );
58 - }
59 -}
60 -
61 -if ( ! function_exists( 'booktics_get_posts' ) ) {
62 - /**
63 - * Get posts with date range
64 - *
65 - * @param array $args
66 - *
67 - * @return WP_Query
68 - */
69 - function booktics_get_posts( $args = array() ) {
70 - $defaults = array(
71 - 'post_type' => 'post',
72 - 'post_status' => 'publish',
73 - 'nopaging' => true,
74 - 'date_range' => '',
75 - );
76 -
77 - $args = wp_parse_args( $args, $defaults );
78 -
79 - // Date range found.
80 - if ( $args['date_range'] ) {
81 - $from_date = $args['date_range']['start'];
82 - $to_date = $args['date_range']['end'];
83 -
84 - $args['date_query'] = array(
85 - 'relation' => 'AND',
86 - array(
87 - 'before' => array(
88 - 'year' => gmdate( 'Y', strtotime( $to_date ) ),
89 - 'month' => gmdate( 'm', strtotime( $to_date ) ),
90 - 'day' => gmdate( 'd', strtotime( $to_date ) ),
91 - ),
92 - 'after' => array(
93 - 'year' => gmdate( 'Y', strtotime( $from_date ) ),
94 - 'month' => gmdate( 'm', strtotime( $from_date ) ),
95 - 'day' => gmdate( 'd', strtotime( $from_date ) ),
96 - ),
97 - 'inclusive' => true,
98 - ),
99 - );
100 - }
101 -
102 - // The Query
103 - $posts = new WP_Query( $args );
104 -
105 - return $posts;
106 - }
107 -}
108 -
109 -if ( ! function_exists( 'booktics_count_posts()' ) ) {
110 - /**
111 - * Count post with date range or without date range
112 - *
113 - * @param array $args Required query params
114 - *
115 - * @return integer
116 - */
117 - function booktics_count_posts( $args = array() ) {
118 - $posts = booktics_get_posts( $args );
119 -
120 - return $posts->post_count;
121 - }
122 -}
123 -
124 -if ( ! function_exists( 'booktics_count_users' ) ) {
125 - /**
126 - * Count user by role and date range
127 - *
128 - * @param array $args Required query params
129 - *
130 - * @return integer
131 - */
132 - function booktics_count_users( $args = array() ) {
133 - $defaults = array(
134 - 'role' => '',
135 - 'date_range' => '',
136 - 'count_total' => true,
137 - );
138 -
139 - $args = wp_parse_args( $args, $defaults );
140 -
141 - // Date range found.
142 - if ( $args['date_range'] ) {
143 - $from_date = $args['date_range']['start'];
144 - $to_date = $args['date_range']['end'];
145 -
146 - $args['date_query'] = array(
147 - 'relation' => 'AND',
148 - array(
149 - 'before' => array(
150 - 'year' => gmdate( 'Y', strtotime( $to_date ) ),
151 - 'month' => gmdate( 'm', strtotime( $to_date ) ),
152 - 'day' => gmdate( 'd', strtotime( $to_date ) ),
153 - ),
154 - 'after' => array(
155 - 'year' => gmdate( 'Y', strtotime( $from_date ) ),
156 - 'month' => gmdate( 'm', strtotime( $from_date ) ),
157 - 'day' => gmdate( 'd', strtotime( $from_date ) ),
158 - ),
159 - 'inclusive' => true,
160 - ),
161 - );
162 - }
163 -
164 - // The Query
165 - $user_query = new WP_User_Query( $args );
166 -
167 - // Return total users.
168 - return $user_query->total_users;
169 - }
170 -}
171 -
172 -
173 -if ( ! function_exists( 'booktics_get_default_timezone' ) ) {
174 - /**
175 - * Get default timezone
176 - *
177 - * @return string
178 - */
179 - function booktics_get_default_timezone() {
180 - $timezone = get_option( 'timezone_string' );
181 -
182 - return $timezone;
183 - }
184 -}
185 -
186 -if ( ! function_exists( 'booktics_get_currencies' ) ) {
187 - /**
188 - * Get currency list
189 - *
190 - * @return array
191 - */
192 - function booktics_get_currencies() {
193 - $currencies = require_once __DIR__ . '/currency.php';
194 -
195 - return $currencies;
196 - }
197 -}
198 -
199 -if ( ! function_exists( 'booktics_update_default_settings' ) ) {
200 -
201 - /**
202 - * Update default settings
203 - *
204 - * @return void
205 - */
206 - function booktics_update_default_settings() {
207 - $settings = array(
208 - 'google_auth_redirect_uri' => booktics_get_auth_redirect_uri( 'google-auth' ),
209 - 'default_booking_status' => 'pending',
210 - 'currency' => 'USD',
211 - 'primary_color' => '#3161F1',
212 - 'secondary_color' => '#6188ff',
213 - );
214 -
215 - $settings = apply_filters( 'booktics_default_settings', $settings );
216 -
217 - foreach ( $settings as $key => $value ) {
218 - booktics_update_option( $key, $value );
219 - }
220 - }
221 -}
222 -
223 -if ( ! function_exists( 'booktics_get_post_status' ) ) {
224 -
225 - /**
226 - * Get post status
227 - *
228 - * @return array
229 - */
230 - function booktics_get_post_status() {
231 - return array(
232 - 'approved',
233 - 'pending',
234 - 'cancel',
235 - 'completed',
236 - );
237 - }
238 -}
239 -
240 -if ( ! function_exists( 'booktics_replace_placeholder' ) ) {
241 - /**
242 - * Replace string with placeholder
243 - *
244 - * @param array $placeholders
245 - * @param string $text
246 - *
247 - * @return string
248 - */
249 - function booktics_replace_placeholder( $text, $placeholders = array() ) {
250 -
251 - if ( ! $placeholders ) {
252 - return $text;
253 - }
254 -
255 - foreach ( $placeholders as $placeholder => $replace_with ) {
256 - $text = str_replace( $placeholder, $replace_with, $text );
257 - }
258 -
259 - return $text;
260 - }
261 -}
262 -
263 -if ( ! function_exists( 'booktics_register_cron' ) ) {
264 - /**
265 - * Register booktics cron
266 - *
267 - * @return void
268 - */
269 - function booktics_register_cron() {
270 - wp_schedule_event( time(), 'daily', 'booktics_booking_clear_schedule' );
271 - }
272 -}
273 -
274 -if ( ! function_exists( 'booktics_is_woocommerce_active' ) ) {
275 -
276 - /**
277 - * Check woocommerce is active or not
278 - *
279 - * @return bool
280 - */
281 - function booktics_is_woocommerce_active() {
282 -
283 - if ( function_exists( 'WC' ) && booktics_get_option( 'wc_integration' ) ) {
284 - return true;
285 - }
286 -
287 - return false;
288 - }
289 -}
290 -
291 -if ( ! function_exists( 'booktics_add_woocommerce_product_cat' ) ) {
292 - /**
293 - * Add woocommerce product category
294 - *
295 - * @return void
296 - */
297 - function booktics_add_woocommerce_product_cat() {
298 - $category_name = __( 'Booktics Meeting', 'booktics' );
299 -
300 - // Prepare category arguments
301 - $category_args = array(
302 - 'taxonomy' => 'product_cat',
303 - 'cat_name' => $category_name,
304 - 'category_description' => __( 'Booktics meeting', 'booktics' ),
305 - 'slug' => 'booktics-meeting',
306 - );
307 -
308 - // Insert the category
309 - wp_insert_term( $category_args['cat_name'], $category_args['taxonomy'], $category_args );
310 - }
311 -}
312 -
313 -if ( ! function_exists( 'booktics_is_valid_timezone' ) ) {
314 - /**
315 - * Check a timezone is valid or not
316 - *
317 - * @param string $timezone
318 - *
319 - * @return bool
320 - */
321 - function booktics_is_valid_timezone( $timezone ) {
322 - try {
323 - new DateTimeZone( $timezone );
324 - } catch ( Exception $e ) {
325 - return false;
326 - }
327 -
328 - return true;
329 - }
330 -}
331 -
332 -if ( ! function_exists( 'booktics_datetime' ) ) {
333 -
334 - /**
335 - * Booktics date time with timezone
336 - *
337 - * @param int | string $date
338 - * @param string $timezone
339 - *
340 - * @return string
341 - */
342 - function booktics_datetime( $format, $date, $timezone = '' ) {
343 - $date = is_int( $date ) ? gmdate( 'Y-m-d g:ia', $date ) : $date;
344 -
345 - if ( ! $timezone ) {
346 - $timezone = 'UTC';
347 - }
348 -
349 - // Create a DateTime object with the provided timestamp and time zone
350 - $datetime = new \DateTime( $date, new \DateTimeZone( $timezone ) );
351 -
352 - // Get the converted timestamp
353 - return $datetime->format( $format );
354 - }
355 -}
356 -
357 -if ( ! function_exists( 'booktics_convert_timezone' ) ) {
358 -
359 - /**
360 - * Convert date and time from timezone to another timezone
361 - *
362 - * @param string $format
363 - * @param string $date_time
364 - * @param string $from
365 - * @param string $to
366 - *
367 - * @return \DateTime
368 - */
369 - function booktics_convert_timezone( $date_time_string, $from, $to ) {
370 -
371 - if ( empty( $from ) || empty( $to ) ) {
372 - return new DateTime( $date_time_string );
373 - }
374 -
375 - $date_time_string = is_int( $date_time_string ) ? gmdate( 'Y-m-d g:ia', $date_time_string ) : gmdate( 'Y-m-d g:ia', strtotime( $date_time_string ) );
376 -
377 - $datetime = new DateTime( $date_time_string, new DateTimeZone( $from ) );
378 - $datetime->setTimezone( new DateTimeZone( $to ) );
379 -
380 - return $datetime;
381 - }
382 -}
383 -
384 -if ( ! function_exists( 'booktics_generate_username' ) ) {
385 - /**
386 - * Generate unique username
387 - *
388 - * @param string $email
389 - *
390 - * @return string
391 - */
392 - function booktics_generate_username( $email ) {
393 - $username = strtok( $email, '@' );
394 -
395 - if ( username_exists( $username ) ) {
396 - $username = $username . wp_rand( 10, 100 );
397 - }
398 -
399 - return $username;
400 - }
401 -}
402 -
403 -if ( ! function_exists( 'booktics_is_json' ) ) {
404 - /**
405 - * Check a string is valid json or not
406 - *
407 - * @param string $json_string
408 - *
409 - * @return bool
410 - */
411 - function booktics_is_json( $json_string ) {
412 - if ( ! is_string( $json_string ) ) {
413 - return false;
414 - }
415 -
416 - $data = json_decode( $json_string, true );
417 -
418 - return $data !== null && json_last_error() === JSON_ERROR_NONE;
419 - }
420 -}
421 -
422 -
423 -if ( ! function_exists( 'booktics_get_current_user' ) ) {
424 - /**
425 - * Get current user data
426 - *
427 - * @return array
428 - */
429 - function booktics_get_current_user() {
430 - $user_id = get_current_user_id();
431 - $user = get_userdata( $user_id );
432 -
433 - $user_data = array(
434 - 'id' => $user->ID,
435 - 'username' => $user->user_login,
436 - 'email' => $user->user_email,
437 - 'roles' => $user->roles,
438 - );
439 -
440 - return $user_data;
441 - }
442 -}
443 -
444 -if ( ! function_exists( 'booktics_validate' ) ) {
445 - /**
446 - * Validate user input
447 - *
448 - * @param array $request
449 - * @param array $rules
450 - *
451 - * @return bool | WP_Error
452 - */
453 - function booktics_validate( $request, $rules ) {
454 - $validator = new \Booktics\Validation\Validator( $request );
455 -
456 - $validator->set_rules( $rules );
457 -
458 - if ( ! $validator->validate() ) {
459 - return $validator->get_error();
460 - }
461 -
462 - return true;
463 - }
464 -}
465 -
466 -if ( ! function_exists( 'booktics_snake_case' ) ) {
467 - /**
468 - * Convert a string to snake_case.
469 - *
470 - * @param string $string
471 - *
472 - * @return string
473 - */
474 - function booktics_snake_case( string $string ): string {
475 - $string = preg_replace( '/([a-z])([A-Z])/', '$1_$2', $string );
476 -
477 - return strtolower( str_replace( array( '-', ' ' ), '_', $string ) );
478 - }
479 -}
480 -
481 -if ( ! function_exists( 'booktics_kebab_case' ) ) {
482 - /**
483 - * Convert a string to kebab-case.
484 - *
485 - * @param string $string
486 - *
487 - * @return string
488 - */
489 - function booktics_kebab_case( string $string ): string {
490 - $string = preg_replace( '/([a-z])([A-Z])/', '$1-$2', $string );
491 -
492 - return strtolower( str_replace( array( '_', ' ' ), '-', $string ) );
493 - }
494 -}
495 -
496 -if ( ! function_exists( 'booktics_camel_case' ) ) {
497 - /**
498 - * Convert a string to camelCase.
499 - *
500 - * @param string $string
501 - *
502 - * @return string
503 - */
504 - function booktics_camel_case( string $string ): string {
505 - $string = str_replace( array( '-', '_' ), ' ', strtolower( $string ) );
506 - $string = ucwords( $string );
507 - $string = str_replace( ' ', '', $string );
508 -
509 - return lcfirst( $string );
510 - }
511 -}
512 -
513 -if ( ! function_exists( 'booktics_pascal_case' ) ) {
514 - /**
515 - * Convert a string to PascalCase.
516 - *
517 - * @param string $string
518 - *
519 - * @return string
520 - */
521 - function booktics_pascal_case( string $string ): string {
522 - $string = str_replace( array( '-', '_' ), ' ', strtolower( $string ) );
523 - $string = ucwords( $string );
524 -
525 - return str_replace( ' ', '', $string );
526 - }
527 -}
528 -
529 -if ( ! function_exists( 'booktics_date_range_loop' ) ) {
530 - /**
531 - * Make date range iterable array
532 - *
533 - * @param $start
534 - * @param $end
535 - * @param $step
536 - * @param $format
537 - *
538 - * @return array
539 - * @throws Exception
540 - */
541 - function booktics_date_range_loop( $start, $end, $step = '+1 day', $format = 'Y-m-d' ) {
542 - $dates = array();
543 -
544 - $current_date = new DateTime( $start );
545 - $end_date = new DateTime( $end );
546 -
547 - while ( $current_date <= $end_date ) {
548 - $dates[] = $current_date->format( $format );
549 - $current_date->modify( $step );
550 - }
551 -
552 - return $dates;
553 - }
554 -
555 - if ( ! function_exists( 'booktics_get_option' ) ) {
556 - /**
557 - * Get option for Booktics
558 - *
559 - * @param string $key Option key
560 - * @param mixed $default Default value to return if option doesn't exist
561 - *
562 - * @return mixed
563 - */
564 - function booktics_get_option( $key = '', $default = false ) {
565 - return \Booktics\Settings\Settings::get( $key, $default );
566 - }
567 - }
568 -
569 - if ( ! function_exists( 'booktics_update_option' ) ) {
570 - /**
571 - * Update Booktics option
572 - *
573 - * @param string $key Option key
574 - * @param mixed $value Option value
575 - *
576 - * @return void
577 - */
578 - function booktics_update_option( $key, $value ) {
579 - \Booktics\Settings\Settings::update( $key, $value );
580 - }
581 - }
582 -}
583 -
584 -if ( ! function_exists( 'is_booktics_pro' ) ) {
585 - /**
586 - * Check Booktics Pro plugin activate or deactivate
587 - *
588 - * @return bool
589 - */
590 - function is_booktics_pro(): bool {
591 - return is_plugin_active( 'booktics-pro/booktics-pro.php' );
592 - }
593 -}
594 -
595 -if ( ! function_exists( 'booktics_extension' ) ) {
596 - /**
597 - * Get Booktics extension object from tools-sdk
598 - */
599 - function booktics_extension() {
600 - return ( new \Arraytics\ToolsSdk\Extension( 'booktics_available_modules', booktics_modules_list() ) );
601 - }
602 -}
603 -
604 -if ( ! function_exists( 'booktics_get_enabled_modules' ) ) {
605 - /**
606 - * Get enabled modules
607 - *
608 - * @return array
609 - */
610 - function booktics_get_enabled_modules(): array {
611 - $modules = get_option( 'booktics_available_modules', array() );
612 -
613 - if ( empty( $modules ) || ! is_array( $modules ) ) {
614 - return array();
615 - }
616 -
617 - $enabled = array();
618 -
619 - foreach ( $modules as $module_slug => $module_data ) {
620 - if ( is_array( $module_data ) && isset( $module_data['status'] ) && $module_data['status'] === 'on' ) {
621 - $enabled[] = $module_slug;
622 - } elseif ( is_string( $module_data ) && $module_data === 'on' ) {
623 - $enabled[] = $module_slug;
624 - }
625 - }
626 -
627 - return $enabled;
628 - }
629 -
630 - if ( ! function_exists( 'get_booktics_modules_list' ) ) {
631 - /**
632 - * Get Booktics modules list
633 - *
634 - * @return array
635 - */
636 - function booktics_modules_list(): array {
637 - $modules_list = array(
638 - 'stripe' => array(
639 - 'name' => 'stripe',
640 - 'slug' => 'stripe',
641 - 'type' => 'module',
642 - 'upgrade' => true,
643 - 'upgrade_link' => 'https://themewinter.com/purchase-history',
644 - 'status' => 'off',
645 - 'is_pro' => false,
646 - 'title' => __( 'Stripe', 'booktics' ),
647 - 'description' => __( 'It allows you to use stripe payment.', 'booktics' ),
648 - 'icon' => \Booktics\Extensions\Extension_Icon::get( 'stripe-addon' ),
649 - 'notice' => '',
650 - 'demo_link' => 'https://support.themewinter.com/docs/plugins/plugin-docs/integration/booktics-stripe',
651 - 'settings_link' => '',
652 - 'doc_link' => 'https://support.themewinter.com/docs/plugins/plugin-docs/integration/booktics-stripe',
653 - ),
654 - 'fluentcrm' => array(
655 - 'name' => 'fluentcrm',
656 - 'slug' => 'fluentcrm',
657 - 'type' => 'module',
658 - 'upgrade' => true,
659 - 'upgrade_link' => 'https://themewinter.com/purchase-history',
660 - 'status' => 'off',
661 - 'is_pro' => false,
662 - 'title' => __( 'FluentCRM', 'booktics' ),
663 - 'description' => __( 'It allows you to use FluentCRM.', 'booktics' ),
664 - 'icon' => \Booktics\Extensions\Extension_Icon::get( 'booktics-fluentcrm-addon' ),
665 - 'notice' => '',
666 - 'demo_link' => 'https://support.themewinter.com/docs/plugins/plugin-docs/integration/booktics-fluentcrm',
667 - 'settings_link' => '',
668 - 'doc_link' => 'https://support.themewinter.com/docs/plugins/plugin-docs/integration/booktics-google-calendar',
669 - ),
670 - 'google-calendar' => array(
671 - 'name' => 'google-calendar',
672 - 'slug' => 'google-calendar',
673 - 'type' => 'module',
674 - 'upgrade' => true,
675 - 'upgrade_link' => 'https://themewinter.com/purchase-history',
676 - 'status' => 'off',
677 - 'is_pro' => true,
678 - 'title' => __( 'Booktics Google Calendar', 'booktics' ),
679 - 'description' => __( 'It allows you to use google calendar.', 'booktics' ),
680 - 'icon' => \Booktics\Extensions\Extension_Icon::get( 'booktics-google-calendar' ),
681 - 'notice' => '',
682 - 'demo_link' => 'https://support.themewinter.com/docs/plugins/plugin-docs/integration/booktics-google-calendar',
683 - 'settings_link' => '',
684 - 'doc_link' => 'https://support.themewinter.com/docs/plugins/plugin-docs/integration/booktics-google-calendar',
685 - ),
686 - 'outlook-calendar' => array(
687 - 'name' => 'outlook-calendar',
688 - 'slug' => 'outlook-calendar',
689 - 'type' => 'module',
690 - 'upgrade' => true,
691 - 'upgrade_link' => 'https://themewinter.com/purchase-history',
692 - 'status' => 'off',
693 - 'is_pro' => true,
694 - 'title' => __( 'Booktics Outlook Calendar', 'booktics' ),
695 - 'description' => __( 'It allows you to use outlook calendar.', 'booktics' ),
696 - 'icon' => \Booktics\Extensions\Extension_Icon::get( 'booktics-outlook-calendar' ),
697 - 'notice' => '',
698 - 'demo_link' => 'https://support.themewinter.com/docs/plugins/plugin-docs/integration/booktics-outlook-calendar-addon',
699 - 'settings_link' => '',
700 - 'doc_link' => 'https://support.themewinter.com/docs/plugins/plugin-docs/integration/booktics-outlook-calendar-addon',
701 - ),
702 - 'paypal' => array(
703 - 'name' => 'paypal',
704 - 'slug' => 'paypal',
705 - 'type' => 'module',
706 - 'upgrade' => true,
707 - 'upgrade_link' => 'https://themewinter.com/purchase-history',
708 - 'status' => 'off',
709 - 'is_pro' => true,
710 - 'title' => __( 'PayPal', 'booktics' ),
711 - 'description' => __( 'It allows you to use PayPal.', 'booktics' ),
712 - 'icon' => \Booktics\Extensions\Extension_Icon::get( 'booktics-paypal-addon' ),
713 - 'notice' => '',
714 - 'demo_link' => 'https://support.themewinter.com/docs/plugins/plugin-docs/integration/booktics-paypal-addon',
715 - 'settings_link' => '',
716 - 'doc_link' => 'https://support.themewinter.com/docs/plugins/plugin-docs/integration/booktics-paypal-addon',
717 - ),
718 - );
719 -
720 - return $modules_list;
721 - }
722 - }
723 -}
1 +<?php
2 +if ( ! defined( 'ABSPATH' ) ) {
3 + // Composer autoloads this file, so CLI tooling loads it without WordPress.
4 + if ( PHP_SAPI !== 'cli' ) {
5 + exit;
6 + }
7 +}
8 +/**
9 + * Get timezone list.
10 + *
11 + * @return array
12 + * @since 1.0.0
13 + */
14 +if ( ! function_exists( 'booktics_get_timezone_list' ) ) {
15 + function booktics_get_timezone_list() {
16 + $tzlist = wp_timezone_choice( 'UTC' );
17 + $doc = new DOMDocument();
18 + $doc->loadHTML( $tzlist );
19 +
20 + $elements = $doc->getElementsByTagName( 'option' );
21 + $timezones = array();
22 +
23 + if ( ! empty( $elements ) ) {
24 + foreach ( $elements as $element ) {
25 + $data = array(
26 + 'value' => $element->getAttribute( 'value' ),
27 + 'name' => $element->textContent,
28 + // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
29 + );
30 +
31 + $timezones[] = $data;
32 + }
33 + }
34 +
35 + return $timezones;
36 + }
37 +}
38 +
39 +if ( ! function_exists( 'booktics_get_auth_redirect_uri' ) ) {
40 + /**
41 + * Get auth redirect uri
42 + *
43 + * @param string $auth
44 + *
45 + * @return string
46 + */
47 + function booktics_get_auth_redirect_uri( $auth = '' ) {
48 + return site_url( 'booktics-integration/' . $auth );
49 + }
50 +}
51 +
52 +
53 +if ( ! function_exists( 'booktics_get_google_auth' ) ) {
54 + /**
55 + * Get google auth
56 + *
57 + * @param integer $user_id
58 + *
59 + * @return array
60 + */
61 + function booktics_get_google_auth( $user_id = 0 ) {
62 + return get_user_meta( $user_id, 'booktics_google_auth', true );
63 + }
64 +}
65 +
66 +if ( ! function_exists( 'booktics_get_posts' ) ) {
67 + /**
68 + * Get posts with date range
69 + *
70 + * @param array $args
71 + *
72 + * @return WP_Query
73 + */
74 + function booktics_get_posts( $args = array() ) {
75 + $defaults = array(
76 + 'post_type' => 'post',
77 + 'post_status' => 'publish',
78 + 'nopaging' => true,
79 + 'date_range' => '',
80 + );
81 +
82 + $args = wp_parse_args( $args, $defaults );
83 +
84 + // Date range found.
85 + if ( $args['date_range'] ) {
86 + $from_date = $args['date_range']['start'];
87 + $to_date = $args['date_range']['end'];
88 +
89 + $args['date_query'] = array(
90 + 'relation' => 'AND',
91 + array(
92 + 'before' => array(
93 + 'year' => gmdate( 'Y', strtotime( $to_date ) ),
94 + 'month' => gmdate( 'm', strtotime( $to_date ) ),
95 + 'day' => gmdate( 'd', strtotime( $to_date ) ),
96 + ),
97 + 'after' => array(
98 + 'year' => gmdate( 'Y', strtotime( $from_date ) ),
99 + 'month' => gmdate( 'm', strtotime( $from_date ) ),
100 + 'day' => gmdate( 'd', strtotime( $from_date ) ),
101 + ),
102 + 'inclusive' => true,
103 + ),
104 + );
105 + }
106 +
107 + // The Query
108 + $posts = new WP_Query( $args );
109 +
110 + return $posts;
111 + }
112 +}
113 +
114 +if ( ! function_exists( 'booktics_count_posts()' ) ) {
115 + /**
116 + * Count post with date range or without date range
117 + *
118 + * @param array $args Required query params
119 + *
120 + * @return integer
121 + */
122 + function booktics_count_posts( $args = array() ) {
123 + $posts = booktics_get_posts( $args );
124 +
125 + return $posts->post_count;
126 + }
127 +}
128 +
129 +if ( ! function_exists( 'booktics_count_users' ) ) {
130 + /**
131 + * Count user by role and date range
132 + *
133 + * @param array $args Required query params
134 + *
135 + * @return integer
136 + */
137 + function booktics_count_users( $args = array() ) {
138 + $defaults = array(
139 + 'role' => '',
140 + 'date_range' => '',
141 + 'count_total' => true,
142 + );
143 +
144 + $args = wp_parse_args( $args, $defaults );
145 +
146 + // Date range found.
147 + if ( $args['date_range'] ) {
148 + $from_date = $args['date_range']['start'];
149 + $to_date = $args['date_range']['end'];
150 +
151 + $args['date_query'] = array(
152 + 'relation' => 'AND',
153 + array(
154 + 'before' => array(
155 + 'year' => gmdate( 'Y', strtotime( $to_date ) ),
156 + 'month' => gmdate( 'm', strtotime( $to_date ) ),
157 + 'day' => gmdate( 'd', strtotime( $to_date ) ),
158 + ),
159 + 'after' => array(
160 + 'year' => gmdate( 'Y', strtotime( $from_date ) ),
161 + 'month' => gmdate( 'm', strtotime( $from_date ) ),
162 + 'day' => gmdate( 'd', strtotime( $from_date ) ),
163 + ),
164 + 'inclusive' => true,
165 + ),
166 + );
167 + }
168 +
169 + // The Query
170 + $user_query = new WP_User_Query( $args );
171 +
172 + // Return total users.
173 + return $user_query->total_users;
174 + }
175 +}
176 +
177 +
178 +if ( ! function_exists( 'booktics_get_default_timezone' ) ) {
179 + /**
180 + * Get default timezone
181 + *
182 + * @return string
183 + */
184 + function booktics_get_default_timezone() {
185 + $timezone = get_option( 'timezone_string' );
186 +
187 + return $timezone;
188 + }
189 +}
190 +
191 +if ( ! function_exists( 'booktics_get_currencies' ) ) {
192 + /**
193 + * Get currency list
194 + *
195 + * @return array
196 + */
197 + function booktics_get_currencies() {
198 + $currencies = require_once __DIR__ . '/currency.php';
199 +
200 + return $currencies;
201 + }
202 +}
203 +
204 +if ( ! function_exists( 'booktics_update_default_settings' ) ) {
205 +
206 + /**
207 + * Update default settings
208 + *
209 + * @return void
210 + */
211 + function booktics_update_default_settings() {
212 + $settings = array(
213 + 'google_auth_redirect_uri' => booktics_get_auth_redirect_uri( 'google-auth' ),
214 + 'default_booking_status' => 'pending',
215 + 'currency' => 'USD',
216 + 'primary_color' => '#3161F1',
217 + 'secondary_color' => '#6188ff',
218 + );
219 +
220 + $settings = apply_filters( 'booktics_default_settings', $settings );
221 +
222 + foreach ( $settings as $key => $value ) {
223 + booktics_update_option( $key, $value );
224 + }
225 + }
226 +}
227 +
228 +if ( ! function_exists( 'booktics_get_post_status' ) ) {
229 +
230 + /**
231 + * Get post status
232 + *
233 + * @return array
234 + */
235 + function booktics_get_post_status() {
236 + return array(
237 + 'approved',
238 + 'pending',
239 + 'cancel',
240 + 'completed',
241 + );
242 + }
243 +}
244 +
245 +if ( ! function_exists( 'booktics_replace_placeholder' ) ) {
246 + /**
247 + * Replace string with placeholder
248 + *
249 + * @param array $placeholders
250 + * @param string $text
251 + *
252 + * @return string
253 + */
254 + function booktics_replace_placeholder( $text, $placeholders = array() ) {
255 +
256 + if ( ! $placeholders ) {
257 + return $text;
258 + }
259 +
260 + foreach ( $placeholders as $placeholder => $replace_with ) {
261 + $text = str_replace( $placeholder, $replace_with, $text );
262 + }
263 +
264 + return $text;
265 + }
266 +}
267 +
268 +if ( ! function_exists( 'booktics_register_cron' ) ) {
269 + /**
270 + * Register booktics cron
271 + *
272 + * @return void
273 + */
274 + function booktics_register_cron() {
275 + wp_schedule_event( time(), 'daily', 'booktics_booking_clear_schedule' );
276 + }
277 +}
278 +
279 +if ( ! function_exists( 'booktics_is_woocommerce_active' ) ) {
280 +
281 + /**
282 + * Check whether WooCommerce payments are usable.
283 + *
284 + * True only when WooCommerce itself is running, the module is enabled and
285 + * the payment setting is on. Any one of the three being false means the
286 + * gateway must not be offered.
287 + *
288 + * @return bool
289 + */
290 + function booktics_is_woocommerce_active() {
291 + if ( ! class_exists( 'WooCommerce' ) ) {
292 + return false;
293 + }
294 +
295 + if ( ! booktics_extension()->is_enabled( 'woocommerce' ) ) {
296 + return false;
297 + }
298 +
299 + return (bool) booktics_get_option( 'enable_woocommerce_payment' );
300 + }
301 +}
302 +
303 +
304 +if ( ! function_exists( 'booktics_is_valid_timezone' ) ) {
305 + /**
306 + * Check a timezone is valid or not
307 + *
308 + * @param string $timezone
309 + *
310 + * @return bool
311 + */
312 + function booktics_is_valid_timezone( $timezone ) {
313 + try {
314 + new DateTimeZone( $timezone );
315 + } catch ( Exception $e ) {
316 + return false;
317 + }
318 +
319 + return true;
320 + }
321 +}
322 +
323 +if ( ! function_exists( 'booktics_datetime' ) ) {
324 +
325 + /**
326 + * Booktics date time with timezone
327 + *
328 + * @param int | string $date
329 + * @param string $timezone
330 + *
331 + * @return string
332 + */
333 + function booktics_datetime( $format, $date, $timezone = '' ) {
334 + $date = is_int( $date ) ? gmdate( 'Y-m-d g:ia', $date ) : $date;
335 +
336 + if ( ! $timezone ) {
337 + $timezone = 'UTC';
338 + }
339 +
340 + // Create a DateTime object with the provided timestamp and time zone
341 + $datetime = new \DateTime( $date, new \DateTimeZone( $timezone ) );
342 +
343 + // Get the converted timestamp
344 + return $datetime->format( $format );
345 + }
346 +}
347 +
348 +if ( ! function_exists( 'booktics_convert_timezone' ) ) {
349 +
350 + /**
351 + * Convert date and time from timezone to another timezone
352 + *
353 + * @param string $format
354 + * @param string $date_time
355 + * @param string $from
356 + * @param string $to
357 + *
358 + * @return \DateTime
359 + */
360 + function booktics_convert_timezone( $date_time_string, $from, $to ) {
361 +
362 + if ( empty( $from ) || empty( $to ) ) {
363 + return new DateTime( $date_time_string );
364 + }
365 +
366 + $date_time_string = is_int( $date_time_string ) ? gmdate( 'Y-m-d g:ia', $date_time_string ) : gmdate( 'Y-m-d g:ia', strtotime( $date_time_string ) );
367 +
368 + $datetime = new DateTime( $date_time_string, new DateTimeZone( $from ) );
369 + $datetime->setTimezone( new DateTimeZone( $to ) );
370 +
371 + return $datetime;
372 + }
373 +}
374 +
375 +if ( ! function_exists( 'booktics_generate_username' ) ) {
376 + /**
377 + * Generate unique username
378 + *
379 + * @param string $email
380 + *
381 + * @return string
382 + */
383 + function booktics_generate_username( $email ) {
384 + $username = strtok( $email, '@' );
385 +
386 + if ( username_exists( $username ) ) {
387 + $username = $username . wp_rand( 10, 100 );
388 + }
389 +
390 + return $username;
391 + }
392 +}
393 +
394 +if ( ! function_exists( 'booktics_is_json' ) ) {
395 + /**
396 + * Check a string is valid json or not
397 + *
398 + * @param string $json_string
399 + *
400 + * @return bool
401 + */
402 + function booktics_is_json( $json_string ) {
403 + if ( ! is_string( $json_string ) ) {
404 + return false;
405 + }
406 +
407 + $data = json_decode( $json_string, true );
408 +
409 + return $data !== null && json_last_error() === JSON_ERROR_NONE;
410 + }
411 +}
412 +
413 +
414 +if ( ! function_exists( 'booktics_get_current_user' ) ) {
415 + /**
416 + * Get current user data
417 + *
418 + * @return array
419 + */
420 + function booktics_get_current_user() {
421 + $user_id = get_current_user_id();
422 + $user = get_userdata( $user_id );
423 +
424 + $user_data = array(
425 + 'id' => $user->ID,
426 + 'username' => $user->user_login,
427 + 'email' => $user->user_email,
428 + 'roles' => $user->roles,
429 + );
430 +
431 + return $user_data;
432 + }
433 +}
434 +
435 +if ( ! function_exists( 'booktics_validate' ) ) {
436 + /**
437 + * Validate user input
438 + *
439 + * @param array $request
440 + * @param array $rules
441 + *
442 + * @return bool | WP_Error
443 + */
444 + function booktics_validate( $request, $rules ) {
445 + $validator = new \Booktics\Validation\Validator( $request );
446 +
447 + $validator->set_rules( $rules );
448 +
449 + if ( ! $validator->validate() ) {
450 + return $validator->get_error();
451 + }
452 +
453 + return true;
454 + }
455 +}
456 +
457 +if ( ! function_exists( 'booktics_snake_case' ) ) {
458 + /**
459 + * Convert a string to snake_case.
460 + *
461 + * @param string $string
462 + *
463 + * @return string
464 + */
465 + function booktics_snake_case( string $string ): string {
466 + $string = preg_replace( '/([a-z])([A-Z])/', '$1_$2', $string );
467 +
468 + return strtolower( str_replace( array( '-', ' ' ), '_', $string ) );
469 + }
470 +}
471 +
472 +if ( ! function_exists( 'booktics_kebab_case' ) ) {
473 + /**
474 + * Convert a string to kebab-case.
475 + *
476 + * @param string $string
477 + *
478 + * @return string
479 + */
480 + function booktics_kebab_case( string $string ): string {
481 + $string = preg_replace( '/([a-z])([A-Z])/', '$1-$2', $string );
482 +
483 + return strtolower( str_replace( array( '_', ' ' ), '-', $string ) );
484 + }
485 +}
486 +
487 +if ( ! function_exists( 'booktics_camel_case' ) ) {
488 + /**
489 + * Convert a string to camelCase.
490 + *
491 + * @param string $string
492 + *
493 + * @return string
494 + */
495 + function booktics_camel_case( string $string ): string {
496 + $string = str_replace( array( '-', '_' ), ' ', strtolower( $string ) );
497 + $string = ucwords( $string );
498 + $string = str_replace( ' ', '', $string );
499 +
500 + return lcfirst( $string );
501 + }
502 +}
503 +
504 +if ( ! function_exists( 'booktics_pascal_case' ) ) {
505 + /**
506 + * Convert a string to PascalCase.
507 + *
508 + * @param string $string
509 + *
510 + * @return string
511 + */
512 + function booktics_pascal_case( string $string ): string {
513 + $string = str_replace( array( '-', '_' ), ' ', strtolower( $string ) );
514 + $string = ucwords( $string );
515 +
516 + return str_replace( ' ', '', $string );
517 + }
518 +}
519 +
520 +if ( ! function_exists( 'booktics_date_range_loop' ) ) {
521 + /**
522 + * Make date range iterable array
523 + *
524 + * @param $start
525 + * @param $end
526 + * @param $step
527 + * @param $format
528 + *
529 + * @return array
530 + * @throws Exception
531 + */
532 + function booktics_date_range_loop( $start, $end, $step = '+1 day', $format = 'Y-m-d' ) {
533 + $dates = array();
534 +
535 + $current_date = new DateTime( $start );
536 + $end_date = new DateTime( $end );
537 +
538 + while ( $current_date <= $end_date ) {
539 + $dates[] = $current_date->format( $format );
540 + $current_date->modify( $step );
541 + }
542 +
543 + return $dates;
544 + }
545 +
546 + if ( ! function_exists( 'booktics_get_option' ) ) {
547 + /**
548 + * Get option for Booktics
549 + *
550 + * @param string $key Option key
551 + * @param mixed $default Default value to return if option doesn't exist
552 + *
553 + * @return mixed
554 + */
555 + function booktics_get_option( $key = '', $default = false ) {
556 + return \Booktics\Settings\Settings::get( $key, $default );
557 + }
558 + }
559 +
560 + if ( ! function_exists( 'booktics_update_option' ) ) {
561 + /**
562 + * Update Booktics option
563 + *
564 + * @param string $key Option key
565 + * @param mixed $value Option value
566 + *
567 + * @return void
568 + */
569 + function booktics_update_option( $key, $value ) {
570 + \Booktics\Settings\Settings::update( $key, $value );
571 + }
572 + }
573 +}
574 +
575 +if ( ! function_exists( 'booktics_is_pro_active' ) ) {
576 + /**
577 + * Check Booktics Pro plugin activate or deactivate
578 + *
579 + * @return bool
580 + */
581 + function booktics_is_pro_active(): bool {
582 + return is_plugin_active( 'booktics-pro/booktics-pro.php' );
583 + }
584 +}
585 +
586 +if ( ! function_exists( 'booktics_extension' ) ) {
587 + /**
588 + * Get Booktics extension object from tools-sdk
589 + */
590 + function booktics_extension() {
591 + return ( new \Arraytics\ToolsSdk\Extension( 'booktics_available_modules', booktics_modules_list() ) );
592 + }
593 +}
594 +
595 +if ( ! function_exists( 'booktics_get_enabled_modules' ) ) {
596 + /**
597 + * Get enabled modules
598 + *
599 + * @return array
600 + */
601 + function booktics_get_enabled_modules(): array {
602 + $modules = get_option( 'booktics_available_modules', array() );
603 +
604 + if ( empty( $modules ) || ! is_array( $modules ) ) {
605 + return array();
606 + }
607 +
608 + $enabled = array();
609 +
610 + foreach ( $modules as $module_slug => $module_data ) {
611 + if ( is_array( $module_data ) && isset( $module_data['status'] ) && $module_data['status'] === 'on' ) {
612 + $enabled[] = $module_slug;
613 + } elseif ( is_string( $module_data ) && $module_data === 'on' ) {
614 + $enabled[] = $module_slug;
615 + }
616 + }
617 +
618 + return $enabled;
619 + }
620 +
621 + if ( ! function_exists( 'get_booktics_modules_list' ) ) {
622 + /**
623 + * Get Booktics modules list
624 + *
625 + * @return array
626 + */
627 + function booktics_modules_list(): array {
628 + $modules_list = array(
629 + 'stripe' => array(
630 + 'name' => 'stripe',
631 + 'slug' => 'stripe',
632 + 'type' => 'module',
633 + 'upgrade' => true,
634 + 'upgrade_link' => 'https://themewinter.com/purchase-history',
635 + 'status' => 'off',
636 + 'is_pro' => false,
637 + 'title' => __( 'Stripe', 'booktics' ),
638 + 'description' => __( 'It allows you to use stripe payment.', 'booktics' ),
639 + 'icon' => \Booktics\Extensions\Extension_Icon::get( 'stripe-addon' ),
640 + 'notice' => '',
641 + 'demo_link' => 'https://support.themewinter.com/docs/plugins/plugin-docs/integration/booktics-stripe',
642 + 'settings_link' => '',
643 + 'doc_link' => 'https://docs.arraytics.com/docs/booktics/payment-type/how-to-configure-the-stripe-in-booktics/',
644 + ),
645 + 'woocommerce' => array(
646 + 'name' => 'woocommerce',
647 + 'slug' => 'woocommerce',
648 + 'type' => 'module',
649 + 'upgrade' => true,
650 + 'upgrade_link' => 'https://themewinter.com/purchase-history',
651 + 'status' => 'off',
652 + 'is_pro' => false,
653 + 'title' => __( 'WooCommerce', 'booktics' ),
654 + 'description' => __( 'Take booking payments through WooCommerce checkout.', 'booktics' ),
655 + 'icon' => \Booktics\Extensions\Extension_Icon::get( 'woocommerce-addon' ),
656 + 'notice' => '',
657 + 'demo_link' => '',
658 + 'settings_link' => '',
659 + 'doc_link' => 'https://arraytics.com/docs/docs/booktics/integration/woocommerce-payment-in-booktics-setup-guide/',
660 + ),
661 + 'fluentcrm' => array(
662 + 'name' => 'fluentcrm',
663 + 'slug' => 'fluentcrm',
664 + 'type' => 'integration',
665 + 'upgrade' => true,
666 + 'upgrade_link' => 'https://themewinter.com/purchase-history',
667 + 'status' => 'off',
668 + 'is_pro' => false,
669 + 'title' => __( 'FluentCRM', 'booktics' ),
670 + 'description' => __( 'It allows you to use FluentCRM.', 'booktics' ),
671 + 'icon' => \Booktics\Extensions\Extension_Icon::get( 'booktics-fluentcrm-addon' ),
672 + 'notice' => '',
673 + 'demo_link' => 'https://support.themewinter.com/docs/plugins/plugin-docs/integration/booktics-fluentcrm',
674 + 'settings_link' => '',
675 + 'doc_link' => 'https://docs.arraytics.com/docs/booktics/integration/how-to-integrate-fluentcrm-with-booktics/',
676 + ),
677 + 'pabbly' => array(
678 + 'name' => 'pabbly',
679 + 'slug' => 'pabbly',
680 + 'type' => 'integration',
681 + 'upgrade' => true,
682 + 'upgrade_link' => 'https://themewinter.com/purchase-history',
683 + 'status' => 'off',
684 + 'is_pro' => false,
685 + 'title' => __( 'Pabbly', 'booktics' ),
686 + 'description' => __( 'Send booking data to Pabbly Connect via webhook.', 'booktics' ),
687 + 'icon' => \Booktics\Extensions\Extension_Icon::get( 'booktics-pabbly-addon' ),
688 + 'notice' => '',
689 + 'demo_link' => 'https://support.themewinter.com/docs/plugins/plugin-docs/integration/booktics-pabbly',
690 + 'settings_link' => '',
691 + 'doc_link' => 'https://docs.arraytics.com/docs/booktics/integration/how-to-integrate-booktics-with-pabbly/',
692 + ),
693 + 'zapier' => array(
694 + 'name' => 'zapier',
695 + 'slug' => 'zapier',
696 + 'type' => 'integration',
697 + 'upgrade' => true,
698 + 'upgrade_link' => 'https://themewinter.com/purchase-history',
699 + 'status' => 'off',
700 + 'is_pro' => false,
701 + 'title' => __( 'Zapier', 'booktics' ),
702 + 'description' => __( 'Send booking data to Zapier via webhook.', 'booktics' ),
703 + 'icon' => \Booktics\Extensions\Extension_Icon::get( 'booktics-zapier-addon' ),
704 + 'notice' => '',
705 + 'demo_link' => 'https://support.themewinter.com/docs/plugins/plugin-docs/integration/booktics-zapier',
706 + 'settings_link' => '',
707 + 'doc_link' => 'https://docs.arraytics.com/docs/booktics/integration/how-to-integrate-booktics-with-zapier/',
708 + ),
709 + 'zoho' => array(
710 + 'name' => 'zoho',
711 + 'slug' => 'zoho',
712 + 'type' => 'integration',
713 + 'upgrade' => true,
714 + 'upgrade_link' => 'https://themewinter.com/purchase-history',
715 + 'status' => 'off',
716 + 'is_pro' => false,
717 + 'title' => __( 'Zoho', 'booktics' ),
718 + 'description' => __( 'Send booking data to Zoho via webhook.', 'booktics' ),
719 + 'icon' => \Booktics\Extensions\Extension_Icon::get( 'booktics-zoho-addon' ),
720 + 'notice' => '',
721 + 'demo_link' => 'https://support.themewinter.com/docs/plugins/plugin-docs/integration/booktics-zoho',
722 + 'settings_link' => '',
723 + 'doc_link' => 'https://docs.arraytics.com/docs/booktics/integration/how-to-integrate-booktics-with-zoho-crm/',
724 + ),
725 + 'mailpoet' => array(
726 + 'name' => 'mailpoet',
727 + 'slug' => 'mailpoet',
728 + 'type' => 'integration',
729 + 'upgrade' => true,
730 + 'upgrade_link' => 'https://themewinter.com/purchase-history',
731 + 'status' => 'off',
732 + 'is_pro' => false,
733 + 'plugin_slug' => 'mailpoet',
734 + 'title' => __( 'MailPoet', 'booktics' ),
735 + 'description' => __( 'Add booking customers to your MailPoet lists.', 'booktics' ),
736 + 'icon' => \Booktics\Extensions\Extension_Icon::get( 'booktics-mailpoet-addon' ),
737 + 'notice' => '',
738 + 'demo_link' => 'https://support.themewinter.com/docs/plugins/plugin-docs/integration/booktics-mailpoet',
739 + 'settings_link' => '',
740 + 'doc_link' => 'https://docs.arraytics.com/docs/booktics/integration/how-to-integrate-booktics-with-mailpoet/',
741 + ),
742 + 'google-calendar' => array(
743 + 'name' => 'google-calendar',
744 + 'slug' => 'google-calendar',
745 + 'type' => 'module',
746 + 'upgrade' => true,
747 + 'upgrade_link' => 'https://themewinter.com/purchase-history',
748 + 'status' => 'off',
749 + 'is_pro' => false,
750 + 'title' => __( 'Booktics Google Calendar', 'booktics' ),
751 + 'description' => __( 'It allows you to use google calendar.', 'booktics' ),
752 + 'icon' => \Booktics\Extensions\Extension_Icon::get( 'booktics-google-calendar' ),
753 + 'notice' => '',
754 + 'demo_link' => 'https://support.themewinter.com/docs/plugins/plugin-docs/integration/booktics-google-calendar',
755 + 'settings_link' => '',
756 + 'doc_link' => 'https://docs.arraytics.com/docs/booktics/integration/how-to-integrate-google-calendar-with-booktics/',
757 + ),
758 + 'outlook-calendar' => array(
759 + 'name' => 'outlook-calendar',
760 + 'slug' => 'outlook-calendar',
761 + 'type' => 'module',
762 + 'upgrade' => true,
763 + 'upgrade_link' => 'https://themewinter.com/purchase-history',
764 + 'status' => 'off',
765 + 'is_pro' => true,
766 + 'title' => __( 'Booktics Outlook Calendar', 'booktics' ),
767 + 'description' => __( 'It allows you to use outlook calendar.', 'booktics' ),
768 + 'icon' => \Booktics\Extensions\Extension_Icon::get( 'booktics-outlook-calendar' ),
769 + 'notice' => '',
770 + 'demo_link' => 'https://support.themewinter.com/docs/plugins/plugin-docs/integration/booktics-outlook-calendar-addon',
771 + 'settings_link' => '',
772 + 'doc_link' => 'https://docs.arraytics.com/docs/booktics/integration/how-to-integrate-outlook-calendar-with-booktics/',
773 + ),
774 + 'paypal' => array(
775 + 'name' => 'paypal',
776 + 'slug' => 'paypal',
777 + 'type' => 'module',
778 + 'upgrade' => true,
779 + 'upgrade_link' => 'https://themewinter.com/purchase-history',
780 + 'status' => 'off',
781 + 'is_pro' => true,
782 + 'title' => __( 'PayPal', 'booktics' ),
783 + 'description' => __( 'It allows you to use PayPal.', 'booktics' ),
784 + 'icon' => \Booktics\Extensions\Extension_Icon::get( 'booktics-paypal-addon' ),
785 + 'notice' => '',
786 + 'demo_link' => 'https://support.themewinter.com/docs/plugins/plugin-docs/integration/booktics-paypal-addon',
787 + 'settings_link' => '',
788 + 'doc_link' => 'https://docs.arraytics.com/docs/booktics/payment-type/how-to-configure-the-paypal-in-booktics-pro/',
789 + ),
790 + );
791 +
792 + return $modules_list;
793 + }
794 + }
795 +}
796 +
797 +if ( ! function_exists( 'booktics_log' ) ) {
798 + /**
799 + * Write a debug line, but only when the site has debug logging switched on.
800 + *
801 + * @param string $message Message to log.
802 + *
803 + * @return void
804 + */
805 + function booktics_log( $message ) {
806 + if ( ! defined( 'WP_DEBUG_LOG' ) || ! WP_DEBUG_LOG ) {
807 + return;
808 + }
809 +
810 + // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Guarded above; this is the plugin's only debug sink.
811 + error_log( 'Booktics: ' . $message );
812 + }
813 +}
814 +
815 +if ( ! function_exists( 'booktics_get_multisite_table_name' ) ) {
816 + /**
817 + * Get multisite-safe table name
818 + *
819 + * @param string $table_name
820 + *
821 + * @return string
822 + */
823 + function booktics_get_table_name( $table_name ) {
824 + global $wpdb;
825 + $prefix = $wpdb->prefix;
826 + if ( strpos( $table_name, $prefix ) === 0 ) {
827 + return $table_name;
828 + }
829 + return $wpdb->prefix . $table_name;
830 + }
831 +}
832 +
833 +if ( ! function_exists( 'booktics_handle_conflicting_plugins' ) ) {
834 + /**
835 + * Deactivate conflicting plugins if active and check if they exist.
836 + */
837 + function booktics_handle_conflicting_plugins() {
838 + // Avoid during AJAX or REST requests
839 + if ( wp_doing_ajax() || defined( 'REST_REQUEST' ) ) {
840 + return;
841 + }
842 +
843 + // Only proceed if user is logged in and can manage plugins
844 + if ( ! is_user_logged_in() || ! current_user_can( 'activate_plugins' ) ) {
845 + return;
846 + }
847 +
848 + // Prevent running repeatedly on every page load using a transient
849 + if ( get_transient( 'booktics_conflict_check_done' ) ) {
850 + return;
851 + }
852 +
853 + $conflicting_plugins = array(
854 + 'booktics-google-calendar-addon/booktics-google-calendar.php',
855 + 'booktics-outlook-calendar-addon/booktics-outlook-calendar-addon.php',
856 + 'booktics-paypal-addon/booktics-paypal-addon.php',
857 + 'booktics-stripe-addon/booktics_stripe.php',
858 + 'booktics-fluentcrm-addon/booktics-fluentcrm-addon.php',
859 + );
860 +
861 + $active_plugins = get_option( 'active_plugins', array() );
862 + $deactivated_list = array();
863 +
864 + foreach ( $conflicting_plugins as $plugin_file ) {
865 + $plugin_path = WP_PLUGIN_DIR . '/' . $plugin_file;
866 +
867 + if ( file_exists( $plugin_path ) ) {
868 + if ( in_array( $plugin_file, $active_plugins, true ) ) {
869 + deactivate_plugins( $plugin_file );
870 + $plugin_data = get_plugin_data( $plugin_path );
871 + $deactivated_list[] = $plugin_data['Name'];
872 + }
873 + }
874 + }
875 +
876 + if ( ! empty( $deactivated_list ) ) {
877 + // Store data in transient for 1 minute to show notice once
878 + set_transient( 'booktics_deactivated_plugins_notice', $deactivated_list, MINUTE_IN_SECONDS );
879 + }
880 +
881 + // Set the conflict check as done (even if nothing was deactivated)
882 + set_transient( 'booktics_conflict_check_done', true, MINUTE_IN_SECONDS );
883 + }
884 +}
885 +
886 +if ( ! function_exists( 'booktics_show_conflict_deactivation_notice' ) ) {
887 + /**
888 + * Show a one-time admin notice for deactivated conflicting plugins.
889 + */
890 + function booktics_show_conflict_deactivation_notice() {
891 + if ( ! current_user_can( 'activate_plugins' ) ) {
892 + return;
893 + }
894 +
895 + $deactivated_list = get_transient( 'booktics_deactivated_plugins_notice' );
896 +
897 + if ( ! empty( $deactivated_list ) ) {
898 + delete_transient( 'booktics_deactivated_plugins_notice' );
899 +
900 + $plugin_names = implode( ', ', array_map( 'esc_html', $deactivated_list ) );
901 + ?>
902 + <div class="notice notice-warning is-dismissible">
903 + <p>
904 + <?php echo esc_html__( 'No need for addons! The following addon plugins have been added as modules in Booktics:', 'booktics' ); ?>
905 + <strong><?php echo esc_html( $plugin_names ); ?></strong>
906 + <br><?php echo esc_html__( 'Please deactivate and remove these plugins to avoid future issues. Don\'t worry, it will not affect your existing data and the features will remain the same.', 'booktics' ); ?>
907 + </p>
908 + </div>
909 + <?php
910 + }
911 + }
912 +}
913 +
914 +if ( ! function_exists( 'booktics_time_to_gmt' ) ) {
915 + /**
916 + * Convert time from site timezone to GMT/UTC
917 + *
918 + * @param string $time Time string (e.g., "9:00 AM", "09:00:00")
919 + * @param string|int $context_date Date context (Y-m-d or 0 for regular schedules)
920 + * @return string Time in H:i:s format (GMT/UTC)
921 + * @since 1.0.15
922 + */
923 + function booktics_time_to_gmt( string $time, $context_date = 0 ): string {
924 + // Skip '0' placeholder for holidays
925 + if ( $time === '0' || $time === 0 ) {
926 + return '0';
927 + }
928 +
929 + // For regular schedules (context_date = 0), use current date as context
930 + // For custom schedules, use the actual date for DST-aware conversion
931 + $date = ( $context_date === 0 || $context_date === '0' || empty( $context_date ) )
932 + ? gmdate( 'Y-m-d' )
933 + : $context_date;
934 +
935 + $timestamp = strtotime( $time );
936 + if ( $timestamp === false ) {
937 + return $time;
938 + }
939 + $formatted_time = gmdate( 'H:i:s', $timestamp );
940 + $datetime_string = $date . ' ' . $formatted_time;
941 +
942 + $gmt_datetime = get_gmt_from_date( $datetime_string );
943 +
944 + // Extract and return only the time part
945 + return gmdate( 'H:i:s', strtotime( $gmt_datetime ) );
946 + }
947 +}
948 +
949 +if ( ! function_exists( 'booktics_is_admin_or_team_member' ) ) {
950 + /**
951 + * Check if the current user is an admin or a Booktics team member.
952 + *
953 + * @return bool
954 + * @since 1.0.16
955 + */
956 + function booktics_is_admin_or_team_member() {
957 + if ( current_user_can( 'manage_options' ) ) {
958 + return true;
959 + }
960 + if ( is_user_logged_in() ) {
961 + $roles = (array) wp_get_current_user()->roles;
962 + if ( in_array( 'booktics_team_member', $roles, true ) ) {
963 + return true;
964 + }
965 + }
966 + return false;
967 + }
968 +}
969 +
970 +if ( ! function_exists( 'booktics_is_team_member' ) ) {
971 + function booktics_is_team_member() {
972 + if ( is_user_logged_in() ) {
973 + $roles = (array) wp_get_current_user()->roles;
974 + if ( in_array( 'booktics_team_member', $roles, true ) ) {
975 + return true;
976 + }
977 + }
978 + return false;
979 + }
980 +}
981 +
982 +if ( ! function_exists( 'booktics_time_from_gmt' ) ) {
983 + /**
984 + * Convert time from GMT/UTC to site timezone
985 + *
986 + * @param string $time Time string in UTC (e.g., "14:00:00")
987 + * @param string|int $context_date Date context (Y-m-d or 0 for regular schedules)
988 + * @return string Time in H:i:s format (site timezone)
989 + * @since 1.0.15
990 + */
991 + function booktics_time_from_gmt( string $time, $context_date = 0 ): string {
992 + // Skip '0' placeholder for holidays
993 + if ( $time === '0' || $time === 0 ) {
994 + return '0';
995 + }
996 +
997 + $date = ( $context_date === 0 || $context_date === '0' || empty( $context_date ) )
998 + ? gmdate( 'Y-m-d' )
999 + : $context_date;
1000 +
1001 + $timestamp = strtotime( $time );
1002 + if ( $timestamp === false ) {
1003 + return $time;
1004 + }
1005 + $formatted_time = gmdate( 'H:i:s', $timestamp );
1006 + $gmt_datetime_string = $date . ' ' . $formatted_time;
1007 +
1008 + $local_datetime = get_date_from_gmt( $gmt_datetime_string );
1009 +
1010 + return gmdate( 'H:i:s', strtotime( $local_datetime ) );
1011 + }
1012 +}
1013 +
1014 +if ( ! function_exists( 'booktics_safe_maybe_unserialize' ) ) {
1015 + /**
1016 + * Safely unserialize a value without ever instantiating PHP objects.
1017 + *
1018 + * @param mixed $value Possibly serialized value.
1019 + *
1020 + * @return mixed Unserialized value, or the original value when not serialized.
1021 + */
1022 + function booktics_safe_maybe_unserialize( $value ) {
1023 + if ( is_string( $value ) && is_serialized( $value ) ) {
1024 + // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_unserialize -- allowed_classes => false prevents PHP Object Injection.
1025 + return @unserialize( trim( $value ), array( 'allowed_classes' => false ) );
1026 + }
1027 +
1028 + return $value;
1029 + }
1030 +}
1031 +
1032 +if ( ! function_exists( 'booktics_sanitize_id_string' ) ) {
1033 + /**
1034 + * Sanitize a free-form identifier received from a request.
1035 + *
1036 + * Returns a plain-text string id (e.g. 'default' or a client-generated id such
1037 + * as a uuid/nanoid). A legitimate identifier is never a PHP-serialized string,
1038 + * so any value that looks serialized is dropped to '' — this neutralizes PHP
1039 + * Object Injection payloads at the storage boundary while leaving every real id
1040 + * untouched. Non-scalar input (arrays/objects) is rejected to ''.
1041 + *
1042 + * @param mixed $value Raw value from the request.
1043 + *
1044 + * @return string Sanitized identifier, or '' when missing, non-scalar, or serialized.
1045 + */
1046 + function booktics_sanitize_id_string( $value ) {
1047 + if ( ! is_scalar( $value ) ) {
1048 + return '';
1049 + }
1050 +
1051 + $value = sanitize_text_field( (string) $value );
1052 +
1053 + return is_serialized( $value ) ? '' : $value;
1054 + }
1055 +}
1056 +
1057 +if ( ! function_exists( 'booktics_aisentic_identity' ) ) {
1058 + /**
1059 + * Build the site identity offered to Aisentic when the user opts in.
1060 + *
1061 + * The consent UI and the send path both read this helper, so the email shown
1062 + * on the consent checkbox is always the email that actually leaves the site.
1063 + *
1064 + * @return array{name: string, email: string, site_url: string}
1065 + */
1066 + function booktics_aisentic_identity(): array {
1067 + $name = booktics_get_option( 'business_name', get_bloginfo( 'name' ) );
1068 + $email = booktics_get_option( 'business_email', get_option( 'admin_email' ) );
1069 +
1070 + return array(
1071 + 'name' => sanitize_text_field( (string) $name ),
1072 + 'email' => sanitize_email( (string) $email ),
1073 + 'site_url' => esc_url_raw( site_url() ),
1074 + );
1075 + }
1076 +}
1077 +
1078 +if ( ! function_exists( 'booktics_aisentic_is_registered' ) ) {
1079 + /**
1080 + * Whether Aisentic already holds an API key.
1081 + *
1082 + * Reading Aisentic's own storage instead of our consent option means anyone
1083 + * who connected from Aisentic's screens is never nagged by our banner.
1084 + *
1085 + * @return bool True when an Aisentic API key is present.
1086 + */
1087 + function booktics_aisentic_is_registered(): bool {
1088 + if ( function_exists( 'aisentic_get_option' ) ) {
1089 + return ! empty( aisentic_get_option( 'llmProviders.aisentic.apiKey', '' ) );
1090 + }
1091 +
1092 + $settings = get_option( 'aisentic_settings', array() );
1093 +
1094 + return ! empty( $settings['llmProviders']['aisentic']['apiKey'] );
1095 + }
1096 +}
1097 +
1098 +if ( ! function_exists( 'booktics_get_store_currency' ) ) {
1099 + /**
1100 + * The currency the merchant configured for the store, lowercased.
1101 + *
1102 + * Public checkout must always price in this currency. A currency sent by the
1103 + * buyer cannot be trusted: the total is computed server side in the store
1104 + * currency, so relabeling it as another currency changes what is charged.
1105 + *
1106 + * @return string Lowercase ISO 4217 currency code.
1107 + */
1108 + function booktics_get_store_currency(): string {
1109 + $currency = strtolower( trim( (string) booktics_get_option( 'currency', 'USD' ) ) );
1110 +
1111 + // An empty or malformed setting would make every Stripe charge fail.
1112 + return preg_match( '/^[a-z]{3}$/', $currency ) ? $currency : 'usd';
1113 + }
1114 +}
1115 +
1116 +if ( ! function_exists( 'booktics_amount_to_minor_units' ) ) {
1117 + /**
1118 + * Convert a decimal amount to the smallest unit of a currency.
1119 + *
1120 + * Stripe takes amounts in minor units, and not every currency has two
1121 + * decimals: 100 JPY is sent as 100, 100 USD as 10000 and 100 KWD as 100000.
1122 + * ISK and UGX are zero-decimal but Stripe still expects them as whole
1123 + * amounts times 100, so 5 UGX is sent as 500.
1124 + *
1125 + * @see https://docs.stripe.com/currencies#minor-units
1126 + *
1127 + * @param float|int|string $amount Amount in major units, e.g. 19.99.
1128 + * @param string $currency ISO 4217 currency code.
1129 + *
1130 + * @return int Amount in minor units.
1131 + */
1132 + function booktics_amount_to_minor_units( $amount, string $currency ): int {
1133 + $zero_decimal = array( 'bif', 'clp', 'djf', 'gnf', 'jpy', 'kmf', 'krw', 'mga', 'pyg', 'rwf', 'vnd', 'vuv', 'xaf', 'xof', 'xpf' );
1134 + $whole_x100 = array( 'isk', 'ugx' );
1135 + $three_decimal = array( 'bhd', 'jod', 'kwd', 'omr', 'tnd' );
1136 + $currency = strtolower( $currency );
1137 + $amount = (float) $amount;
1138 +
1139 + if ( in_array( $currency, $zero_decimal, true ) ) {
1140 + return (int) round( $amount );
1141 + }
1142 +
1143 + if ( in_array( $currency, $whole_x100, true ) ) {
1144 + return (int) round( $amount ) * 100;
1145 + }
1146 +
1147 + if ( in_array( $currency, $three_decimal, true ) ) {
1148 + // Stripe requires the last digit of three-decimal amounts to be 0.
1149 + return (int) ( round( $amount * 100 ) * 10 );
1150 + }
1151 +
1152 + return (int) round( $amount * 100 );
1153 + }
1154 +}