| @@ -174,15 +174,14 @@ | ||
| 174 | 174 | /** |
| 175 | 175 | * Define constant if not already set. |
| 176 | 176 | * |
| 177 | 177 | * @since 1.0.0 |
| 178 | - * @param string $timetics_constant_name Constant name. | |
| 178 | + * @param string $name Constant name. | |
| 179 | 179 | * @param string|bool $value Constant value. |
| 180 | 180 | */ |
| 181 | - private function define( $timetics_constant_name, $value ) { | |
| 182 | - if ( ! defined( $timetics_constant_name ) ) { | |
| 183 | - // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.VariableConstantNameFound -- Constant name is dynamic but prefixed by caller | |
| 184 | - define( $timetics_constant_name, $value ); | |
| 181 | + private function define( $name, $value ) { | |
| 182 | + if ( ! defined( $name ) ) { | |
| 183 | + define( $name, $value ); | |
| 185 | 184 | } |
| 186 | 185 | } |
| 187 | 186 | |
| 188 | 187 | /** |
| @@ -228,15 +227,13 @@ | ||
| 228 | 227 | |
| 229 | 228 | // Register scripts and styles first |
| 230 | 229 | if ( $this->is_request( 'admin' ) ) { |
| 231 | 230 | add_action( 'admin_enqueue_scripts', [ $this, 'admin_scripts' ] ); |
| 232 | - add_action( 'admin_enqueue_scripts', [ $this, 'preload_js_translations' ], 5 ); | |
| 233 | 231 | add_action( 'admin_footer', [ $this, 'admin_helpscout_beacon' ] ); |
| 234 | 232 | } |
| 235 | 233 | |
| 236 | 234 | if ( $this->is_request( 'frontend' ) ) { |
| 237 | 235 | add_action( 'wp_enqueue_scripts', array( $this, 'frontend_scripts' ) ); |
| 238 | - add_action( 'wp_enqueue_scripts', [ $this, 'preload_js_translations' ], 5 ); | |
| 239 | 236 | } |
| 240 | 237 | |
| 241 | 238 | // Register admin menu |
| 242 | 239 | Core\Admin\Menu::instance()->init(); |
| @@ -263,109 +260,8 @@ | ||
| 263 | 260 | return "https://arraytics.com/timetics/"; |
| 264 | 261 | } |
| 265 | 262 | |
| 266 | 263 | /** |
| 267 | - * Preload PHP-loaded translations into wp.i18n before any bundle runs. | |
| 268 | - * | |
| 269 | - * Why: webpack bundles collapse many source files into one. wp_set_script_translations() | |
| 270 | - * looks up a JSON file hashed from the registered bundle URL, but Loco and wp i18n make-json | |
| 271 | - * shard JSON by source-file path. The hashes never match, so per-script JSON loading silently | |
| 272 | - * fails. Reading the textdomain that load_text_domain() already populated and injecting it via | |
| 273 | - * setLocaleData() bypasses the hash mismatch entirely — every __() call across every bundle | |
| 274 | - * resolves from in-memory data set before any React component renders. | |
| 275 | - */ | |
| 276 | - public function preload_js_translations() { | |
| 277 | - $locale = determine_locale(); | |
| 278 | - $plural_forms = 'nplurals=2; plural=(n != 1);'; | |
| 279 | - | |
| 280 | - // Read .mo first — canonical, always-fresh source that Loco rewrites on every save. | |
| 281 | - // .l10n.php cache often lags behind .mo because Loco does not regenerate it. | |
| 282 | - $messages = $this->extract_mo_messages( $locale, $plural_forms ); | |
| 283 | - | |
| 284 | - // Merge .l10n.php on top (covers WP 6.5+ system-translated plugins where only .l10n.php exists). | |
| 285 | - foreach ( [ | |
| 286 | - WP_LANG_DIR . '/loco/plugins/timetics-' . $locale . '.l10n.php', | |
| 287 | - TIMETICS_PLUGIN_DIR . 'languages/timetics-' . $locale . '.l10n.php', | |
| 288 | - WP_LANG_DIR . '/plugins/timetics-' . $locale . '.l10n.php', | |
| 289 | - ] as $file ) { | |
| 290 | - if ( ! is_readable( $file ) ) { | |
| 291 | - continue; | |
| 292 | - } | |
| 293 | - $data = include $file; | |
| 294 | - if ( ! is_array( $data ) || empty( $data['messages'] ) ) { | |
| 295 | - continue; | |
| 296 | - } | |
| 297 | - $messages = array_merge( $messages, $data['messages'] ); | |
| 298 | - if ( ! empty( $data['plural-forms'] ) ) { | |
| 299 | - $plural_forms = $data['plural-forms']; | |
| 300 | - } | |
| 301 | - } | |
| 302 | - | |
| 303 | - if ( empty( $messages ) ) { | |
| 304 | - return; | |
| 305 | - } | |
| 306 | - | |
| 307 | - $locale_data = [ | |
| 308 | - '' => [ | |
| 309 | - 'domain' => 'timetics', | |
| 310 | - 'lang' => $locale, | |
| 311 | - 'plural-forms' => $plural_forms, | |
| 312 | - ], | |
| 313 | - ]; | |
| 314 | - | |
| 315 | - foreach ( $messages as $msgid => $msgstr ) { | |
| 316 | - if ( is_string( $msgid ) && $msgid !== '' && is_string( $msgstr ) && $msgstr !== '' ) { | |
| 317 | - $locale_data[ $msgid ] = [ $msgstr ]; | |
| 318 | - } | |
| 319 | - } | |
| 320 | - | |
| 321 | - wp_add_inline_script( | |
| 322 | - 'wp-i18n', | |
| 323 | - sprintf( | |
| 324 | - 'wp.i18n.setLocaleData( %s, "timetics" );', | |
| 325 | - wp_json_encode( $locale_data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ) | |
| 326 | - ), | |
| 327 | - 'after' | |
| 328 | - ); | |
| 329 | - } | |
| 330 | - | |
| 331 | - /** | |
| 332 | - * Fallback: parse .mo file to extract all translations. | |
| 333 | - */ | |
| 334 | - private function extract_mo_messages( $locale, &$plural_forms ) { | |
| 335 | - $candidates = [ | |
| 336 | - WP_LANG_DIR . '/loco/plugins/timetics-' . $locale . '.mo', | |
| 337 | - TIMETICS_PLUGIN_DIR . 'languages/timetics-' . $locale . '.mo', | |
| 338 | - WP_LANG_DIR . '/plugins/timetics-' . $locale . '.mo', | |
| 339 | - ]; | |
| 340 | - | |
| 341 | - $messages = []; | |
| 342 | - | |
| 343 | - foreach ( $candidates as $mofile ) { | |
| 344 | - if ( ! is_readable( $mofile ) ) { | |
| 345 | - continue; | |
| 346 | - } | |
| 347 | - if ( ! class_exists( 'MO' ) ) { | |
| 348 | - require_once ABSPATH . WPINC . '/pomo/mo.php'; | |
| 349 | - } | |
| 350 | - $mo = new \MO(); | |
| 351 | - if ( ! $mo->import_from_file( $mofile ) ) { | |
| 352 | - continue; | |
| 353 | - } | |
| 354 | - if ( ! empty( $mo->headers['Plural-Forms'] ) ) { | |
| 355 | - $plural_forms = $mo->headers['Plural-Forms']; | |
| 356 | - } | |
| 357 | - foreach ( $mo->entries as $msgid => $entry ) { | |
| 358 | - if ( ! empty( $entry->translations[0] ) ) { | |
| 359 | - $messages[ $msgid ] = $entry->translations[0]; | |
| 360 | - } | |
| 361 | - } | |
| 362 | - } | |
| 363 | - | |
| 364 | - return $messages; | |
| 365 | - } | |
| 366 | - | |
| 367 | - /** | |
| 368 | 264 | * Register scripts and styles for admin |
| 369 | 265 | * |
| 370 | 266 | * @return void |
| 371 | 267 | */ |
| @@ -405,9 +301,8 @@ | ||
| 405 | 301 | } |
| 406 | 302 | |
| 407 | 303 | wp_enqueue_script( 'timetics-dashboard-scripts', TIMETICS_ASSETS_URL . 'js/dashboard.js', [ 'wp-plugins', 'wp-i18n', 'wp-element', 'wp-dom', 'wp-data', 'wp-color-picker' ], TIMETICS_VERSION, true ); |
| 408 | 304 | |
| 409 | - wp_set_script_translations( 'timetics-dashboard-scripts', 'timetics', TIMETICS_PLUGIN_DIR . 'languages/' ); | |
| 410 | 305 | |
| 411 | 306 | $localize_obj = array( |
| 412 | 307 | 'site_url' => site_url(), |
| 413 | 308 | 'admin_url' => admin_url(), |
| @@ -425,9 +320,8 @@ | ||
| 425 | 320 | 'current_user_id' => get_current_user_id(), |
| 426 | 321 | 'timeticsPro' => class_exists('TimeticsPro') ? true : false, |
| 427 | 322 | 'demo_url' => $this->demo_url(), |
| 428 | 323 | 'current_user' => timetics_get_current_user(), |
| 429 | - 'aisentic' => $this->get_aisentic_state(), | |
| 430 | 324 | ); |
| 431 | 325 | |
| 432 | 326 | $localize_obj = apply_filters( 'timetics_admin_localize_data', $localize_obj ); |
| 433 | 327 | |
| @@ -434,35 +328,8 @@ | ||
| 434 | 328 | wp_localize_script( 'timetics-dashboard-scripts', 'timetics', $localize_obj ); |
| 435 | 329 | } |
| 436 | 330 | |
| 437 | 331 | /** |
| 438 | - * Aisentic connect state for the onboarding consent box and dashboard banner. | |
| 439 | - * | |
| 440 | - * Both surfaces show the email before the user opts in, so the value comes | |
| 441 | - * from the same resolver the connect request uses. | |
| 442 | - * | |
| 443 | - * @return array Aisentic identity plus active/registered flags. | |
| 444 | - */ | |
| 445 | - private function get_aisentic_state() { | |
| 446 | - $identity = timetics_aisentic_identity(); | |
| 447 | - | |
| 448 | - return [ | |
| 449 | - 'name' => $identity['name'], | |
| 450 | - 'email' => $identity['email'], | |
| 451 | - 'active' => class_exists( 'Aisentic\Init' ), | |
| 452 | - 'registered' => timetics_aisentic_is_registered(), | |
| 453 | - 'icon' => \Timetics\Core\Addon\Extension_Icon::get( 'aisentic' ), | |
| 454 | - /* | |
| 455 | - * Aisentic builds before the registration handshake have no | |
| 456 | - * listener for our hook. Connecting would fail silently there, so | |
| 457 | - * the UI hides the offer instead of promising something dead. | |
| 458 | - */ | |
| 459 | - 'supports_registration' => class_exists( 'Aisentic\Api\Services\Registration_Service' ), | |
| 460 | - 'terms_url' => 'https://arraytics.com/terms-of-service/', | |
| 461 | - ]; | |
| 462 | - } | |
| 463 | - | |
| 464 | - /** | |
| 465 | 332 | * Register scripts and styles for frontend |
| 466 | 333 | * |
| 467 | 334 | * @return void |
| 468 | 335 | */ |
| @@ -470,25 +337,8 @@ | ||
| 470 | 337 | |
| 471 | 338 | wp_register_style( 'timetics-vendor', TIMETICS_ASSETS_URL . 'css/vendor.css', [], TIMETICS_VERSION, 'all' ); |
| 472 | 339 | wp_register_style( 'timetics-frontend', TIMETICS_ASSETS_URL . 'css/frontend.css', [], TIMETICS_VERSION, 'all' ); |
| 473 | 340 | |
| 474 | - global $post; | |
| 475 | - if ( $post ) { | |
| 476 | - $is_timetics_cpt = ( 'timetics-appointment' === get_post_type( $post ) ); | |
| 477 | - $timetics_shortcodes = [ 'timetics-booking-form', 'timetics-meeting-list', 'timetics-user-dashboard', 'timetics-category' ]; | |
| 478 | - $has_timetics_shortcode = false; | |
| 479 | - foreach ( $timetics_shortcodes as $shortcode ) { | |
| 480 | - if ( has_shortcode( $post->post_content, $shortcode ) ) { | |
| 481 | - $has_timetics_shortcode = true; | |
| 482 | - break; | |
| 483 | - } | |
| 484 | - } | |
| 485 | - if ( $is_timetics_cpt || $has_timetics_shortcode ) { | |
| 486 | - wp_enqueue_style( 'timetics-vendor' ); | |
| 487 | - wp_enqueue_style( 'timetics-frontend' ); | |
| 488 | - } | |
| 489 | - } | |
| 490 | - | |
| 491 | 341 | wp_register_script( 'timetics-flatpickr-scripts', TIMETICS_ASSETS_URL . 'js/vendors/flatpickr.js', [ 'wp-plugins', 'wp-i18n', 'wp-element', 'wp-dom', 'wp-data' ], TIMETICS_VERSION, true ); |
| 492 | 342 | wp_enqueue_script( 'timetics-packages', TIMETICS_ASSETS_URL . 'js/packages.js', [ 'timetics-flatpickr-scripts' ], TIMETICS_VERSION, true ); |
| 493 | 343 | |
| 494 | 344 | wp_register_script( 'timetics-frontend-scripts', TIMETICS_ASSETS_URL . 'js/frontend.js', [ 'jquery', 'wp-plugins', 'wp-i18n', 'wp-element', 'wp-dom', 'wp-data' ], TIMETICS_VERSION, true ); |
| @@ -493,10 +343,10 @@ | ||
| 493 | 343 | |
| 494 | 344 | wp_register_script( 'timetics-frontend-scripts', TIMETICS_ASSETS_URL . 'js/frontend.js', [ 'jquery', 'wp-plugins', 'wp-i18n', 'wp-element', 'wp-dom', 'wp-data' ], TIMETICS_VERSION, true ); |
| 495 | 345 | |
| 496 | 346 | $calendar_locale = timetics_get_option('calendar_locale'); |
| 497 | - if (!empty($calendar_locale) && file_exists( TIMETICS_PLUGIN_DIR . "assets/js/local/{$calendar_locale}.js" ) ) { | |
| 498 | - wp_register_script( 'calendar-locale', TIMETICS_ASSETS_URL . "js/local/{$calendar_locale}.js", [ 'jquery', 'wp-plugins', 'wp-i18n', 'wp-element', 'wp-dom', 'wp-data' ], TIMETICS_VERSION, true ); | |
| 347 | + if (!empty($calendar_locale)) { | |
| 348 | + wp_register_script( 'calendar-locale', "https://npmcdn.com/flatpickr/dist/l10n/{$calendar_locale}.js", [ 'jquery', 'wp-plugins', 'wp-i18n', 'wp-element', 'wp-dom', 'wp-data' ], TIMETICS_VERSION, true ); | |
| 499 | 349 | } |
| 500 | 350 | |
| 501 | 351 | // load text domain |
| 502 | 352 | wp_set_script_translations( 'timetics-frontend-scripts', 'timetics', TIMETICS_PLUGIN_DIR . 'languages/' ); |
| @@ -570,10 +420,9 @@ | ||
| 570 | 420 | * |
| 571 | 421 | * @return void |
| 572 | 422 | */ |
| 573 | 423 | public function handle_buy_pro_module() { |
| 574 | - // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only page parameter check, no form processing | |
| 575 | - $page = isset( $_GET['page'] ) ? sanitize_text_field( wp_unslash( $_GET['page'] ) ) : ''; | |
| 424 | + $page = isset( $_GET['page'] ) ? sanitize_text_field( $_GET['page'] ) : ''; | |
| 576 | 425 | |
| 577 | 426 | |
| 578 | 427 | if ( 'timetics' !== $page ) { |
| 579 | 428 | return; |
| @@ -595,9 +444,9 @@ | ||
| 595 | 444 | |
| 596 | 445 | \Wpmet\Libs\Banner::instance('timetics') |
| 597 | 446 | ->is_test(true) |
| 598 | 447 | ->set_filter(ltrim($filter_string, ',')) |
| 599 | - ->set_api_url('https://banner.arraytics.com/public/jhanda') | |
| 448 | + ->set_api_url('https://banner.themefunction.com/public/jhanda') | |
| 600 | 449 | ->set_plugin_screens('timetics') |
| 601 | 450 | ->set_plugin_screens('toplevel_page_timetics') |
| 602 | 451 | ->call(); |
| 603 | 452 | // show get-help and upgrade-to-premium menu. |
| @@ -635,9 +484,9 @@ | ||
| 635 | 484 | color: "#0060e5", |
| 636 | 485 | }); |
| 637 | 486 | window.Beacon('init', '4be24aa2-ee50-483c-a90c-db4216664d65'); |
| 638 | 487 | window.Beacon('on', 'ready', function(){ |
| 639 | - window.Beacon('close'); | |
| 488 | + window.Beacon('show'); | |
| 640 | 489 | }); |
| 641 | 490 | |
| 642 | 491 | </script> |
| 643 | 492 | <?php |