PluginProbe
Timetics – Appointment Booking Calendar & Scheduling / 1.0.64
Timetics – Appointment Booking Calendar & Scheduling v1.0.64
1.0.64 1.0.62 1.0.63 1.0.61 1.0.60 1.0.59 1.0.58 1.0.57 1.0.56 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.19 1.0.2 1.0.20 1.0.21 All 65 releases
← All changes | utils/global-helper.php +146 -4 1.0.61 → 1.0.64 View file →
@@ -345,8 +345,70 @@
345 345 return $total;
346 346 }
347 347 }
348 348
349 +if ( ! function_exists( 'timetics_can_view_all_data' ) ) {
350 + /**
351 + * Check the current user is allowed to see site wide data
352 + *
353 + * Staff hold `manage_timetics`, so that capability cannot be used to tell an
354 + * administrator apart from a team member.
355 + *
356 + * @return bool
357 + */
358 + function timetics_can_view_all_data() {
359 + return current_user_can( 'manage_options' );
360 + }
361 +}
362 +
363 +if ( ! function_exists( 'timetics_get_visible_booking_ids' ) ) {
364 + /**
365 + * Get booking ids a user is allowed to see
366 + *
367 + * @param integer $user_id
368 + *
369 + * @return array Never empty, so it is always safe to pass to `post__in`.
370 + */
371 + function timetics_get_visible_booking_ids( $user_id = 0 ) {
372 + if ( ! $user_id ) {
373 + $user_id = get_current_user_id();
374 + }
375 +
376 + $booking_ids = Booking::get_visible_ids_for_user( $user_id );
377 +
378 + // WP_Query ignores an empty post__in and returns everything, so fall back to a
379 + // non-existent id to mean "nothing visible".
380 + return ! empty( $booking_ids ) ? $booking_ids : [0];
381 + }
382 +}
383 +
384 +if ( ! function_exists( 'timetics_get_visible_customer_ids' ) ) {
385 + /**
386 + * Get customer user ids a user is allowed to see, derived from their visible bookings
387 + *
388 + * @param integer $user_id
389 + *
390 + * @return array Never empty, so it is always safe to pass to `include`.
391 + */
392 + function timetics_get_visible_customer_ids( $user_id = 0 ) {
393 + $booking_ids = timetics_get_visible_booking_ids( $user_id );
394 + $customer_ids = [];
395 +
396 + foreach ( $booking_ids as $booking_id ) {
397 + $customer_id = (int) get_post_meta( $booking_id, '_tt_booking_customer', true );
398 +
399 + if ( $customer_id ) {
400 + $customer_ids[] = $customer_id;
401 + }
402 + }
403 +
404 + $customer_ids = array_values( array_unique( $customer_ids ) );
405 +
406 + // WP_User_Query ignores an empty include and returns everything.
407 + return ! empty( $customer_ids ) ? $customer_ids : [0];
408 + }
409 +}
410 +
349 411 if ( ! function_exists( 'timetics_get_staff_integrations' ) ) {
350 412 /**
351 413 * Get all staff integrations
352 414 *
@@ -449,8 +511,9 @@
449 511 'default_booking_status' => 'pending',
450 512 'currency' => 'USD',
451 513 'primary_color' => '#3161F1',
452 514 'secondary_color' => '#6188ff',
515 + 'unpaid_booking_expiry_minutes' => 5,
453 516 ];
454 517
455 518 $settings = apply_filters( 'timetics_default_settings', $settings );
456 519
@@ -552,8 +615,14 @@
552 615 * @return void
553 616 */
554 617 function timetics_register_cron() {
555 618 wp_schedule_event( time(), 'daily', 'timetics_booking_clear_schedule' );
619 +
620 + // Guarded: Hooks::maybe_schedule_cleanup_cron() also calls this on
621 + // every 'init', so without the guard it'd stack duplicate events.
622 + if ( ! wp_next_scheduled( 'timetics_cleanup_unpaid_bookings' ) ) {
623 + wp_schedule_event( time(), 'timetics_five_minutes', 'timetics_cleanup_unpaid_bookings' );
624 + }
556 625 }
557 626 }
558 627
559 628 if ( ! function_exists( 'timetics_is_woocommerce_active' ) ) {
@@ -876,15 +945,14 @@
876 945 'upgrade_link' => '',
877 946 'status' => 'on',
878 947 'is_pro' => false,
879 948 'title' => __( 'Aisentic', 'timetics' ),
880 - 'description' => __( 'AI assistant for WordPress — automate content, replies and on-site tasks without leaving your dashboard.', 'timetics' ),
949 + 'description' => __( 'Your AI assistant for Timetics. Manage bookings, analyze schedules and automate routine tasks using plain English — get work done in seconds instead of hours.', 'timetics' ),
881 950 'icon' => \Timetics\Core\Addon\Extension_Icon::get( 'aisentic' ),
882 951 'notice' => '',
883 952 'demo_link' => '',
884 953 'settings_link' => '',
885 954 'doc_link' => 'https://support.themewinter.com/docs/plugins/docs/aisentic/',
886 - 'download_url' => 'https://github.com/themewinter/aisentic-public/releases/download/v1.0.0/aisentic-1.0.0.zip',
887 955 ],
888 956 'optiontics' => [
889 957 'name' => 'optiontics',
890 958 'slug' => 'optiontics',
@@ -893,9 +961,9 @@
893 961 'upgrade_link' => '',
894 962 'status' => 'on',
895 963 'is_pro' => false,
896 964 'title' => __( 'Optiontics', 'timetics' ),
897 - 'description' => __( 'Product add-ons / extras for food items — let customers pick toppings, sizes and other paid options along with the products.', 'timetics' ),
965 + 'description' => __( 'Offer paid add-ons and extra options with every booking — services, extras, packages or custom upgrades that customers can pick during checkout.', 'timetics' ),
898 966 'icon' => \Timetics\Core\Addon\Extension_Icon::get( 'optiontics' ),
899 967 'notice' => '',
900 968 'demo_link' => '',
901 969 'settings_link' => '',
@@ -943,8 +1011,33 @@
943 1011 return $enabled;
944 1012 }
945 1013 }
946 1014
1015 +if ( ! function_exists( 'timetics_wp_timezone_string' ) ) {
1016 + /**
1017 + * wp_timezone_string() polyfill — core's version needs WP 5.3, plugin supports 5.2.
1018 + *
1019 + * @return string
1020 + */
1021 + function timetics_wp_timezone_string() {
1022 + if ( function_exists( 'wp_timezone_string' ) ) {
1023 + return wp_timezone_string();
1024 + }
1025 +
1026 + $timezone_string = get_option( 'timezone_string' );
1027 +
1028 + if ( $timezone_string ) {
1029 + return $timezone_string;
1030 + }
1031 +
1032 + $offset = (float) get_option( 'gmt_offset' );
1033 + $hours = (int) $offset;
1034 + $minutes = abs( ( $offset - $hours ) * 60 );
1035 +
1036 + return sprintf( '%s%02d:%02d', ( $offset < 0 ) ? '-' : '+', abs( $hours ), $minutes );
1037 + }
1038 +}
1039 +
947 1040 if ( ! function_exists( 'timetics_reminder_fallback_timezone' ) ) {
948 1041 /**
949 1042 * Timezone to interpret a booking's stored wall clock in when the booking
950 1043 * itself carries no usable timezone.
@@ -958,9 +1051,9 @@
958 1051 *
959 1052 * @return string A timezone identifier or ±hh:mm offset accepted by DateTimeZone.
960 1053 */
961 1054 function timetics_reminder_fallback_timezone() {
962 - $timezone = wp_timezone_string();
1055 + $timezone = timetics_wp_timezone_string();
963 1056
964 1057 return $timezone ? $timezone : 'UTC';
965 1058 }
966 1059 }
@@ -989,6 +1082,55 @@
989 1082 'day' => date_i18n( 'l', $timestamp ),
990 1083 'time' => date_i18n( $wp_time_format, $timestamp ),
991 1084 'date' => date_i18n( $wp_date_format, $timestamp ),
992 1085 ];
1086 + }
1087 +}
1088 +
1089 +if ( ! function_exists( 'timetics_aisentic_identity' ) ) {
1090 + /**
1091 + * Resolve the identity Timetics hands to Aisentic when the user connects.
1092 + *
1093 + * The consent UI shows these exact values before anything leaves the site,
1094 + * so the connect request must read them from here too. Showing one email
1095 + * and sending another would break the consent.
1096 + *
1097 + * The account belongs to the person who opts in, so it is the logged-in
1098 + * user's name and email. The Ask AI setup dialog lets them edit the email,
1099 + * which arrives here as $email. The site admin email is the last fallback.
1100 + *
1101 + * @param string $email Optional email the user typed in the consent UI.
1102 + * @return array{name:string,email:string,site_url:string}
1103 + */
1104 + function timetics_aisentic_identity( $email = '' ) {
1105 + $user = wp_get_current_user();
1106 + $name = sanitize_text_field( (string) $user->display_name );
1107 + $email = sanitize_email( (string) ( $email ?: $user->user_email ?: get_option( 'admin_email', '' ) ) );
1108 +
1109 + return [
1110 + 'name' => $name,
1111 + 'email' => $email,
1112 + 'site_url' => site_url(),
1113 + ];
1114 + }
1115 +}
1116 +
1117 +if ( ! function_exists( 'timetics_aisentic_is_registered' ) ) {
1118 + /**
1119 + * Whether Aisentic already holds a provider api key.
1120 + *
1121 + * Reads Aisentic's own settings, so a site the user registered from
1122 + * Aisentic's own screens counts as connected as well. Without that the
1123 + * dashboard banner would keep nagging people who are already set up.
1124 + *
1125 + * @return bool
1126 + */
1127 + function timetics_aisentic_is_registered() {
1128 + if ( function_exists( 'aisentic_get_option' ) ) {
1129 + return ! empty( aisentic_get_option( 'llmProviders.aisentic.apiKey', '' ) );
1130 + }
1131 +
1132 + $settings = get_option( 'aisentic_settings', [] );
1133 +
1134 + return ! empty( $settings['llmProviders']['aisentic']['apiKey'] );
993 1135 }
994 1136 }