PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 4.0.5
Visualizer – Tables & Charts Manager with Built-in AI Generator v4.0.5
4.0.8 4.0.7 4.0.6 4.0.5 4.0.4 4.0.3 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.1 3.1.2 3.1.3 3.10.0 3.10.1 3.10.10 3.10.11 3.10.12 3.10.13 3.10.14 3.10.15 3.10.2 3.10.3 All 149 releases
← All changes | classes/Visualizer/Module/Admin.php +504 -106 3.10.144.0.5 View file →
@@ -29,8 +29,10 @@
29 29 class Visualizer_Module_Admin extends Visualizer_Module {
30 30
31 31 const NAME = __CLASS__;
32 32
33 + const OPTION_GLOBAL_SETTINGS = 'visualizer_global_settings';
34 +
33 35 /**
34 36 * Library page suffix.
35 37 *
36 38 * @since 1.0.0
@@ -40,8 +42,24 @@
40 42 */
41 43 private $_libraryPage;
42 44
43 45 /**
46 + * Support page suffix.
47 + *
48 + * @access private
49 + * @var string
50 + */
51 + private $_supportPage;
52 +
53 + /**
54 + * Settings page suffix.
55 + *
56 + * @access private
57 + * @var string
58 + */
59 + private $_settingsPage;
60 +
61 + /**
44 62 * Constructor.
45 63 *
46 64 * @since 1.0.0
47 65 *
@@ -52,10 +70,14 @@
52 70 public function __construct( Visualizer_Plugin $plugin ) {
53 71 parent::__construct( $plugin );
54 72 $this->_addAction( 'load-post.php', 'enqueueMediaScripts' );
55 73 $this->_addAction( 'load-post-new.php', 'enqueueMediaScripts' );
74 + $this->_addAction( 'enqueue_block_editor_assets', 'enqueueMediaScripts' );
56 75 $this->_addAction( 'admin_footer', 'renderTemplates' );
57 76 $this->_addAction( 'admin_enqueue_scripts', 'enqueueLibraryScripts', null, 0 );
77 + $this->_addAction( 'admin_enqueue_scripts', 'enqueue_support_page' );
78 + $this->_addAction( 'admin_enqueue_scripts', 'enqueueSettingsScripts' );
79 + $this->_addAction( 'admin_post_visualizer_save_global_settings', 'saveGlobalSettings' );
58 80 $this->_addAction( 'admin_menu', 'registerAdminMenu' );
59 81 $this->_addFilter( 'media_view_strings', 'setupMediaViewStrings' );
60 82 $this->_addFilter( 'plugin_action_links', 'getPluginActionLinks', 10, 2 );
61 83 $this->_addFilter( 'plugin_row_meta', 'getPluginMetaLinks', 10, 2 );
@@ -60,8 +82,9 @@
60 82 $this->_addFilter( 'plugin_action_links', 'getPluginActionLinks', 10, 2 );
61 83 $this->_addFilter( 'plugin_row_meta', 'getPluginMetaLinks', 10, 2 );
62 84 $this->_addFilter( 'visualizer_logger_data', 'getLoggerData' );
63 85 $this->_addFilter( 'visualizer_feedback_review_trigger', 'feedbackReviewTrigger' );
86 + $this->_addFilter( 'themeisle_sdk_blackfriday_data', 'add_black_friday_data' );
64 87
65 88 // screen pagination
66 89 $this->_addFilter( 'set-screen-option', 'setScreenOptions', 10, 3 );
67 90
@@ -75,14 +98,45 @@
75 98 $this->_addAction( 'admin_init', 'init' );
76 99
77 100 $this->_addAction( 'visualizer_chart_languages', 'addMultilingualSupport' );
78 101
79 - if ( defined( 'TI_CYPRESS_TESTING' ) ) {
102 + $this->_addFilter( 'admin_footer_text', 'render_review_notice' );
103 +
104 + if ( ! defined( 'TI_E2E_TESTING' ) ) {
105 + $this->_addFilter( 'themeisle-sdk/survey/' . VISUALIZER_DIRNAME, 'get_survey_metadata', 10, 2 );
106 + }
107 +
108 + if ( defined( 'TI_E2E_TESTING' ) ) {
80 109 $this->load_cypress_hooks();
81 110 }
111 + }
112 + /**
113 + * Display review notice.
114 + */
115 + public function render_review_notice( $footer_text ) {
116 + $current_screen = get_current_screen();
82 117
118 + $visualizer_page_ids = array( 'toplevel_page_visualizer', 'visualizer_page_viz-support', 'visualizer_page_ti-about-visualizer' );
119 +
120 + if ( ! empty( $current_screen ) && isset( $current_screen->id ) ) {
121 + foreach ( $visualizer_page_ids as $page_to_check ) {
122 + if ( strpos( $current_screen->id, $page_to_check ) !== false ) {
123 + $footer_text = sprintf(
124 + // translators: %1$s - the name of the plugin (Visualizer), %2$s - message (You can help us by leaving a), %3$s - HTML entity code.
125 + __( 'Enjoying %1$s? %2$s %3$s rating. Thank you for being so supportive!', 'visualizer' ),
126 + '<b>Visualizer</b>',
127 + esc_html__( 'You can help us by leaving a', 'visualizer' ),
128 + '<a href="https://wordpress.org/support/plugin/visualizer/reviews/#new-post" target="_blank">&#9733;&#9733;&#9733;&#9733;&#9733;</a>'
129 + );
130 + break;
131 + }
132 + }
133 + }
134 +
135 + return $footer_text;
83 136 }
84 137
138 +
85 139 /**
86 140 * Define the hooks that are needed for cypress.
87 141 *
88 142 * @since 3.4.0
@@ -90,9 +144,9 @@
90 144 */
91 145 private function load_cypress_hooks() {
92 146 // all charts should load on the same page without pagination.
93 147 add_filter(
94 - 'visualizer_query_args', function( $args ) {
148 + 'visualizer_query_args', function ( $args ) {
95 149 $args['posts_per_page'] = 20;
96 150 return $args;
97 151 }, 10, 1
98 152 );
@@ -113,23 +167,16 @@
113 167 if ( apply_filters( 'visualizer_is_business', false ) ) {
114 168 return;
115 169 }
116 170
117 - $license = __( 'PRO', 'visualizer' );
118 - if ( Visualizer_Module::is_pro() ) {
119 - switch ( $plan ) {
120 - case 1:
121 - $license = __( 'Developer', 'visualizer' );
122 - break;
123 - }
124 - }
171 + $is_new_personal = apply_filters( 'visualizer_is_new_personal', false );
125 172
126 173 $hours = array(
127 - '0' => __( 'Live', 'visualizer' ),
128 - '1' => __( 'Each hour', 'visualizer' ),
129 - '12' => __( 'Each 12 hours', 'visualizer' ),
130 - '24' => __( 'Each day', 'visualizer' ),
131 - '72' => __( 'Each 3 days', 'visualizer' ),
174 + '0.16' => __( '10 minutes', 'visualizer' ),
175 + '1' => __( 'Each hour', 'visualizer' ),
176 + '12' => __( 'Each 12 hours', 'visualizer' ),
177 + '24' => __( 'Each day', 'visualizer' ),
178 + '72' => __( 'Each 3 days', 'visualizer' ),
132 179 );
133 180
134 181 switch ( $feature ) {
135 182 case 'json':
@@ -134,9 +181,9 @@
134 181 switch ( $feature ) {
135 182 case 'json':
136 183 case 'csv':
137 184 // no more schedules if pro is already active.
138 - if ( Visualizer_Module::is_pro() ) {
185 + if ( Visualizer_Module::is_pro() && ! $is_new_personal ) {
139 186 return;
140 187 }
141 188 break;
142 189 case 'wp':
@@ -146,9 +193,9 @@
146 193 default:
147 194 return;
148 195 }
149 196
150 - echo '<optgroup disabled label="' . sprintf( __( 'More in the %s version', 'visualizer' ), $license ) . '">';
197 + echo '<optgroup disabled label="' . __( 'Upgrade required', 'visualizer' ) . '">';
151 198 foreach ( $hours as $hour => $desc ) {
152 199 echo '<option disabled>' . $desc . '</option>';
153 200 }
154 201 echo '</optgroup>';
@@ -216,10 +263,9 @@
216 263 *
217 264 * @access public
218 265 */
219 266 public function init() {
220 - // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
221 - if ( current_user_can( 'edit_posts' ) && current_user_can( 'edit_pages' ) && 'true' == get_user_option( 'rich_editing' ) ) {
267 + if ( current_user_can( 'edit_posts' ) && current_user_can( 'edit_pages' ) && 'true' === get_user_option( 'rich_editing' ) ) {
222 268 $this->_addFilter( 'mce_external_languages', 'add_tinymce_lang', 10, 1 );
223 269 $this->_addFilter( 'mce_external_plugins', 'tinymce_plugin', 10, 1 );
224 270 $this->_addFilter( 'mce_buttons', 'register_mce_button', 10, 1 );
225 271 $this->_addFilter( 'tiny_mce_before_init', 'get_strings_for_block', 10, 1 );
@@ -231,9 +277,9 @@
231 277 *
232 278 * @since ?
233 279 * @access friendly
234 280 */
235 - function get_strings_for_block( $settings ) {
281 + public function get_strings_for_block( $settings ) {
236 282 $class = new Visualizer_Module_Language();
237 283 $strings = $class->get_strings();
238 284 $array = array( 'visualizer_tinymce_plugin' => json_encode( $strings ) );
239 285 return array_merge( $settings, $array );
@@ -309,11 +355,36 @@
309 355 * @access public
310 356 */
311 357 public function enqueueMediaScripts() {
312 358 global $typenow;
313 - if ( post_type_supports( $typenow, 'editor' ) ) {
359 + global $current_screen;
360 +
361 + if ( post_type_supports( $typenow, 'editor' ) || $current_screen->id === 'widgets' || $current_screen->id === 'customize' ) {
314 362 wp_enqueue_style( 'visualizer-media', VISUALIZER_ABSURL . 'css/media.css', array( 'media-views' ), Visualizer_Plugin::VERSION );
315 363
364 + $d3_renderer_asset = VISUALIZER_ABSPATH . '/classes/Visualizer/D3Renderer/build/index.asset.php';
365 + if ( file_exists( $d3_renderer_asset ) && ! wp_script_is( 'visualizer-d3-renderer', 'registered' ) ) {
366 + // @phpstan-ignore-next-line
367 + $d3_asset = include $d3_renderer_asset;
368 + wp_register_script(
369 + 'visualizer-d3-renderer',
370 + VISUALIZER_ABSURL . 'classes/Visualizer/D3Renderer/build/index.js',
371 + array_merge( $d3_asset['dependencies'], array( 'jquery' ) ),
372 + $d3_asset['version'],
373 + true
374 + );
375 + }
376 + if ( wp_script_is( 'visualizer-d3-renderer', 'registered' ) ) {
377 + wp_enqueue_script( 'visualizer-d3-renderer' );
378 + wp_localize_script(
379 + 'visualizer-d3-renderer',
380 + 'vizD3Renderer',
381 + array(
382 + 'iframeJsUrl' => VISUALIZER_ABSURL . 'classes/Visualizer/D3Renderer/build/iframe.js',
383 + )
384 + );
385 + }
386 +
316 387 // Load all the assets for the different libraries we support.
317 388 $deps = array(
318 389 Visualizer_Render_Sidebar_Google::enqueue_assets( array( 'media-editor' ) ),
319 390 Visualizer_Render_Sidebar_Type_DataTable_Tabular::enqueue_assets( array( 'media-editor' ) ),
@@ -327,9 +398,9 @@
327 398 'visualizer-media-view',
328 399 'visualizer',
329 400 array(
330 401 'i10n' => array(
331 - 'insert' => __( 'Insert', 'visualizer' ),
402 + 'insert' => __( 'Insert Chart', 'visualizer' ),
332 403 ),
333 404 )
334 405 );
335 406 wp_enqueue_script( 'visualizer-media-toolbar', VISUALIZER_ABSURL . 'js/media/toolbar.js', array( 'visualizer-media-view' ), Visualizer_Plugin::VERSION, true );
@@ -358,9 +429,9 @@
358 429 'controller' => array(
359 430 'title' => esc_html__( 'Visualizations', 'visualizer' ),
360 431 ),
361 432 'routers' => array(
362 - 'library' => esc_html__( 'From Library', 'visualizer' ),
433 + 'library' => esc_html__( 'Insert from Library', 'visualizer' ),
363 434 'create' => esc_html__( 'Create New', 'visualizer' ),
364 435 ),
365 436 'library' => array(
366 437 'filters' => $chart_types,
@@ -390,9 +461,9 @@
390 461 'enabled' => true,
391 462 );
392 463 }
393 464
394 - $enabled = self::proFeaturesLocked();
465 + $enabled = self::proFeaturesEnabled();
395 466
396 467 $types = array_merge(
397 468 $additional,
398 469 array(
@@ -403,14 +474,14 @@
403 474 ),
404 475 'pie' => array(
405 476 'name' => esc_html__( 'Pie/Donut', 'visualizer' ),
406 477 'enabled' => true,
407 - 'supports' => $enabled ? array( 'Google Charts', 'ChartJS' ) : array( 'Google Charts' ),
478 + 'supports' => array( 'Google Charts', 'ChartJS' ),
408 479 ),
409 480 'line' => array(
410 481 'name' => esc_html__( 'Line', 'visualizer' ),
411 482 'enabled' => true,
412 - 'supports' => $enabled ? array( 'Google Charts', 'ChartJS' ) : array( 'Google Charts' ),
483 + 'supports' => array( 'Google Charts', 'ChartJS' ),
413 484 ),
414 485 'bar' => array(
415 486 'name' => esc_html__( 'Bar', 'visualizer' ),
416 487 'enabled' => true,
@@ -420,18 +491,21 @@
420 491 'name' => esc_html__( 'Area', 'visualizer' ),
421 492 'enabled' => $enabled,
422 493 // in ChartJS, the fill option is used to make Line chart an area: https://www.chartjs.org/docs/latest/charts/area.html
423 494 'supports' => array( 'Google Charts' ),
495 + 'demo_url' => 'https://demo.themeisle.com/visualizer/area-chart/',
424 496 ),
425 497 'geo' => array(
426 498 'name' => esc_html__( 'Geo', 'visualizer' ),
427 499 'enabled' => $enabled,
428 500 'supports' => array( 'Google Charts' ),
501 + 'demo_url' => 'https://demo.themeisle.com/visualizer/geo-chart/',
429 502 ),
430 503 'column' => array(
431 504 'name' => esc_html__( 'Column', 'visualizer' ),
432 505 'enabled' => $enabled,
433 506 'supports' => array( 'Google Charts', 'ChartJS' ),
507 + 'demo_url' => 'https://demo.themeisle.com/visualizer/column-chart/',
434 508 ),
435 509 'bubble' => array(
436 510 'name' => esc_html__( 'Bubble', 'visualizer' ),
437 511 'enabled' => $enabled,
@@ -436,23 +510,27 @@
436 510 'name' => esc_html__( 'Bubble', 'visualizer' ),
437 511 'enabled' => $enabled,
438 512 // chartjs' bubble is ugly looking (and it won't work off the default bubble.csv) so it is being excluded for the time being.
439 513 'supports' => array( 'Google Charts' ),
514 + 'demo_url' => 'https://demo.themeisle.com/visualizer/bubble-chart/',
440 515 ),
441 516 'scatter' => array(
442 517 'name' => esc_html__( 'Scatter', 'visualizer' ),
443 518 'enabled' => $enabled,
444 519 'supports' => array( 'Google Charts' ),
520 + 'demo_url' => 'https://demo.themeisle.com/visualizer/scatter-chart/',
445 521 ),
446 522 'gauge' => array(
447 523 'name' => esc_html__( 'Gauge', 'visualizer' ),
448 524 'enabled' => $enabled,
449 525 'supports' => array( 'Google Charts' ),
526 + 'demo_url' => 'https://demo.themeisle.com/visualizer/gauge-chart/',
450 527 ),
451 528 'candlestick' => array(
452 529 'name' => esc_html__( 'Candlestick', 'visualizer' ),
453 530 'enabled' => $enabled,
454 531 'supports' => array( 'Google Charts' ),
532 + 'demo_url' => 'https://demo.themeisle.com/visualizer/candlestick-chart/',
455 533 ),
456 534 // pro types
457 535 'timeline' => array(
458 536 'name' => esc_html__( 'Timeline', 'visualizer' ),
@@ -457,23 +535,27 @@
457 535 'timeline' => array(
458 536 'name' => esc_html__( 'Timeline', 'visualizer' ),
459 537 'enabled' => false,
460 538 'supports' => array( 'Google Charts', 'ChartJS' ),
539 + 'demo_url' => 'https://demo.themeisle.com/visualizer/timeline-chart/',
461 540 ),
462 541 'combo' => array(
463 542 'name' => esc_html__( 'Combo', 'visualizer' ),
464 543 'enabled' => false,
465 544 'supports' => array( 'Google Charts' ),
545 + 'demo_url' => 'https://demo.themeisle.com/visualizer/combo-chart/',
466 546 ),
467 547 'polarArea' => array(
468 548 'name' => esc_html__( 'Polar Area', 'visualizer' ),
469 549 'enabled' => false,
470 550 'supports' => array( 'ChartJS' ),
551 + 'demo_url' => 'https://demo.themeisle.com/visualizer/polar-area-chart/',
471 552 ),
472 553 'radar' => array(
473 554 'name' => esc_html__( 'Radar/Spider', 'visualizer' ),
474 555 'enabled' => false,
475 556 'supports' => array( 'ChartJS' ),
557 + 'demo_url' => 'https://demo.themeisle.com/visualizer/radar-spider-chart/',
476 558 ),
477 559 )
478 560 );
479 561 $types = apply_filters( 'visualizer_pro_chart_types', $types );
@@ -600,45 +682,159 @@
600 682 *
601 683 * @param string $suffix The current page suffix.
602 684 */
603 685 public function enqueueLibraryScripts( $suffix ) {
604 - if ( $suffix === $this->_libraryPage ) {
605 - wp_enqueue_style( 'visualizer-library', VISUALIZER_ABSURL . 'css/library.css', array(), Visualizer_Plugin::VERSION );
606 - $this->_addFilter( 'media_upload_tabs', 'setupVisualizerTab' );
607 - wp_enqueue_media();
686 + if ( $suffix !== $this->_libraryPage ) {
687 + return;
688 + }
689 +
690 + wp_enqueue_style( 'visualizer-library', VISUALIZER_ABSURL . 'css/library.css', array(), Visualizer_Plugin::VERSION );
691 + $this->_addFilter( 'media_upload_tabs', 'setupVisualizerTab' );
692 + wp_enqueue_media();
693 + wp_enqueue_script(
694 + 'visualizer-library',
695 + VISUALIZER_ABSURL . 'js/library.js',
696 + array(
697 + 'jquery',
698 + 'media-views',
699 + 'clipboard',
700 + ),
701 + Visualizer_Plugin::VERSION,
702 + true
703 + );
704 +
705 + wp_enqueue_script( 'visualizer-customization', $this->get_user_customization_js(), array(), null, true );
706 +
707 + $has_d3 = false;
708 + $query = $this->getQuery();
709 + while ( $query->have_posts() ) {
710 + $chart = $query->next_post();
711 + $library = $this->load_chart_type( $chart->ID );
712 + if ( is_null( $library ) ) {
713 + continue;
714 + }
608 715 wp_enqueue_script(
609 - 'visualizer-library',
610 - VISUALIZER_ABSURL . 'js/library.js',
611 - array(
612 - 'jquery',
613 - 'media-views',
614 - 'clipboard',
615 - ),
716 + "visualizer-render-$library",
717 + VISUALIZER_ABSURL . 'js/render-facade.js',
718 + apply_filters( 'visualizer_assets_render', array( 'visualizer-library', 'visualizer-customization' ), true ),
616 719 Visualizer_Plugin::VERSION,
617 720 true
618 721 );
722 + if ( 'd3' === $library ) {
723 + $has_d3 = true;
724 + }
725 + }
619 726
620 - wp_enqueue_script( 'visualizer-customization', $this->get_user_customization_js(), array(), null, true );
727 + if ( $has_d3 ) {
728 + $d3_renderer_asset = VISUALIZER_ABSPATH . '/classes/Visualizer/D3Renderer/build/index.asset.php';
729 + if ( file_exists( $d3_renderer_asset ) && ! wp_script_is( 'visualizer-d3-renderer', 'registered' ) ) {
730 + /**
731 + * Ignore missing build asset in source checkout.
732 + *
733 + * @phpstan-ignore-next-line
734 + */
735 + $d3_asset = include $d3_renderer_asset;
736 + wp_register_script(
737 + 'visualizer-d3-renderer',
738 + VISUALIZER_ABSURL . 'classes/Visualizer/D3Renderer/build/index.js',
739 + array_merge( $d3_asset['dependencies'], array( 'jquery' ) ),
740 + $d3_asset['version'],
741 + true
742 + );
743 + wp_enqueue_script( 'visualizer-d3-renderer' );
744 + }
745 + wp_localize_script(
746 + 'visualizer-d3-renderer',
747 + 'vizD3Renderer',
748 + array(
749 + 'iframeJsUrl' => VISUALIZER_ABSURL . 'classes/Visualizer/D3Renderer/build/iframe.js',
750 + )
751 + );
752 + }
621 753
622 - $query = $this->getQuery();
623 - while ( $query->have_posts() ) {
624 - $chart = $query->next_post();
625 - $library = $this->load_chart_type( $chart->ID );
626 - if ( is_null( $library ) ) {
627 - continue;
628 - }
629 - wp_enqueue_script(
630 - "visualizer-render-$library",
631 - VISUALIZER_ABSURL . 'js/render-facade.js',
632 - apply_filters( 'visualizer_assets_render', array( 'visualizer-library', 'visualizer-customization' ), true ),
633 - Visualizer_Plugin::VERSION,
754 + $chart_builder_asset = VISUALIZER_ABSPATH . '/classes/Visualizer/ChartBuilder/build/index.asset.php';
755 + if ( file_exists( $chart_builder_asset ) ) {
756 + /**
757 + * Ignore missing build asset in source checkout.
758 + *
759 + * @phpstan-ignore-next-line
760 + */
761 + $asset = include $chart_builder_asset;
762 + $chart_builder_deps = $asset['dependencies'];
763 + if ( ! wp_script_is( 'visualizer-handsontable', 'registered' ) && defined( 'Visualizer_Pro_ABSURL' ) && defined( 'VISUALIZER_PRO_VERSION' ) ) {
764 + wp_register_script(
765 + 'visualizer-handsontable',
766 + Visualizer_Pro_ABSURL . 'vendor/handsontable/dist/handsontable.full.min.js',
767 + array(),
768 + VISUALIZER_PRO_VERSION,
634 769 true
635 770 );
636 771 }
772 + if ( ! wp_style_is( 'visualizer-handsontable', 'registered' ) && defined( 'Visualizer_Pro_ABSURL' ) && defined( 'VISUALIZER_PRO_VERSION' ) ) {
773 + wp_register_style(
774 + 'visualizer-handsontable',
775 + Visualizer_Pro_ABSURL . 'vendor/handsontable/dist/handsontable.full.min.css',
776 + array(),
777 + VISUALIZER_PRO_VERSION
778 + );
779 + }
780 + if ( wp_script_is( 'visualizer-handsontable', 'registered' ) ) {
781 + wp_enqueue_script( 'visualizer-handsontable' );
782 + $chart_builder_deps = array_unique( array_merge( $chart_builder_deps, array( 'visualizer-handsontable' ) ) );
783 + }
784 + if ( wp_style_is( 'visualizer-handsontable', 'registered' ) ) {
785 + wp_enqueue_style( 'visualizer-handsontable' );
786 + }
787 + wp_enqueue_script(
788 + 'visualizer-chart-builder',
789 + VISUALIZER_ABSURL . 'classes/Visualizer/ChartBuilder/build/index.js',
790 + $chart_builder_deps,
791 + $asset['version'],
792 + true
793 + );
794 + wp_enqueue_style(
795 + 'visualizer-chart-builder',
796 + VISUALIZER_ABSURL . 'classes/Visualizer/ChartBuilder/build/style-index.css',
797 + array(),
798 + $asset['version']
799 + );
800 + $chart_builder_css = VISUALIZER_ABSPATH . '/classes/Visualizer/ChartBuilder/build/index.css';
801 + if ( file_exists( $chart_builder_css ) ) {
802 + wp_enqueue_style(
803 + 'visualizer-chart-builder-runtime',
804 + VISUALIZER_ABSURL . 'classes/Visualizer/ChartBuilder/build/index.css',
805 + array( 'visualizer-chart-builder' ),
806 + $asset['version']
807 + );
808 + }
809 + wp_localize_script(
810 + 'visualizer-chart-builder',
811 + 'vizAIBuilder',
812 + array(
813 + 'ajaxUrl' => admin_url( 'admin-ajax.php' ),
814 + 'nonce' => wp_create_nonce( 'visualizer-ai-builder' ),
815 + 'isPro' => Visualizer_Module::is_pro(),
816 + 'upgradeUrl' => tsdk_utmify( Visualizer_Plugin::PRO_TEASER_URL, 'aiBuilderUpgrade', 'dataSource' ),
817 + )
818 + );
637 819 }
820 +
821 + do_action( 'themeisle_internal_page', VISUALIZER_DIRNAME, 'library' );
638 822 }
639 823
640 824 /**
825 + * Enqueue script for support page.
826 + *
827 + * @param string $hook_suffix Current hook.
828 + */
829 + public function enqueue_support_page( $hook_suffix ) {
830 + if ( $this->_supportPage !== $hook_suffix ) {
831 + return;
832 + }
833 + do_action( 'themeisle_internal_page', VISUALIZER_DIRNAME, 'support' );
834 + }
835 +
836 + /**
641 837 * Adds visualizer tab for media upload tabs array.
642 838 *
643 839 * @since 1.0.0
644 840 *
@@ -679,10 +875,20 @@
679 875 __( 'Add New Chart', 'visualizer' ),
680 876 'edit_posts',
681 877 'admin.php?page=' . Visualizer_Plugin::NAME . '&vaction=addnew'
682 878 );
683 - add_submenu_page(
879 +
880 + $this->_settingsPage = add_submenu_page(
684 881 Visualizer_Plugin::NAME,
882 + __( 'Settings', 'visualizer' ),
883 + __( 'Settings', 'visualizer' ),
884 + 'manage_options',
885 + 'viz-settings',
886 + array( $this, 'renderSettingsPage' )
887 + );
888 +
889 + $this->_supportPage = add_submenu_page(
890 + Visualizer_Plugin::NAME,
685 891 __( 'Support', 'visualizer' ),
686 892 __( 'Support', 'visualizer' ) . '<span class="dashicons dashicons-editor-help more-features-icon" style="width: 17px; height: 17px; margin-left: 4px; color: #ffca54; font-size: 17px; vertical-align: -3px;"></span>',
687 893 'edit_posts',
688 894 'viz-support',
@@ -707,9 +913,9 @@
707 913 font-size: 14px;
708 914 font-weight: 600;
709 915 color: #fff;
710 916 }
711 - #toplevel_page_visualizer ul.wp-submenu li:last-child > a {
917 + #toplevel_page_visualizer ul.wp-submenu li:last-child > a > span {
712 918 color: #fff !important;
713 919 }
714 920 #toplevel_page_visualizer ul.wp-submenu li:last-child > a:hover {
715 921 box-shadow: inherit;
@@ -730,9 +936,9 @@
730 936
731 937 /**
732 938 * Adds the screen options for pagination.
733 939 */
734 - function addScreenOptions() {
940 + public function addScreenOptions() {
735 941 $screen = get_current_screen();
736 942
737 943 // bail if it's some other page.
738 944 if ( ! is_object( $screen ) || $screen->id !== $this->_libraryPage ) {
@@ -749,9 +955,9 @@
749 955
750 956 /**
751 957 * Returns the screen option for pagination.
752 958 */
753 - function setScreenOptions( $status, $option, $value ) {
959 + public function setScreenOptions( $status, $option, $value ) {
754 960 if ( 'visualizer_library_per_page' === $option ) {
755 961 return $value;
756 962 }
757 963 }
@@ -899,13 +1105,95 @@
899 1105 * @access public
900 1106 */
901 1107 public function renderSupportPage() {
902 1108 wp_enqueue_style( 'visualizer-upsell', VISUALIZER_ABSURL . 'css/upsell.css', array(), Visualizer_Plugin::VERSION );
903 - $this->load_survey();
904 1109 include_once VISUALIZER_ABSPATH . '/templates/support.php';
905 1110 }
906 1111
907 1112 /**
1113 + * Enqueues scripts/styles for the Settings page.
1114 + *
1115 + * @access public
1116 + */
1117 + public function enqueueSettingsScripts( string $hook_suffix ): void {
1118 + if ( $this->_settingsPage !== $hook_suffix ) {
1119 + return;
1120 + }
1121 + wp_enqueue_style( 'wp-color-picker' );
1122 + wp_enqueue_script( 'wp-color-picker' );
1123 + }
1124 +
1125 + /**
1126 + * Renders the global style settings page.
1127 + *
1128 + * @access public
1129 + */
1130 + public function renderSettingsPage(): void {
1131 + if ( ! current_user_can( 'manage_options' ) ) {
1132 + wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'visualizer' ) );
1133 + }
1134 + $settings = self::getGlobalSettings();
1135 + include_once VISUALIZER_ABSPATH . '/templates/global-settings.php';
1136 + }
1137 +
1138 + /**
1139 + * Returns the global style settings option.
1140 + *
1141 + * @return array<string, string>
1142 + * @access public
1143 + * @static
1144 + */
1145 + public static function getGlobalSettings(): array {
1146 + $defaults = array(
1147 + 'color_primary' => '',
1148 + 'color_secondary' => '',
1149 + 'apply_existing' => '0',
1150 + );
1151 + $saved = get_option( self::OPTION_GLOBAL_SETTINGS, array() );
1152 + return wp_parse_args( $saved, $defaults );
1153 + }
1154 +
1155 + /**
1156 + * Handles saving of the global style settings.
1157 + *
1158 + * @access public
1159 + */
1160 + public function saveGlobalSettings(): void {
1161 + if ( ! current_user_can( 'manage_options' ) ) {
1162 + wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'visualizer' ) );
1163 + }
1164 +
1165 + check_admin_referer( 'visualizer_save_global_settings' );
1166 +
1167 + $color_primary = sanitize_hex_color( wp_unslash( $_POST['visualizer_color_primary'] ?? '' ) );
1168 + $color_secondary = sanitize_hex_color( wp_unslash( $_POST['visualizer_color_secondary'] ?? '' ) );
1169 + $apply_existing = ! empty( $_POST['visualizer_apply_existing'] ) ? '1' : '0';
1170 + $clear = ! empty( $_POST['visualizer_clear_settings'] );
1171 +
1172 + if ( $clear ) {
1173 + delete_option( self::OPTION_GLOBAL_SETTINGS );
1174 + } else {
1175 + $settings = array(
1176 + 'color_primary' => $color_primary ? $color_primary : '',
1177 + 'color_secondary' => $color_secondary ? $color_secondary : '',
1178 + 'apply_existing' => $apply_existing,
1179 + );
1180 + update_option( self::OPTION_GLOBAL_SETTINGS, $settings );
1181 + }
1182 +
1183 + wp_safe_redirect(
1184 + add_query_arg(
1185 + array(
1186 + 'page' => 'viz-settings',
1187 + 'updated' => $clear ? 'cleared' : 'true',
1188 + ),
1189 + admin_url( 'admin.php' )
1190 + )
1191 + );
1192 + exit;
1193 + }
1194 +
1195 + /**
908 1196 * Renders visualizer library page.
909 1197 *
910 1198 * @since 1.0.0
911 1199 *
@@ -989,8 +1277,9 @@
989 1277
990 1278 // add chart to the array
991 1279 $charts[ $id ] = array(
992 1280 'id' => $chart->ID,
1281 + 'title' => $chart->post_title,
993 1282 'type' => $type,
994 1283 'series' => $series,
995 1284 'settings' => $settings,
996 1285 'data' => $data,
@@ -995,11 +1284,15 @@
995 1284 'settings' => $settings,
996 1285 'data' => $data,
997 1286 'library' => $library,
998 1287 );
1288 + if ( 'd3' === $library ) {
1289 + $charts[ $id ]['code'] = get_post_meta( $chart->ID, Visualizer_Module_AIBuilder::CF_D3_CODE, true );
1290 + }
999 1291 }
1000 1292 // enqueue charts array
1001 1293 $ajaxurl = admin_url( 'admin-ajax.php' );
1294 +
1002 1295 wp_localize_script(
1003 1296 'visualizer-library',
1004 1297 'visualizer',
1005 1298 array(
@@ -1036,8 +1329,9 @@
1036 1329 'i10n' => array(
1037 1330 'copied' => __( 'The shortcode has been copied to your clipboard. Hit Ctrl-V/Cmd-V to paste it.', 'visualizer' ),
1038 1331 'conflict' => __( 'We have detected a potential conflict with another component that prevents Visualizer from functioning properly. Please disable any of the following components if they are activated on your instance: Modern Events Calendar plugin, Acronix plugin. In case the aforementioned components are not activated or you continue to see this error message, please disable all other plugins and enable them one by one to find out the component that is causing the conflict.', 'visualizer' ),
1039 1332 ),
1333 + 'is_pro_user' => Visualizer_Module::is_pro(),
1040 1334 )
1041 1335 );
1042 1336 // render library page
1043 1337 $render = new Visualizer_Render_Library();
@@ -1054,10 +1348,8 @@
1054 1348 'type' => 'array',
1055 1349 )
1056 1350 );
1057 1351
1058 - $this->load_survey();
1059 -
1060 1352 $render->render();
1061 1353 }
1062 1354
1063 1355 /**
@@ -1099,19 +1391,36 @@
1099 1391 *
1100 1392 * @return array Updated array of plugin meta links.
1101 1393 */
1102 1394 public function getPluginMetaLinks( $plugin_meta, $plugin_file ) {
1395 + if ( Visualizer_Module::is_pro() ) {
1396 + return $plugin_meta;
1397 + }
1398 +
1399 + // Also suppress the upsell when Pro is installed but not currently active.
1400 + if ( ! function_exists( 'get_plugins' ) ) {
1401 + require_once ABSPATH . 'wp-admin/includes/plugin.php';
1402 + }
1403 + foreach ( array_keys( get_plugins() ) as $installed_plugin ) {
1404 + if ( false !== strpos( $installed_plugin, 'visualizer-pro' ) ) {
1405 + return $plugin_meta;
1406 + }
1407 + }
1408 +
1103 1409 if ( $plugin_file === plugin_basename( VISUALIZER_BASEFILE ) ) {
1410 + $is_black_friday = apply_filters( 'themeisle_sdk_is_black_friday_sale', false );
1104 1411 // knowledge base link
1105 1412 $plugin_meta[] = sprintf(
1106 1413 '<a href="' . VISUALIZER_MAIN_DOC . '" target="_blank">%s</a>',
1107 1414 esc_html__( 'Docs', 'visualizer' )
1108 1415 );
1109 - // flattr link
1110 - $plugin_meta[] = sprintf(
1111 - '<a style="color:red" href="' . tsdk_utmify( Visualizer_Plugin::PRO_TEASER_URL, 'pluginrow' ) . '" target="_blank">%s</a>',
1112 - esc_html__( 'Pro Addon', 'visualizer' )
1113 - );
1416 + if ( ! $is_black_friday ) {
1417 + // flattr link
1418 + $plugin_meta[] = sprintf(
1419 + '<a style="color:red" href="' . tsdk_utmify( Visualizer_Plugin::PRO_TEASER_URL, 'pluginrow' ) . '" target="_blank">%s</a>',
1420 + esc_html__( 'Get Visualizer Pro', 'visualizer' )
1421 + );
1422 + }
1114 1423 }
1115 1424
1116 1425 return $plugin_meta;
1117 1426 }
@@ -1116,17 +1425,39 @@
1116 1425 return $plugin_meta;
1117 1426 }
1118 1427
1119 1428 /**
1120 - * If check is existing user.
1429 + * Returns true when premium chart types should be enabled for the current user.
1121 1430 *
1122 - * @return bool Default false
1431 + * Pro 1.9.0+ hooks the 'visualizer_is_pro' filter and returns true only when
1432 + * the license is valid. Pre-1.9.0 Pro defined a Visualizer_Pro class instead;
1433 + * we detect that as a fallback so those legacy installs still work.
1434 + *
1435 + * Passing false as the filter default ensures the constant VISUALIZER_PRO
1436 + * (which is set to true whenever the Pro class exists, regardless of license
1437 + * state) cannot bypass the license check.
1438 + *
1439 + * @return bool
1123 1440 */
1124 - public static function proFeaturesLocked() {
1125 - if ( Visualizer_Module::is_pro() ) {
1441 + public static function proFeaturesEnabled() {
1442 + $is_pro_filter = apply_filters( 'visualizer_is_pro', false );
1443 +
1444 + // Pro 1.9.0+: filter is hooked and returns true only with a valid license.
1445 + if ( $is_pro_filter ) {
1126 1446 return true;
1127 1447 }
1128 - return 'yes' === get_option( 'visualizer-new-user', 'yes' ) ? false : true;
1448 +
1449 + // Pro is installed (active) but the license check above did not pass.
1450 + // Do NOT fall through to the legacy "old user" path — that path exists
1451 + // only for sites that never had Pro installed. If Pro is present but
1452 + // the license is inactive we should lock, regardless of chart history.
1453 + if ( class_exists( 'Visualizer_Pro', false ) ) {
1454 + return false;
1455 + }
1456 +
1457 + // No Pro installed at all: grant legacy access to existing users so
1458 + // their charts are not suddenly broken when they upgrade the free plugin.
1459 + return 'yes' === get_option( 'visualizer-new-user' ) ? false : true;
1129 1460 }
1130 1461
1131 1462 /**
1132 1463 * Multilingual Support.
@@ -1189,72 +1520,139 @@
1189 1520
1190 1521 /**
1191 1522 * Get the survey metadata.
1192 1523 *
1524 + * @param array $data The data for survey in Formbricks format.
1525 + * @param string $page_slug The slug of the loaded page.
1526 + *
1193 1527 * @return array The survey metadata.
1194 1528 */
1195 - private function get_survey_metadata() {
1196 - $install_date = get_option( 'visualizer_install', false );
1197 - $install_category = 0;
1529 + public function get_survey_metadata( $data, $page_slug ) {
1530 + $install_date = get_option( 'visualizer_install', time() );
1531 + $install_days_number = intval( ( time() - $install_date ) / DAY_IN_SECONDS );
1198 1532
1199 - if ( false !== $install_date ) {
1200 - $days_since_install = round( ( time() - $install_date ) / DAY_IN_SECONDS );
1533 + $license_status = apply_filters( 'product_visualizer_license_status', 'invalid' );
1534 + $license_plan = apply_filters( 'product_visualizer_license_plan', false );
1535 + $license_key = apply_filters( 'product_visualizer_license_key', false );
1201 1536
1202 - if ( 0 === $days_since_install || 1 === $days_since_install ) {
1203 - $install_category = 0;
1204 - } elseif ( 1 < $days_since_install && 8 > $days_since_install ) {
1205 - $install_category = 7;
1206 - } elseif ( 8 <= $days_since_install && 31 > $days_since_install ) {
1207 - $install_category = 30;
1208 - } elseif ( 30 < $days_since_install && 90 > $days_since_install ) {
1209 - $install_category = 90;
1210 - } elseif ( 90 <= $days_since_install ) {
1211 - $install_category = 91;
1212 - }
1213 - }
1214 -
1215 1537 $plugin_data = get_plugin_data( VISUALIZER_BASEFILE, false, false );
1216 1538 $plugin_version = '';
1539 +
1217 1540 if ( ! empty( $plugin_data['Version'] ) ) {
1218 1541 $plugin_version = $plugin_data['Version'];
1219 1542 }
1220 1543
1221 - $user_id = 'visualizer_' . preg_replace( '/[^\w\d]*/', '', get_site_url() ); // Use a normalized version of the site URL as a user ID.
1544 + $count_charts_cache_key = 'visualizer_count_charts';
1545 + $charts_number = get_transient( $count_charts_cache_key );
1222 1546
1223 - $license_data = get_option( 'visualizer_pro_license_data', false );
1224 - if ( false !== $license_data ) {
1225 - $user_id = 'visualizer_' . $license_data->key;
1547 + if ( false === $charts_number ) {
1548 + $charts_number = $this->count_charts( 100 );
1549 + set_transient( $count_charts_cache_key, $charts_number, 100 === $charts_number ? WEEK_IN_SECONDS : 6 * HOUR_IN_SECONDS );
1550 + } else {
1551 + $charts_number = strval( $charts_number );
1226 1552 }
1227 1553
1228 - return array(
1229 - 'userId' => $user_id,
1554 + $data = array(
1555 + 'environmentId' => 'cltef8cut1s7wyyfxy3rlxzs5',
1230 1556 'attributes' => array(
1231 - 'days_since_install' => $install_category,
1232 - 'free_version' => $plugin_version,
1233 - 'pro_version' => defined( 'VISUALIZER_PRO_VERSION' ) ? VISUALIZER_PRO_VERSION : '',
1234 - 'license_status' => apply_filters( 'product_visualizer_license_status', 'invalid' ),
1557 + 'free_version' => $plugin_version,
1558 + 'pro_version' => defined( 'VISUALIZER_PRO_VERSION' ) ? VISUALIZER_PRO_VERSION : '',
1559 + 'license_status' => $license_status,
1560 + 'install_days_number' => $install_days_number,
1561 + 'charts_number' => $charts_number,
1235 1562 ),
1236 1563 );
1564 +
1565 + if ( ! empty( $license_plan ) ) {
1566 + $data['attributes']['plan'] = $license_plan;
1567 + }
1568 +
1569 + if ( ! empty( $license_key ) ) {
1570 + $data['attributes']['license_key'] = apply_filters( 'themeisle_sdk_secret_masking', $license_key );
1571 + }
1572 +
1573 + return $data;
1237 1574 }
1238 1575
1239 1576 /**
1240 - * Load the survey.
1577 + * Count the charts.
1578 + *
1579 + * @param int $limit The count limit (optional).
1580 + *
1581 + * @return int The number of charts.
1241 1582 */
1242 - private function load_survey() {
1583 + public function count_charts( $limit = -1 ) {
1584 + $args = array(
1585 + 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
1586 + 'post_status' => 'publish',
1587 + 'posts_per_page' => $limit,
1588 + 'fields' => 'ids',
1589 + );
1243 1590
1244 - if ( defined( 'TI_CYPRESS_TESTING' ) ) {
1245 - return;
1591 + $query = new WP_Query( $args);
1592 + return $query->post_count;
1593 + }
1594 +
1595 + /**
1596 + * Set the black friday data.
1597 + *
1598 + * @param array $configs The configuration array for the loaded products.
1599 + * @return array
1600 + */
1601 + public function add_black_friday_data( $configs ) {
1602 + $config = $configs['default'];
1603 +
1604 + $message = __( 'Database queries, private charts, auto-sync. Go beyond basic charts. Exclusively for existing Visualizer users.', 'visualizer' );
1605 + $cta_label = __( 'Get Visualizer Pro', 'visualizer' );
1606 +
1607 + $plan = apply_filters( 'product_visualizer_license_plan', 0 );
1608 + $license = apply_filters( 'product_visualizer_license_key', false );
1609 + $status = apply_filters( 'product_visualizer_license_status', false );
1610 + $pro_product_slug = defined( 'VISUALIZER_PRO_BASEFILE' ) ? basename( dirname( VISUALIZER_PRO_BASEFILE ) ) : '';
1611 +
1612 + $is_pro = 'valid' === $status;
1613 + $is_expired = 'expired' === $status || 'active-expired' === $status;
1614 +
1615 + if ( $is_pro ) {
1616 + // translators: %s is the discount percentage.
1617 + $config['plugin_meta_message'] = sprintf( __( 'Black Friday Sale - up to %s off', 'visualizer' ), '30%' );
1618 + // translators: %1$s - discount, %2$s - discount.
1619 + $message = sprintf( __( 'Upgrade your Visualizer Pro plan: %1$s off this week. Already on the plan you need? Renew early and save up to %2$s.', 'visualizer' ), '30%', '20%' );
1620 + $cta_label = __( 'See your options', 'visualizer' );
1621 + } elseif ( $is_expired ) {
1622 + // translators: %s is the discount percentage.
1623 + $config['upgrade_menu_text'] = sprintf( __( 'BF Sale - %s off', 'visualizer' ), '50%' );
1624 + // translators: %s is the discount percentage.
1625 + $config['plugin_meta_message'] = sprintf( __( 'Black Friday Sale - %s off', 'visualizer' ), '50%' );
1626 + $message = __( 'Your Visualizer Pro features are still here, just locked. Renew at a reduced rate this week.', 'visualizer' );
1627 + $cta_label = __( 'Reactivate now', 'visualizer' );
1628 + } else {
1629 + // translators: %s is the discount percentage.
1630 + $config['plugin_meta_message'] = sprintf( __( 'Black Friday Sale - %s off', 'visualizer' ), '60%' );
1631 + $config['title'] = __( 'Visualizer Pro: 60% off this week', 'visualizer' );
1632 + // translators: %s is the discount percentage.
1633 + $config['upgrade_menu_text'] = sprintf( __( 'BF Sale - %s off', 'visualizer' ), '60%' );
1246 1634 }
1247 1635
1248 - $survey_handler = apply_filters( 'themeisle_sdk_dependency_script_handler', 'survey' );
1636 + $url_params = array(
1637 + 'utm_term' => $is_pro ? 'plan-' . $plan : 'free',
1638 + 'lkey' => ! empty( $license ) ? $license : false,
1639 + 'expired' => $is_expired ? '1' : false,
1640 + );
1249 1641
1250 - if ( empty( $survey_handler ) ) {
1251 - return;
1642 + if ( ( $is_pro || $is_expired ) && ! empty( $pro_product_slug ) ) {
1643 + $config['plugin_meta_targets'] = array( $pro_product_slug );
1252 1644 }
1253 1645
1254 - $metadata = $this->get_survey_metadata();
1646 + $config['message'] = $message;
1647 + $config['cta_label'] = $cta_label;
1255 1648
1256 - do_action( 'themeisle_sdk_dependency_enqueue_script', 'survey' );
1257 - wp_enqueue_script( 'visualizer_chart_survey', VISUALIZER_ABSURL . 'js/survey.js', array( $survey_handler ), $metadata['attributes']['free_version'], true );
1258 - wp_localize_script( 'visualizer_chart_survey', 'visualizerSurveyData', $metadata );
1649 + $config['sale_url'] = add_query_arg(
1650 + $url_params,
1651 + tsdk_translate_link( tsdk_utmify( 'https://themeisle.link/vizualizer-bf', 'bfcm', 'visualizer' ) )
1652 + );
1653 +
1654 + $configs[ VISUALIZER_DIRNAME ] = $config;
1655 +
1656 + return $configs;
1259 1657 }
1260 1658 }