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 +7 -171 1.0.591.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(),
@@ -442,25 +335,8 @@
442 335
443 336 wp_register_style( 'timetics-vendor', TIMETICS_ASSETS_URL . 'css/vendor.css', [], TIMETICS_VERSION, 'all' );
444 337 wp_register_style( 'timetics-frontend', TIMETICS_ASSETS_URL . 'css/frontend.css', [], TIMETICS_VERSION, 'all' );
445 338
446 - global $post;
447 - if ( $post ) {
448 - $is_timetics_cpt = ( 'timetics-appointment' === get_post_type( $post ) );
449 - $timetics_shortcodes = [ 'timetics-booking-form', 'timetics-meeting-list', 'timetics-user-dashboard', 'timetics-category' ];
450 - $has_timetics_shortcode = false;
451 - foreach ( $timetics_shortcodes as $shortcode ) {
452 - if ( has_shortcode( $post->post_content, $shortcode ) ) {
453 - $has_timetics_shortcode = true;
454 - break;
455 - }
456 - }
457 - if ( $is_timetics_cpt || $has_timetics_shortcode ) {
458 - wp_enqueue_style( 'timetics-vendor' );
459 - wp_enqueue_style( 'timetics-frontend' );
460 - }
461 - }
462 -
463 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 );
464 340 wp_enqueue_script( 'timetics-packages', TIMETICS_ASSETS_URL . 'js/packages.js', [ 'timetics-flatpickr-scripts' ], TIMETICS_VERSION, true );
465 341
466 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 );
@@ -542,10 +418,9 @@
542 418 *
543 419 * @return void
544 420 */
545 421 public function handle_buy_pro_module() {
546 - // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only page parameter check, no form processing
547 - $page = isset( $_GET['page'] ) ? sanitize_text_field( wp_unslash( $_GET['page'] ) ) : '';
422 + $page = isset( $_GET['page'] ) ? sanitize_text_field( $_GET['page'] ) : '';
548 423
549 424
550 425 if ( 'timetics' !== $page ) {
551 426 return;
@@ -567,53 +442,14 @@
567 442
568 443 \Wpmet\Libs\Banner::instance('timetics')
569 444 ->is_test(true)
570 445 ->set_filter(ltrim($filter_string, ','))
571 - ->set_api_url('https://banner.themefunction.com/public/jhanda')
446 + ->set_api_url('https://product.themewinter.com/auth/public/jhanda')
572 447 ->set_plugin_screens('timetics')
573 448 ->set_plugin_screens('toplevel_page_timetics')
574 449 ->call();
575 450 // show get-help and upgrade-to-premium menu.
576 451 // $this->handle_get_help_and_upgrade_menu();
577 - }
578 -
579 - /**
580 - * Add HelpScout script to admin footer
581 - *
582 - * @return void
583 - */
584 - public function admin_helpscout_beacon() {
585 - $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
586 - $is_timetics_screen = false;
587 -
588 - if ( $screen ) {
589 - $screen_id = isset( $screen->id ) ? $screen->id : '';
590 -
591 - // Check: only load on Timetics top-level admin page
592 - if ( 'toplevel_page_timetics' === $screen_id ) {
593 - $is_timetics_screen = true;
594 - }
595 - }
596 -
597 - // Allow overriding detection via filter.
598 - $is_timetics_screen = apply_filters( 'timetics_is_admin_screen', $is_timetics_screen, $screen );
599 -
600 - if ( ! $is_timetics_screen ) {
601 - return;
602 - }
603 - ?>
604 - <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>
605 - <script type="text/javascript">
606 - window.Beacon('config', {
607 - color: "#0060e5",
608 - });
609 - window.Beacon('init', '4be24aa2-ee50-483c-a90c-db4216664d65');
610 - window.Beacon('on', 'ready', function(){
611 - window.Beacon('close');
612 - });
613 -
614 - </script>
615 - <?php
616 452 }
617 453
618 454 }
619 455