PluginProbe
Booktics – Appointment Booking Calendar for Service Businesses / 1.0.24
Booktics – Appointment Booking Calendar for Service Businesses v1.0.24
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
booktics / utils / global-helper.php

global-helper.php in Booktics – Appointment Booking Calendar for Service Businesses 1.0.24, at utils/global-helper.php

1,074 lines 36.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if ( ! defined( 'ABSPATH' ) && PHP_SAPI !== 'cli' ) { exit; }
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 whether WooCommerce payments are usable.
278 *
279 * True only when WooCommerce itself is running, the module is enabled and
280 * the payment setting is on. Any one of the three being false means the
281 * gateway must not be offered.
282 *
283 * @return bool
284 */
285 function booktics_is_woocommerce_active() {
286 if ( ! class_exists( 'WooCommerce' ) ) {
287 return false;
288 }
289
290 if ( ! booktics_extension()->is_enabled( 'woocommerce' ) ) {
291 return false;
292 }
293
294 return (bool) booktics_get_option( 'enable_woocommerce_payment' );
295 }
296 }
297
298
299 if ( ! function_exists( 'booktics_is_valid_timezone' ) ) {
300 /**
301 * Check a timezone is valid or not
302 *
303 * @param string $timezone
304 *
305 * @return bool
306 */
307 function booktics_is_valid_timezone( $timezone ) {
308 try {
309 new DateTimeZone( $timezone );
310 } catch ( Exception $e ) {
311 return false;
312 }
313
314 return true;
315 }
316 }
317
318 if ( ! function_exists( 'booktics_datetime' ) ) {
319
320 /**
321 * Booktics date time with timezone
322 *
323 * @param int | string $date
324 * @param string $timezone
325 *
326 * @return string
327 */
328 function booktics_datetime( $format, $date, $timezone = '' ) {
329 $date = is_int( $date ) ? gmdate( 'Y-m-d g:ia', $date ) : $date;
330
331 if ( ! $timezone ) {
332 $timezone = 'UTC';
333 }
334
335 // Create a DateTime object with the provided timestamp and time zone
336 $datetime = new \DateTime( $date, new \DateTimeZone( $timezone ) );
337
338 // Get the converted timestamp
339 return $datetime->format( $format );
340 }
341 }
342
343 if ( ! function_exists( 'booktics_convert_timezone' ) ) {
344
345 /**
346 * Convert date and time from timezone to another timezone
347 *
348 * @param string $format
349 * @param string $date_time
350 * @param string $from
351 * @param string $to
352 *
353 * @return \DateTime
354 */
355 function booktics_convert_timezone( $date_time_string, $from, $to ) {
356
357 if ( empty( $from ) || empty( $to ) ) {
358 return new DateTime( $date_time_string );
359 }
360
361 $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 ) );
362
363 $datetime = new DateTime( $date_time_string, new DateTimeZone( $from ) );
364 $datetime->setTimezone( new DateTimeZone( $to ) );
365
366 return $datetime;
367 }
368 }
369
370 if ( ! function_exists( 'booktics_generate_username' ) ) {
371 /**
372 * Generate unique username
373 *
374 * @param string $email
375 *
376 * @return string
377 */
378 function booktics_generate_username( $email ) {
379 $username = strtok( $email, '@' );
380
381 if ( username_exists( $username ) ) {
382 $username = $username . wp_rand( 10, 100 );
383 }
384
385 return $username;
386 }
387 }
388
389 if ( ! function_exists( 'booktics_is_json' ) ) {
390 /**
391 * Check a string is valid json or not
392 *
393 * @param string $json_string
394 *
395 * @return bool
396 */
397 function booktics_is_json( $json_string ) {
398 if ( ! is_string( $json_string ) ) {
399 return false;
400 }
401
402 $data = json_decode( $json_string, true );
403
404 return $data !== null && json_last_error() === JSON_ERROR_NONE;
405 }
406 }
407
408
409 if ( ! function_exists( 'booktics_get_current_user' ) ) {
410 /**
411 * Get current user data
412 *
413 * @return array
414 */
415 function booktics_get_current_user() {
416 $user_id = get_current_user_id();
417 $user = get_userdata( $user_id );
418
419 $user_data = array(
420 'id' => $user->ID,
421 'username' => $user->user_login,
422 'email' => $user->user_email,
423 'roles' => $user->roles,
424 );
425
426 return $user_data;
427 }
428 }
429
430 if ( ! function_exists( 'booktics_validate' ) ) {
431 /**
432 * Validate user input
433 *
434 * @param array $request
435 * @param array $rules
436 *
437 * @return bool | WP_Error
438 */
439 function booktics_validate( $request, $rules ) {
440 $validator = new \Booktics\Validation\Validator( $request );
441
442 $validator->set_rules( $rules );
443
444 if ( ! $validator->validate() ) {
445 return $validator->get_error();
446 }
447
448 return true;
449 }
450 }
451
452 if ( ! function_exists( 'booktics_snake_case' ) ) {
453 /**
454 * Convert a string to snake_case.
455 *
456 * @param string $string
457 *
458 * @return string
459 */
460 function booktics_snake_case( string $string ): string {
461 $string = preg_replace( '/([a-z])([A-Z])/', '$1_$2', $string );
462
463 return strtolower( str_replace( array( '-', ' ' ), '_', $string ) );
464 }
465 }
466
467 if ( ! function_exists( 'booktics_kebab_case' ) ) {
468 /**
469 * Convert a string to kebab-case.
470 *
471 * @param string $string
472 *
473 * @return string
474 */
475 function booktics_kebab_case( string $string ): string {
476 $string = preg_replace( '/([a-z])([A-Z])/', '$1-$2', $string );
477
478 return strtolower( str_replace( array( '_', ' ' ), '-', $string ) );
479 }
480 }
481
482 if ( ! function_exists( 'booktics_camel_case' ) ) {
483 /**
484 * Convert a string to camelCase.
485 *
486 * @param string $string
487 *
488 * @return string
489 */
490 function booktics_camel_case( string $string ): string {
491 $string = str_replace( array( '-', '_' ), ' ', strtolower( $string ) );
492 $string = ucwords( $string );
493 $string = str_replace( ' ', '', $string );
494
495 return lcfirst( $string );
496 }
497 }
498
499 if ( ! function_exists( 'booktics_pascal_case' ) ) {
500 /**
501 * Convert a string to PascalCase.
502 *
503 * @param string $string
504 *
505 * @return string
506 */
507 function booktics_pascal_case( string $string ): string {
508 $string = str_replace( array( '-', '_' ), ' ', strtolower( $string ) );
509 $string = ucwords( $string );
510
511 return str_replace( ' ', '', $string );
512 }
513 }
514
515 if ( ! function_exists( 'booktics_date_range_loop' ) ) {
516 /**
517 * Make date range iterable array
518 *
519 * @param $start
520 * @param $end
521 * @param $step
522 * @param $format
523 *
524 * @return array
525 * @throws Exception
526 */
527 function booktics_date_range_loop( $start, $end, $step = '+1 day', $format = 'Y-m-d' ) {
528 $dates = array();
529
530 $current_date = new DateTime( $start );
531 $end_date = new DateTime( $end );
532
533 while ( $current_date <= $end_date ) {
534 $dates[] = $current_date->format( $format );
535 $current_date->modify( $step );
536 }
537
538 return $dates;
539 }
540
541 if ( ! function_exists( 'booktics_get_option' ) ) {
542 /**
543 * Get option for Booktics
544 *
545 * @param string $key Option key
546 * @param mixed $default Default value to return if option doesn't exist
547 *
548 * @return mixed
549 */
550 function booktics_get_option( $key = '', $default = false ) {
551 return \Booktics\Settings\Settings::get( $key, $default );
552 }
553 }
554
555 if ( ! function_exists( 'booktics_update_option' ) ) {
556 /**
557 * Update Booktics option
558 *
559 * @param string $key Option key
560 * @param mixed $value Option value
561 *
562 * @return void
563 */
564 function booktics_update_option( $key, $value ) {
565 \Booktics\Settings\Settings::update( $key, $value );
566 }
567 }
568 }
569
570 if ( ! function_exists( 'booktics_is_pro_active' ) ) {
571 /**
572 * Check Booktics Pro plugin activate or deactivate
573 *
574 * @return bool
575 */
576 function booktics_is_pro_active(): bool {
577 return is_plugin_active( 'booktics-pro/booktics-pro.php' );
578 }
579 }
580
581 if ( ! function_exists( 'booktics_extension' ) ) {
582 /**
583 * Get Booktics extension object from tools-sdk
584 */
585 function booktics_extension() {
586 return ( new \Arraytics\ToolsSdk\Extension( 'booktics_available_modules', booktics_modules_list() ) );
587 }
588 }
589
590 if ( ! function_exists( 'booktics_get_enabled_modules' ) ) {
591 /**
592 * Get enabled modules
593 *
594 * @return array
595 */
596 function booktics_get_enabled_modules(): array {
597 $modules = get_option( 'booktics_available_modules', array() );
598
599 if ( empty( $modules ) || ! is_array( $modules ) ) {
600 return array();
601 }
602
603 $enabled = array();
604
605 foreach ( $modules as $module_slug => $module_data ) {
606 if ( is_array( $module_data ) && isset( $module_data['status'] ) && $module_data['status'] === 'on' ) {
607 $enabled[] = $module_slug;
608 } elseif ( is_string( $module_data ) && $module_data === 'on' ) {
609 $enabled[] = $module_slug;
610 }
611 }
612
613 return $enabled;
614 }
615
616 if ( ! function_exists( 'get_booktics_modules_list' ) ) {
617 /**
618 * Get Booktics modules list
619 *
620 * @return array
621 */
622 function booktics_modules_list(): array {
623 $modules_list = array(
624 'stripe' => array(
625 'name' => 'stripe',
626 'slug' => 'stripe',
627 'type' => 'module',
628 'upgrade' => true,
629 'upgrade_link' => 'https://themewinter.com/purchase-history',
630 'status' => 'off',
631 'is_pro' => false,
632 'title' => __( 'Stripe', 'booktics' ),
633 'description' => __( 'It allows you to use stripe payment.', 'booktics' ),
634 'icon' => \Booktics\Extensions\Extension_Icon::get( 'stripe-addon' ),
635 'notice' => '',
636 'demo_link' => 'https://support.themewinter.com/docs/plugins/plugin-docs/integration/booktics-stripe',
637 'settings_link' => '',
638 'doc_link' => 'https://docs.arraytics.com/docs/booktics/payment-type/how-to-configure-the-stripe-in-booktics/',
639 ),
640 'woocommerce' => array(
641 'name' => 'woocommerce',
642 'slug' => 'woocommerce',
643 'type' => 'module',
644 'upgrade' => true,
645 'upgrade_link' => 'https://themewinter.com/purchase-history',
646 'status' => 'off',
647 'is_pro' => false,
648 'title' => __( 'WooCommerce', 'booktics' ),
649 'description' => __( 'Take booking payments through WooCommerce checkout.', 'booktics' ),
650 'icon' => \Booktics\Extensions\Extension_Icon::get( 'woocommerce-addon' ),
651 'notice' => '',
652 'demo_link' => '',
653 'settings_link' => '',
654 'doc_link' => 'https://arraytics.com/docs/docs/booktics/integration/woocommerce-payment-in-booktics-setup-guide/',
655 ),
656 'fluentcrm' => array(
657 'name' => 'fluentcrm',
658 'slug' => 'fluentcrm',
659 'type' => 'integration',
660 'upgrade' => true,
661 'upgrade_link' => 'https://themewinter.com/purchase-history',
662 'status' => 'off',
663 'is_pro' => false,
664 'title' => __( 'FluentCRM', 'booktics' ),
665 'description' => __( 'It allows you to use FluentCRM.', 'booktics' ),
666 'icon' => \Booktics\Extensions\Extension_Icon::get( 'booktics-fluentcrm-addon' ),
667 'notice' => '',
668 'demo_link' => 'https://support.themewinter.com/docs/plugins/plugin-docs/integration/booktics-fluentcrm',
669 'settings_link' => '',
670 'doc_link' => 'https://docs.arraytics.com/docs/booktics/integration/how-to-integrate-fluentcrm-with-booktics/',
671 ),
672 'pabbly' => array(
673 'name' => 'pabbly',
674 'slug' => 'pabbly',
675 'type' => 'integration',
676 'upgrade' => true,
677 'upgrade_link' => 'https://themewinter.com/purchase-history',
678 'status' => 'off',
679 'is_pro' => false,
680 'title' => __( 'Pabbly', 'booktics' ),
681 'description' => __( 'Send booking data to Pabbly Connect via webhook.', 'booktics' ),
682 'icon' => \Booktics\Extensions\Extension_Icon::get( 'booktics-pabbly-addon' ),
683 'notice' => '',
684 'demo_link' => 'https://support.themewinter.com/docs/plugins/plugin-docs/integration/booktics-pabbly',
685 'settings_link' => '',
686 'doc_link' => 'https://docs.arraytics.com/docs/booktics/integration/how-to-integrate-booktics-with-pabbly/',
687 ),
688 'zapier' => array(
689 'name' => 'zapier',
690 'slug' => 'zapier',
691 'type' => 'integration',
692 'upgrade' => true,
693 'upgrade_link' => 'https://themewinter.com/purchase-history',
694 'status' => 'off',
695 'is_pro' => false,
696 'title' => __( 'Zapier', 'booktics' ),
697 'description' => __( 'Send booking data to Zapier via webhook.', 'booktics' ),
698 'icon' => \Booktics\Extensions\Extension_Icon::get( 'booktics-zapier-addon' ),
699 'notice' => '',
700 'demo_link' => 'https://support.themewinter.com/docs/plugins/plugin-docs/integration/booktics-zapier',
701 'settings_link' => '',
702 'doc_link' => 'https://docs.arraytics.com/docs/booktics/integration/how-to-integrate-booktics-with-zapier/',
703 ),
704 'zoho' => array(
705 'name' => 'zoho',
706 'slug' => 'zoho',
707 'type' => 'integration',
708 'upgrade' => true,
709 'upgrade_link' => 'https://themewinter.com/purchase-history',
710 'status' => 'off',
711 'is_pro' => false,
712 'title' => __( 'Zoho', 'booktics' ),
713 'description' => __( 'Send booking data to Zoho via webhook.', 'booktics' ),
714 'icon' => \Booktics\Extensions\Extension_Icon::get( 'booktics-zoho-addon' ),
715 'notice' => '',
716 'demo_link' => 'https://support.themewinter.com/docs/plugins/plugin-docs/integration/booktics-zoho',
717 'settings_link' => '',
718 'doc_link' => 'https://docs.arraytics.com/docs/booktics/integration/how-to-integrate-booktics-with-zoho-crm/',
719 ),
720 'mailpoet' => array(
721 'name' => 'mailpoet',
722 'slug' => 'mailpoet',
723 'type' => 'integration',
724 'upgrade' => true,
725 'upgrade_link' => 'https://themewinter.com/purchase-history',
726 'status' => 'off',
727 'is_pro' => false,
728 'plugin_slug' => 'mailpoet',
729 'title' => __( 'MailPoet', 'booktics' ),
730 'description' => __( 'Add booking customers to your MailPoet lists.', 'booktics' ),
731 'icon' => \Booktics\Extensions\Extension_Icon::get( 'booktics-mailpoet-addon' ),
732 'notice' => '',
733 'demo_link' => 'https://support.themewinter.com/docs/plugins/plugin-docs/integration/booktics-mailpoet',
734 'settings_link' => '',
735 'doc_link' => 'https://docs.arraytics.com/docs/booktics/integration/how-to-integrate-booktics-with-mailpoet/',
736 ),
737 'google-calendar' => array(
738 'name' => 'google-calendar',
739 'slug' => 'google-calendar',
740 'type' => 'module',
741 'upgrade' => true,
742 'upgrade_link' => 'https://themewinter.com/purchase-history',
743 'status' => 'off',
744 'is_pro' => false,
745 'title' => __( 'Booktics Google Calendar', 'booktics' ),
746 'description' => __( 'It allows you to use google calendar.', 'booktics' ),
747 'icon' => \Booktics\Extensions\Extension_Icon::get( 'booktics-google-calendar' ),
748 'notice' => '',
749 'demo_link' => 'https://support.themewinter.com/docs/plugins/plugin-docs/integration/booktics-google-calendar',
750 'settings_link' => '',
751 'doc_link' => 'https://docs.arraytics.com/docs/booktics/integration/how-to-integrate-google-calendar-with-booktics/',
752 ),
753 'outlook-calendar' => array(
754 'name' => 'outlook-calendar',
755 'slug' => 'outlook-calendar',
756 'type' => 'module',
757 'upgrade' => true,
758 'upgrade_link' => 'https://themewinter.com/purchase-history',
759 'status' => 'off',
760 'is_pro' => true,
761 'title' => __( 'Booktics Outlook Calendar', 'booktics' ),
762 'description' => __( 'It allows you to use outlook calendar.', 'booktics' ),
763 'icon' => \Booktics\Extensions\Extension_Icon::get( 'booktics-outlook-calendar' ),
764 'notice' => '',
765 'demo_link' => 'https://support.themewinter.com/docs/plugins/plugin-docs/integration/booktics-outlook-calendar-addon',
766 'settings_link' => '',
767 'doc_link' => 'https://docs.arraytics.com/docs/booktics/integration/how-to-integrate-outlook-calendar-with-booktics/',
768 ),
769 'paypal' => array(
770 'name' => 'paypal',
771 'slug' => 'paypal',
772 'type' => 'module',
773 'upgrade' => true,
774 'upgrade_link' => 'https://themewinter.com/purchase-history',
775 'status' => 'off',
776 'is_pro' => true,
777 'title' => __( 'PayPal', 'booktics' ),
778 'description' => __( 'It allows you to use PayPal.', 'booktics' ),
779 'icon' => \Booktics\Extensions\Extension_Icon::get( 'booktics-paypal-addon' ),
780 'notice' => '',
781 'demo_link' => 'https://support.themewinter.com/docs/plugins/plugin-docs/integration/booktics-paypal-addon',
782 'settings_link' => '',
783 'doc_link' => 'https://docs.arraytics.com/docs/booktics/payment-type/how-to-configure-the-paypal-in-booktics-pro/',
784 ),
785 );
786
787 return $modules_list;
788 }
789 }
790 }
791
792 if ( ! function_exists( 'booktics_get_multisite_table_name' ) ) {
793 /**
794 * Get multisite-safe table name
795 *
796 * @param string $table_name
797 *
798 * @return string
799 */
800 function booktics_get_table_name( $table_name ) {
801 global $wpdb;
802 $prefix = $wpdb->prefix;
803 if ( strpos( $table_name, $prefix ) === 0 ) {
804 return $table_name;
805 }
806 return $wpdb->prefix . $table_name;
807 }
808 }
809
810 if ( ! function_exists( 'booktics_handle_conflicting_plugins' ) ) {
811 /**
812 * Deactivate conflicting plugins if active and check if they exist.
813 */
814 function booktics_handle_conflicting_plugins() {
815 // Avoid during AJAX or REST requests
816 if ( wp_doing_ajax() || defined( 'REST_REQUEST' ) ) {
817 return;
818 }
819
820 // Only proceed if user is logged in and can manage plugins
821 if ( ! is_user_logged_in() || ! current_user_can( 'activate_plugins' ) ) {
822 return;
823 }
824
825 // Prevent running repeatedly on every page load using a transient
826 if ( get_transient( 'booktics_conflict_check_done' ) ) {
827 return;
828 }
829
830 $conflicting_plugins = array(
831 'booktics-google-calendar-addon/booktics-google-calendar.php',
832 'booktics-outlook-calendar-addon/booktics-outlook-calendar-addon.php',
833 'booktics-paypal-addon/booktics-paypal-addon.php',
834 'booktics-stripe-addon/booktics_stripe.php',
835 'booktics-fluentcrm-addon/booktics-fluentcrm-addon.php',
836 );
837
838 $active_plugins = get_option( 'active_plugins', array() );
839 $deactivated_list = array();
840
841 foreach ( $conflicting_plugins as $plugin_file ) {
842 $plugin_path = WP_PLUGIN_DIR . '/' . $plugin_file;
843
844 if ( file_exists( $plugin_path ) ) {
845 if ( in_array( $plugin_file, $active_plugins, true ) ) {
846 deactivate_plugins( $plugin_file );
847 $plugin_data = get_plugin_data( $plugin_path );
848 $deactivated_list[] = $plugin_data['Name'];
849 }
850 }
851 }
852
853 if ( ! empty( $deactivated_list ) ) {
854 // Store data in transient for 1 minute to show notice once
855 set_transient( 'booktics_deactivated_plugins_notice', $deactivated_list, MINUTE_IN_SECONDS );
856 }
857
858 // Set the conflict check as done (even if nothing was deactivated)
859 set_transient( 'booktics_conflict_check_done', true, MINUTE_IN_SECONDS );
860 }
861 }
862
863 if ( ! function_exists( 'booktics_show_conflict_deactivation_notice' ) ) {
864 /**
865 * Show a one-time admin notice for deactivated conflicting plugins.
866 */
867 function booktics_show_conflict_deactivation_notice() {
868 if ( ! current_user_can( 'activate_plugins' ) ) {
869 return;
870 }
871
872 $deactivated_list = get_transient( 'booktics_deactivated_plugins_notice' );
873
874 if ( ! empty( $deactivated_list ) ) {
875 delete_transient( 'booktics_deactivated_plugins_notice' );
876
877 $plugin_names = implode( ', ', array_map( 'esc_html', $deactivated_list ) );
878 ?>
879 <div class="notice notice-warning is-dismissible">
880 <p>
881 <?php echo esc_html__( 'No need for addons! The following addon plugins have been added as modules in Booktics:', 'booktics' ); ?>
882 <strong><?php echo esc_html( $plugin_names ); ?></strong>
883 <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' ); ?>
884 </p>
885 </div>
886 <?php
887 }
888 }
889 }
890
891 if ( ! function_exists( 'booktics_time_to_gmt' ) ) {
892 /**
893 * Convert time from site timezone to GMT/UTC
894 *
895 * @param string $time Time string (e.g., "9:00 AM", "09:00:00")
896 * @param string|int $context_date Date context (Y-m-d or 0 for regular schedules)
897 * @return string Time in H:i:s format (GMT/UTC)
898 * @since 1.0.15
899 */
900 function booktics_time_to_gmt( string $time, $context_date = 0 ): string {
901 // Skip '0' placeholder for holidays
902 if ( $time === '0' || $time === 0 ) {
903 return '0';
904 }
905
906 // For regular schedules (context_date = 0), use current date as context
907 // For custom schedules, use the actual date for DST-aware conversion
908 $date = ( $context_date === 0 || $context_date === '0' || empty( $context_date ) )
909 ? gmdate( 'Y-m-d' )
910 : $context_date;
911
912 $timestamp = strtotime( $time );
913 if ( $timestamp === false ) {
914 return $time;
915 }
916 $formatted_time = gmdate( 'H:i:s', $timestamp );
917 $datetime_string = $date . ' ' . $formatted_time;
918
919 $gmt_datetime = get_gmt_from_date( $datetime_string );
920
921 // Extract and return only the time part
922 return gmdate( 'H:i:s', strtotime( $gmt_datetime ) );
923 }
924 }
925
926 if ( ! function_exists( 'booktics_is_admin_or_team_member' ) ) {
927 /**
928 * Check if the current user is an admin or a Booktics team member.
929 *
930 * @return bool
931 * @since 1.0.16
932 */
933 function booktics_is_admin_or_team_member() {
934 if ( current_user_can( 'manage_options' ) ) {
935 return true;
936 }
937 if ( is_user_logged_in() ) {
938 $roles = (array) wp_get_current_user()->roles;
939 if ( in_array( 'booktics_team_member', $roles, true ) ) {
940 return true;
941 }
942 }
943 return false;
944 }
945 }
946
947 if ( ! function_exists( 'booktics_is_team_member' ) ) {
948 function booktics_is_team_member() {
949 if ( is_user_logged_in() ) {
950 $roles = (array) wp_get_current_user()->roles;
951 if ( in_array( 'booktics_team_member', $roles, true ) ) {
952 return true;
953 }
954 }
955 return false;
956 }
957 }
958
959 if ( ! function_exists( 'booktics_time_from_gmt' ) ) {
960 /**
961 * Convert time from GMT/UTC to site timezone
962 *
963 * @param string $time Time string in UTC (e.g., "14:00:00")
964 * @param string|int $context_date Date context (Y-m-d or 0 for regular schedules)
965 * @return string Time in H:i:s format (site timezone)
966 * @since 1.0.15
967 */
968 function booktics_time_from_gmt( string $time, $context_date = 0 ): string {
969 // Skip '0' placeholder for holidays
970 if ( $time === '0' || $time === 0 ) {
971 return '0';
972 }
973
974 $date = ( $context_date === 0 || $context_date === '0' || empty( $context_date ) )
975 ? gmdate( 'Y-m-d' )
976 : $context_date;
977
978 $timestamp = strtotime( $time );
979 if ( $timestamp === false ) {
980 return $time;
981 }
982 $formatted_time = gmdate( 'H:i:s', $timestamp );
983 $gmt_datetime_string = $date . ' ' . $formatted_time;
984
985 $local_datetime = get_date_from_gmt( $gmt_datetime_string );
986
987 return gmdate( 'H:i:s', strtotime( $local_datetime ) );
988 }
989 }
990
991 if ( ! function_exists( 'booktics_safe_maybe_unserialize' ) ) {
992 /**
993 * Safely unserialize a value without ever instantiating PHP objects.
994 *
995 * @param mixed $value Possibly serialized value.
996 *
997 * @return mixed Unserialized value, or the original value when not serialized.
998 */
999 function booktics_safe_maybe_unserialize( $value ) {
1000 if ( is_string( $value ) && is_serialized( $value ) ) {
1001 // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_unserialize -- allowed_classes => false prevents PHP Object Injection.
1002 return @unserialize( trim( $value ), array( 'allowed_classes' => false ) );
1003 }
1004
1005 return $value;
1006 }
1007 }
1008
1009 if ( ! function_exists( 'booktics_sanitize_id_string' ) ) {
1010 /**
1011 * Sanitize a free-form identifier received from a request.
1012 *
1013 * Returns a plain-text string id (e.g. 'default' or a client-generated id such
1014 * as a uuid/nanoid). A legitimate identifier is never a PHP-serialized string,
1015 * so any value that looks serialized is dropped to '' — this neutralizes PHP
1016 * Object Injection payloads at the storage boundary while leaving every real id
1017 * untouched. Non-scalar input (arrays/objects) is rejected to ''.
1018 *
1019 * @param mixed $value Raw value from the request.
1020 *
1021 * @return string Sanitized identifier, or '' when missing, non-scalar, or serialized.
1022 */
1023 function booktics_sanitize_id_string( $value ) {
1024 if ( ! is_scalar( $value ) ) {
1025 return '';
1026 }
1027
1028 $value = sanitize_text_field( (string) $value );
1029
1030 return is_serialized( $value ) ? '' : $value;
1031 }
1032 }
1033
1034 if ( ! function_exists( 'booktics_aisentic_identity' ) ) {
1035 /**
1036 * Build the site identity offered to Aisentic when the user opts in.
1037 *
1038 * The consent UI and the send path both read this helper, so the email shown
1039 * on the consent checkbox is always the email that actually leaves the site.
1040 *
1041 * @return array{name: string, email: string, site_url: string}
1042 */
1043 function booktics_aisentic_identity(): array {
1044 $name = booktics_get_option( 'business_name', get_bloginfo( 'name' ) );
1045 $email = booktics_get_option( 'business_email', get_option( 'admin_email' ) );
1046
1047 return array(
1048 'name' => sanitize_text_field( (string) $name ),
1049 'email' => sanitize_email( (string) $email ),
1050 'site_url' => esc_url_raw( site_url() ),
1051 );
1052 }
1053 }
1054
1055 if ( ! function_exists( 'booktics_aisentic_is_registered' ) ) {
1056 /**
1057 * Whether Aisentic already holds an API key.
1058 *
1059 * Reading Aisentic's own storage instead of our consent option means anyone
1060 * who connected from Aisentic's screens is never nagged by our banner.
1061 *
1062 * @return bool True when an Aisentic API key is present.
1063 */
1064 function booktics_aisentic_is_registered(): bool {
1065 if ( function_exists( 'aisentic_get_option' ) ) {
1066 return ! empty( aisentic_get_option( 'llmProviders.aisentic.apiKey', '' ) );
1067 }
1068
1069 $settings = get_option( 'aisentic_settings', array() );
1070
1071 return ! empty( $settings['llmProviders']['aisentic']['apiKey'] );
1072 }
1073 }
1074