PluginProbe
Timetics – Appointment Booking Calendar & Scheduling / 1.0.49
Timetics – Appointment Booking Calendar & Scheduling v1.0.49
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 -160 trunk1.0.49 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,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