PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 4.0.1
Visualizer – Tables & Charts Manager with Built-in AI Generator v4.0.1
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 +526 -88 3.10.04.0.1 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 *
@@ -670,9 +866,9 @@
670 866 Visualizer_Plugin::NAME,
671 867 __( 'Chart Library', 'visualizer' ),
672 868 __( 'Chart Library', 'visualizer' ),
673 869 'edit_posts',
674 - admin_url( 'admin.php?page=' . Visualizer_Plugin::NAME )
870 + 'admin.php?page=' . Visualizer_Plugin::NAME
675 871 );
676 872 add_submenu_page(
677 873 Visualizer_Plugin::NAME,
678 874 __( 'Add New Chart', 'visualizer' ),
@@ -677,12 +873,22 @@
677 873 Visualizer_Plugin::NAME,
678 874 __( 'Add New Chart', 'visualizer' ),
679 875 __( 'Add New Chart', 'visualizer' ),
680 876 'edit_posts',
681 - admin_url( 'admin.php?page=' . Visualizer_Plugin::NAME . '&vaction=addnew' )
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',
@@ -688,22 +894,12 @@
688 894 'viz-support',
689 895 array( $this, 'renderSupportPage' )
690 896 );
691 897
692 - if ( ! Visualizer_Module::is_pro() ) {
693 - add_submenu_page(
694 - Visualizer_Plugin::NAME,
695 - __( 'Get Visualizer Pro', 'visualizer' ),
696 - __( 'Get Visualizer Pro', 'visualizer' ),
697 - 'edit_posts',
698 - 'viz-get-pro',
699 - '__return_null'
700 - );
701 - add_action( 'admin_footer', array( $this, 'handleGetProSubMenu' ) );
702 - }
703 898 remove_submenu_page( Visualizer_Plugin::NAME, Visualizer_Plugin::NAME );
704 899
705 900 add_action( "load-{$this->_libraryPage}", array( $this, 'addScreenOptions' ) );
901 + add_action( 'admin_footer', array( $this, 'handleGetProSubMenu' ) );
706 902 }
707 903
708 904 /**
709 905 * Handle get pro plugin submenu.
@@ -708,41 +904,41 @@
708 904 /**
709 905 * Handle get pro plugin submenu.
710 906 */
711 907 public function handleGetProSubMenu() {
712 - ?>
908 + if ( ! Visualizer_Module::is_pro() ) {
909 + ?>
713 910 <style type="text/css">
714 - #toplevel_page_visualizer ul.wp-submenu li.wp-first-item + li + li + li {
911 + #toplevel_page_visualizer ul.wp-submenu li:last-child {
715 912 background: #FF7E65;
716 913 font-size: 14px;
717 914 font-weight: 600;
718 915 color: #fff;
719 916 }
720 - #toplevel_page_visualizer ul.wp-submenu li.wp-first-item + li + li + li > a {
917 + #toplevel_page_visualizer ul.wp-submenu li:last-child > a > span {
721 918 color: #fff !important;
722 919 }
723 - #toplevel_page_visualizer ul.wp-submenu li.wp-first-item + li + li + li > a:hover {
920 + #toplevel_page_visualizer ul.wp-submenu li:last-child > a:hover {
724 921 box-shadow: inherit;
725 922 }
726 - #toplevel_page_visualizer ul.wp-submenu li.wp-first-item + li + li + li + li {
727 - display: none;
923 + </style>
924 + <?php
925 + if ( get_option( 'visualizer_fresh_install', false ) ) {
926 + ?>
927 + <style type="text/css">
928 + #toplevel_page_visualizer ul.wp-submenu li.wp-first-item + li + li + li {
929 + display: none;
930 + }
931 + </style>
932 + <?php
728 933 }
729 - </style>
730 - <script type="text/javascript">
731 - jQuery( document ).ready( function() {
732 - jQuery( '#toplevel_page_visualizer' ).on( 'click', 'li:not(.wp-submenu-head, .wp-first-item):eq(2)', function( e ) {
733 - e.preventDefault();
734 - window.open( '<?php echo tsdk_utmify( 'https://themeisle.com/plugins/visualizer-charts-and-graphs/upgrade/', 'toplevel' ); ?>', '_blank' );
735 - } );
736 - } );
737 - </script>
738 - <?php
934 + }
739 935 }
740 936
741 937 /**
742 938 * Adds the screen options for pagination.
743 939 */
744 - function addScreenOptions() {
940 + public function addScreenOptions() {
745 941 $screen = get_current_screen();
746 942
747 943 // bail if it's some other page.
748 944 if ( ! is_object( $screen ) || $screen->id !== $this->_libraryPage ) {
@@ -759,9 +955,9 @@
759 955
760 956 /**
761 957 * Returns the screen option for pagination.
762 958 */
763 - function setScreenOptions( $status, $option, $value ) {
959 + public function setScreenOptions( $status, $option, $value ) {
764 960 if ( 'visualizer_library_per_page' === $option ) {
765 961 return $value;
766 962 }
767 963 }
@@ -913,8 +1109,91 @@
913 1109 include_once VISUALIZER_ABSPATH . '/templates/support.php';
914 1110 }
915 1111
916 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 + /**
917 1196 * Renders visualizer library page.
918 1197 *
919 1198 * @since 1.0.0
920 1199 *
@@ -998,8 +1277,9 @@
998 1277
999 1278 // add chart to the array
1000 1279 $charts[ $id ] = array(
1001 1280 'id' => $chart->ID,
1281 + 'title' => $chart->post_title,
1002 1282 'type' => $type,
1003 1283 'series' => $series,
1004 1284 'settings' => $settings,
1005 1285 'data' => $data,
@@ -1004,11 +1284,15 @@
1004 1284 'settings' => $settings,
1005 1285 'data' => $data,
1006 1286 'library' => $library,
1007 1287 );
1288 + if ( 'd3' === $library ) {
1289 + $charts[ $id ]['code'] = get_post_meta( $chart->ID, Visualizer_Module_AIBuilder::CF_D3_CODE, true );
1290 + }
1008 1291 }
1009 1292 // enqueue charts array
1010 1293 $ajaxurl = admin_url( 'admin-ajax.php' );
1294 +
1011 1295 wp_localize_script(
1012 1296 'visualizer-library',
1013 1297 'visualizer',
1014 1298 array(
@@ -1045,8 +1329,9 @@
1045 1329 'i10n' => array(
1046 1330 'copied' => __( 'The shortcode has been copied to your clipboard. Hit Ctrl-V/Cmd-V to paste it.', 'visualizer' ),
1047 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' ),
1048 1332 ),
1333 + 'is_pro_user' => Visualizer_Module::is_pro(),
1049 1334 )
1050 1335 );
1051 1336 // render library page
1052 1337 $render = new Visualizer_Render_Library();
@@ -1062,8 +1347,9 @@
1062 1347 'total' => $query->max_num_pages,
1063 1348 'type' => 'array',
1064 1349 )
1065 1350 );
1351 +
1066 1352 $render->render();
1067 1353 }
1068 1354
1069 1355 /**
@@ -1105,8 +1391,22 @@
1105 1391 *
1106 1392 * @return array Updated array of plugin meta links.
1107 1393 */
1108 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 +
1109 1409 if ( $plugin_file === plugin_basename( VISUALIZER_BASEFILE ) ) {
1110 1410 // knowledge base link
1111 1411 $plugin_meta[] = sprintf(
1112 1412 '<a href="' . VISUALIZER_MAIN_DOC . '" target="_blank">%s</a>',
@@ -1114,9 +1414,9 @@
1114 1414 );
1115 1415 // flattr link
1116 1416 $plugin_meta[] = sprintf(
1117 1417 '<a style="color:red" href="' . tsdk_utmify( Visualizer_Plugin::PRO_TEASER_URL, 'pluginrow' ) . '" target="_blank">%s</a>',
1118 - esc_html__( 'Pro Addon', 'visualizer' )
1418 + esc_html__( 'Get Visualizer Pro', 'visualizer' )
1119 1419 );
1120 1420 }
1121 1421
1122 1422 return $plugin_meta;
@@ -1122,17 +1422,39 @@
1122 1422 return $plugin_meta;
1123 1423 }
1124 1424
1125 1425 /**
1126 - * If check is existing user.
1426 + * Returns true when premium chart types should be enabled for the current user.
1127 1427 *
1128 - * @return bool Default false
1428 + * Pro 1.9.0+ hooks the 'visualizer_is_pro' filter and returns true only when
1429 + * the license is valid. Pre-1.9.0 Pro defined a Visualizer_Pro class instead;
1430 + * we detect that as a fallback so those legacy installs still work.
1431 + *
1432 + * Passing false as the filter default ensures the constant VISUALIZER_PRO
1433 + * (which is set to true whenever the Pro class exists, regardless of license
1434 + * state) cannot bypass the license check.
1435 + *
1436 + * @return bool
1129 1437 */
1130 - public static function proFeaturesLocked() {
1131 - if ( Visualizer_Module::is_pro() ) {
1438 + public static function proFeaturesEnabled() {
1439 + $is_pro_filter = apply_filters( 'visualizer_is_pro', false );
1440 +
1441 + // Pro 1.9.0+: filter is hooked and returns true only with a valid license.
1442 + if ( $is_pro_filter ) {
1132 1443 return true;
1133 1444 }
1134 - return 'yes' === get_option( 'visualizer-new-user', 'yes' ) ? false : true;
1445 +
1446 + // Pro is installed (active) but the license check above did not pass.
1447 + // Do NOT fall through to the legacy "old user" path — that path exists
1448 + // only for sites that never had Pro installed. If Pro is present but
1449 + // the license is inactive we should lock, regardless of chart history.
1450 + if ( class_exists( 'Visualizer_Pro', false ) ) {
1451 + return false;
1452 + }
1453 +
1454 + // No Pro installed at all: grant legacy access to existing users so
1455 + // their charts are not suddenly broken when they upgrade the free plugin.
1456 + return 'yes' === get_option( 'visualizer-new-user' ) ? false : true;
1135 1457 }
1136 1458
1137 1459 /**
1138 1460 * Multilingual Support.
@@ -1190,6 +1512,122 @@
1190 1512 if ( isset( $types[ $type ] ) ) {
1191 1513 return ! empty( $types[ $type ]['enabled'] );
1192 1514 }
1193 1515 return false;
1516 + }
1517 +
1518 + /**
1519 + * Get the survey metadata.
1520 + *
1521 + * @param array $data The data for survey in Formbricks format.
1522 + * @param string $page_slug The slug of the loaded page.
1523 + *
1524 + * @return array The survey metadata.
1525 + */
1526 + public function get_survey_metadata( $data, $page_slug ) {
1527 + $install_date = get_option( 'visualizer_install', time() );
1528 + $install_days_number = intval( ( time() - $install_date ) / DAY_IN_SECONDS );
1529 +
1530 + $license_status = apply_filters( 'product_visualizer_license_status', 'invalid' );
1531 + $license_plan = apply_filters( 'product_visualizer_license_plan', false );
1532 + $license_key = apply_filters( 'product_visualizer_license_key', false );
1533 +
1534 + $plugin_data = get_plugin_data( VISUALIZER_BASEFILE, false, false );
1535 + $plugin_version = '';
1536 +
1537 + if ( ! empty( $plugin_data['Version'] ) ) {
1538 + $plugin_version = $plugin_data['Version'];
1539 + }
1540 +
1541 + $count_charts_cache_key = 'visualizer_count_charts';
1542 + $charts_number = get_transient( $count_charts_cache_key );
1543 +
1544 + if ( false === $charts_number ) {
1545 + $charts_number = $this->count_charts( 100 );
1546 + set_transient( $count_charts_cache_key, $charts_number, 100 === $charts_number ? WEEK_IN_SECONDS : 6 * HOUR_IN_SECONDS );
1547 + } else {
1548 + $charts_number = strval( $charts_number );
1549 + }
1550 +
1551 + $data = array(
1552 + 'environmentId' => 'cltef8cut1s7wyyfxy3rlxzs5',
1553 + 'attributes' => array(
1554 + 'free_version' => $plugin_version,
1555 + 'pro_version' => defined( 'VISUALIZER_PRO_VERSION' ) ? VISUALIZER_PRO_VERSION : '',
1556 + 'license_status' => $license_status,
1557 + 'install_days_number' => $install_days_number,
1558 + 'charts_number' => $charts_number,
1559 + ),
1560 + );
1561 +
1562 + if ( ! empty( $license_plan ) ) {
1563 + $data['attributes']['plan'] = $license_plan;
1564 + }
1565 +
1566 + if ( ! empty( $license_key ) ) {
1567 + $data['attributes']['license_key'] = apply_filters( 'themeisle_sdk_secret_masking', $license_key );
1568 + }
1569 +
1570 + return $data;
1571 + }
1572 +
1573 + /**
1574 + * Count the charts.
1575 + *
1576 + * @param int $limit The count limit (optional).
1577 + *
1578 + * @return int The number of charts.
1579 + */
1580 + public function count_charts( $limit = -1 ) {
1581 + $args = array(
1582 + 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
1583 + 'post_status' => 'publish',
1584 + 'posts_per_page' => $limit,
1585 + 'fields' => 'ids',
1586 + );
1587 +
1588 + $query = new WP_Query( $args);
1589 + return $query->post_count;
1590 + }
1591 +
1592 + /**
1593 + * Set the black friday data.
1594 + *
1595 + * @param array $configs The configuration array for the loaded products.
1596 + * @return array
1597 + */
1598 + public function add_black_friday_data( $configs ) {
1599 + $config = $configs['default'];
1600 +
1601 + // translators: %1$s - HTML tag, %2$s - discount, %3$s - HTML tag, %4$s - product name.
1602 + $message_template = __( 'Our biggest sale of the year: %1$sup to %2$s OFF%3$s on %4$s. Don\'t miss this limited-time offer.', 'visualizer' );
1603 + $product_label = 'Visualizer';
1604 + $discount = '70%';
1605 +
1606 + $plan = apply_filters( 'product_visualizer_license_plan', 0 );
1607 + $license = apply_filters( 'product_visualizer_license_key', false );
1608 + $is_pro = 0 < $plan;
1609 +
1610 + if ( $is_pro ) {
1611 + // translators: %1$s - HTML tag, %2$s - discount, %3$s - HTML tag, %4$s - product name.
1612 + $message_template = __( 'Get %1$sup to %2$s off%3$s when you upgrade your %4$s plan or renew early.', 'visualizer' );
1613 + $product_label = 'Visualizer Pro';
1614 + $discount = '30%';
1615 + }
1616 +
1617 + $product_label = sprintf( '<strong>%s</strong>', $product_label );
1618 + $url_params = array(
1619 + 'utm_term' => $is_pro ? 'plan-' . $plan : 'free',
1620 + 'lkey' => ! empty( $license ) ? $license : false,
1621 + );
1622 +
1623 + $config['message'] = sprintf( $message_template, '<strong>', $discount, '</strong>', $product_label );
1624 + $config['sale_url'] = add_query_arg(
1625 + $url_params,
1626 + tsdk_translate_link( tsdk_utmify( 'https://themeisle.link/vizualizer-bf', 'bfcm', 'visualizer' ) )
1627 + );
1628 +
1629 + $configs[ VISUALIZER_DIRNAME ] = $config;
1630 +
1631 + return $configs;
1194 1632 }
1195 1633 }