PluginProbe
Timetics – Appointment Booking Calendar & Scheduling / 1.0.38
Timetics – Appointment Booking Calendar & Scheduling v1.0.38
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 1.0.22 1.0.23 1.0.24 All 62 releases
← All changes | bootstrap.php +9 -201 trunk1.0.38 View file →
@@ -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,12 @@
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 - add_action( 'admin_footer', [ $this, 'admin_helpscout_beacon' ] );
234 231 }
235 232
236 233 if ( $this->is_request( 'frontend' ) ) {
237 234 add_action( 'wp_enqueue_scripts', array( $this, 'frontend_scripts' ) );
238 - add_action( 'wp_enqueue_scripts', [ $this, 'preload_js_translations' ], 5 );
239 235 }
240 236
241 237 // Register admin menu
242 238 Core\Admin\Menu::instance()->init();
@@ -263,109 +259,8 @@
263 259 return "https://arraytics.com/timetics/";
264 260 }
265 261
266 262 /**
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 263 * Register scripts and styles for admin
369 264 *
370 265 * @return void
371 266 */
@@ -378,10 +273,9 @@
378 273 wp_register_style( 'timetics-feedback-modal', TIMETICS_ASSETS_URL . 'css/feedback-modal.css', [], TIMETICS_VERSION, 'all' );
379 274 wp_enqueue_style( 'timetics-feedback-modal', TIMETICS_ASSETS_URL . 'css/feedback-modal.css', [], TIMETICS_VERSION, 'all' );
380 275
381 276 $feedback_obj = array(
382 - 'site_url' => site_url(),
383 - 'admin_email' => get_option( 'admin_email' )
277 + 'site_url' => site_url()
384 278 );
385 279
386 280 wp_localize_script( 'timetics-feedback-modal', 'timetics_feedback', $feedback_obj );
387 281
@@ -405,9 +299,8 @@
405 299 }
406 300
407 301 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 302
409 - wp_set_script_translations( 'timetics-dashboard-scripts', 'timetics', TIMETICS_PLUGIN_DIR . 'languages/' );
410 303
411 304 $localize_obj = array(
412 305 'site_url' => site_url(),
413 306 'admin_url' => admin_url(),
@@ -425,9 +318,8 @@
425 318 'current_user_id' => get_current_user_id(),
426 319 'timeticsPro' => class_exists('TimeticsPro') ? true : false,
427 320 'demo_url' => $this->demo_url(),
428 321 'current_user' => timetics_get_current_user(),
429 - 'aisentic' => $this->get_aisentic_state(),
430 322 );
431 323
432 324 $localize_obj = apply_filters( 'timetics_admin_localize_data', $localize_obj );
433 325
@@ -434,35 +326,8 @@
434 326 wp_localize_script( 'timetics-dashboard-scripts', 'timetics', $localize_obj );
435 327 }
436 328
437 329 /**
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 330 * Register scripts and styles for frontend
466 331 *
467 332 * @return void
468 333 */
@@ -470,25 +335,8 @@
470 335
471 336 wp_register_style( 'timetics-vendor', TIMETICS_ASSETS_URL . 'css/vendor.css', [], TIMETICS_VERSION, 'all' );
472 337 wp_register_style( 'timetics-frontend', TIMETICS_ASSETS_URL . 'css/frontend.css', [], TIMETICS_VERSION, 'all' );
473 338
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 339 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 340 wp_enqueue_script( 'timetics-packages', TIMETICS_ASSETS_URL . 'js/packages.js', [ 'timetics-flatpickr-scripts' ], TIMETICS_VERSION, true );
493 341
494 342 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 +341,10 @@
493 341
494 342 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 343
496 344 $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 );
345 + if (!empty($calendar_locale)) {
346 + 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 347 }
500 348
501 349 // load text domain
502 350 wp_set_script_translations( 'timetics-frontend-scripts', 'timetics', TIMETICS_PLUGIN_DIR . 'languages/' );
@@ -570,10 +418,9 @@
570 418 *
571 419 * @return void
572 420 */
573 421 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'] ) ) : '';
422 + $page = isset( $_GET['page'] ) ? sanitize_text_field( $_GET['page'] ) : '';
576 423
577 424
578 425 if ( 'timetics' !== $page ) {
579 426 return;
@@ -595,53 +442,14 @@
595 442
596 443 \Wpmet\Libs\Banner::instance('timetics')
597 444 ->is_test(true)
598 445 ->set_filter(ltrim($filter_string, ','))
599 - ->set_api_url('https://banner.arraytics.com/public/jhanda')
446 + ->set_api_url('https://product.themewinter.com/auth/public/jhanda')
600 447 ->set_plugin_screens('timetics')
601 448 ->set_plugin_screens('toplevel_page_timetics')
602 449 ->call();
603 450 // show get-help and upgrade-to-premium menu.
604 451 // $this->handle_get_help_and_upgrade_menu();
605 - }
606 -
607 - /**
608 - * Add HelpScout script to admin footer
609 - *
610 - * @return void
611 - */
612 - public function admin_helpscout_beacon() {
613 - $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
614 - $is_timetics_screen = false;
615 -
616 - if ( $screen ) {
617 - $screen_id = isset( $screen->id ) ? $screen->id : '';
618 -
619 - // Check: only load on Timetics top-level admin page
620 - if ( 'toplevel_page_timetics' === $screen_id ) {
621 - $is_timetics_screen = true;
622 - }
623 - }
624 -
625 - // Allow overriding detection via filter.
626 - $is_timetics_screen = apply_filters( 'timetics_is_admin_screen', $is_timetics_screen, $screen );
627 -
628 - if ( ! $is_timetics_screen ) {
629 - return;
630 - }
631 - ?>
632 - <script type="text/javascript">!function(e,t,n){function a(){var e=t.getElementsByTagName("script")[0],n=t.createElement("script");n.type="text/javascript",n.async=!0,n.src="https://beacon-v2.helpscout.net",e.parentNode.insertBefore(n,e)}if(e.Beacon=n=function(t,n,a){e.Beacon.readyQueue.push({method:t,options:n,data:a})},n.readyQueue=[],"complete"===t.readyState)a();else if(e.attachEvent)e.attachEvent("onload",a);else e.addEventListener("load",a,!1)}(window,document,window.Beacon||function(){});</script>
633 - <script type="text/javascript">
634 - window.Beacon('config', {
635 - color: "#0060e5",
636 - });
637 - window.Beacon('init', '4be24aa2-ee50-483c-a90c-db4216664d65');
638 - window.Beacon('on', 'ready', function(){
639 - window.Beacon('close');
640 - });
641 -
642 - </script>
643 - <?php
644 452 }
645 453
646 454 }
647 455