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 +995 -119 3.1.14.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,26 +70,139 @@
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 - $this->_addAction( 'admin_enqueue_scripts', 'enqueueLibraryScripts', null, 8 );
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 );
83 + $this->_addFilter( 'plugin_row_meta', 'getPluginMetaLinks', 10, 2 );
61 84 $this->_addFilter( 'visualizer_logger_data', 'getLoggerData' );
62 - $this->_addFilter( 'visualizer_get_chart_counts', 'getChartCountsByTypeAndMeta' );
63 85 $this->_addFilter( 'visualizer_feedback_review_trigger', 'feedbackReviewTrigger' );
86 + $this->_addFilter( 'themeisle_sdk_blackfriday_data', 'add_black_friday_data' );
64 87
88 + // screen pagination
89 + $this->_addFilter( 'set-screen-option', 'setScreenOptions', 10, 3 );
90 +
65 91 // revision support.
66 92 $this->_addFilter( 'wp_revisions_to_keep', 'limitRevisions', null, 10, 2 );
67 93 $this->_addAction( '_wp_put_post_revision', 'addRevision', null, 10, 1 );
68 94 $this->_addAction( 'wp_restore_post_revision', 'restoreRevision', null, 10, 2 );
69 95
96 + $this->_addAction( 'visualizer_chart_schedules_spl', 'addSplChartSchedules', null, 10, 3 );
97 +
70 98 $this->_addAction( 'admin_init', 'init' );
99 +
100 + $this->_addAction( 'visualizer_chart_languages', 'addMultilingualSupport' );
101 +
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' ) ) {
109 + $this->load_cypress_hooks();
110 + }
71 111 }
112 + /**
113 + * Display review notice.
114 + */
115 + public function render_review_notice( $footer_text ) {
116 + $current_screen = get_current_screen();
72 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;
136 + }
137 +
138 +
73 139 /**
140 + * Define the hooks that are needed for cypress.
141 + *
142 + * @since 3.4.0
143 + * @access private
144 + */
145 + private function load_cypress_hooks() {
146 + // all charts should load on the same page without pagination.
147 + add_filter(
148 + 'visualizer_query_args', function ( $args ) {
149 + $args['posts_per_page'] = 20;
150 + return $args;
151 + }, 10, 1
152 + );
153 + }
154 +
155 + /**
156 + * Add disabled `optgroup` schedules to the drop downs.
157 + *
158 + * @since ?
159 + *
160 + * @access public
161 + *
162 + * @param string $feature The feature for which to add schedules.
163 + * @param int $chart_id The chart ID.
164 + * @param int $plan The plan number.
165 + */
166 + public function addSplChartSchedules( $feature, $chart_id, $plan ) {
167 + if ( apply_filters( 'visualizer_is_business', false ) ) {
168 + return;
169 + }
170 +
171 + $is_new_personal = apply_filters( 'visualizer_is_new_personal', false );
172 +
173 + $hours = array(
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' ),
179 + );
180 +
181 + switch ( $feature ) {
182 + case 'json':
183 + case 'csv':
184 + // no more schedules if pro is already active.
185 + if ( Visualizer_Module::is_pro() && ! $is_new_personal ) {
186 + return;
187 + }
188 + break;
189 + case 'wp':
190 + // fall-through.
191 + case 'db':
192 + break;
193 + default:
194 + return;
195 + }
196 +
197 + echo '<optgroup disabled label="' . __( 'Upgrade required', 'visualizer' ) . '">';
198 + foreach ( $hours as $hour => $desc ) {
199 + echo '<option disabled>' . $desc . '</option>';
200 + }
201 + echo '</optgroup>';
202 + }
203 +
204 + /**
74 205 * No limits on revisions.
75 206 */
76 207 public function limitRevisions( $num, $post ) {
77 208 if ( Visualizer_Plugin::CPT_VISUALIZER === $post->post_type ) {
@@ -132,16 +263,30 @@
132 263 *
133 264 * @access public
134 265 */
135 266 public function init() {
136 - 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' ) ) {
137 268 $this->_addFilter( 'mce_external_languages', 'add_tinymce_lang', 10, 1 );
138 269 $this->_addFilter( 'mce_external_plugins', 'tinymce_plugin', 10, 1 );
139 270 $this->_addFilter( 'mce_buttons', 'register_mce_button', 10, 1 );
271 + $this->_addFilter( 'tiny_mce_before_init', 'get_strings_for_block', 10, 1 );
140 272 }
141 273 }
142 274
143 275 /**
276 + * Add the strings required for the TinyMCE buttons for the classic block (not the classic editor).
277 + *
278 + * @since ?
279 + * @access friendly
280 + */
281 + public function get_strings_for_block( $settings ) {
282 + $class = new Visualizer_Module_Language();
283 + $strings = $class->get_strings();
284 + $array = array( 'visualizer_tinymce_plugin' => json_encode( $strings ) );
285 + return array_merge( $settings, $array );
286 + }
287 +
288 + /**
144 289 * Load plugin translation for - TinyMCE API
145 290 *
146 291 * @access public
147 292 * @param array $arr The tinymce_lang array.
@@ -210,14 +355,41 @@
210 355 * @access public
211 356 */
212 357 public function enqueueMediaScripts() {
213 358 global $typenow;
214 - 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' ) {
215 362 wp_enqueue_style( 'visualizer-media', VISUALIZER_ABSURL . 'css/media.css', array( 'media-views' ), Visualizer_Plugin::VERSION );
216 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 +
217 387 // Load all the assets for the different libraries we support.
218 - $deps = Visualizer_Render_Sidebar_Google::enqueue_assets( array( 'media-editor' ) );
219 - $deps += Visualizer_Render_Sidebar_Type_DataTable::enqueue_assets( array( 'media-editor' ) );
388 + $deps = array(
389 + Visualizer_Render_Sidebar_Google::enqueue_assets( array( 'media-editor' ) ),
390 + Visualizer_Render_Sidebar_Type_DataTable_Tabular::enqueue_assets( array( 'media-editor' ) ),
391 + );
220 392
221 393 wp_enqueue_script( 'visualizer-media-model', VISUALIZER_ABSURL . 'js/media/model.js', $deps, Visualizer_Plugin::VERSION, true );
222 394 wp_enqueue_script( 'visualizer-media-collection', VISUALIZER_ABSURL . 'js/media/collection.js', array( 'visualizer-media-model' ), Visualizer_Plugin::VERSION, true );
223 395 wp_enqueue_script( 'visualizer-media-controller', VISUALIZER_ABSURL . 'js/media/controller.js', array( 'visualizer-media-collection' ), Visualizer_Plugin::VERSION, true );
@@ -226,9 +398,9 @@
226 398 'visualizer-media-view',
227 399 'visualizer',
228 400 array(
229 401 'i10n' => array(
230 - 'insert' => __( 'Insert', 'visualizer' ),
402 + 'insert' => __( 'Insert Chart', 'visualizer' ),
231 403 ),
232 404 )
233 405 );
234 406 wp_enqueue_script( 'visualizer-media-toolbar', VISUALIZER_ABSURL . 'js/media/toolbar.js', array( 'visualizer-media-view' ), Visualizer_Plugin::VERSION, true );
@@ -257,9 +429,9 @@
257 429 'controller' => array(
258 430 'title' => esc_html__( 'Visualizations', 'visualizer' ),
259 431 ),
260 432 'routers' => array(
261 - 'library' => esc_html__( 'From Library', 'visualizer' ),
433 + 'library' => esc_html__( 'Insert from Library', 'visualizer' ),
262 434 'create' => esc_html__( 'Create New', 'visualizer' ),
263 435 ),
264 436 'library' => array(
265 437 'filters' => $chart_types,
@@ -265,9 +437,9 @@
265 437 'filters' => $chart_types,
266 438 'types' => array_keys( $chart_types ),
267 439 ),
268 440 'nonce' => wp_create_nonce(),
269 - 'buildurl' => add_query_arg( 'action', Visualizer_Plugin::ACTION_CREATE_CHART, admin_url( 'admin-ajax.php' ) ),
441 + 'buildurl' => esc_url( add_query_arg( 'action', Visualizer_Plugin::ACTION_CREATE_CHART, admin_url( 'admin-ajax.php' ) ) ),
270 442 );
271 443
272 444 return $strings;
273 445 }
@@ -289,64 +461,102 @@
289 461 'enabled' => true,
290 462 );
291 463 }
292 464
465 + $enabled = self::proFeaturesEnabled();
466 +
293 467 $types = array_merge(
294 468 $additional,
295 469 array(
296 - 'dataTable' => array(
297 - 'name' => esc_html__( 'Table (New)', 'visualizer' ),
470 + 'tabular' => array(
471 + 'name' => esc_html__( 'Table', 'visualizer' ),
298 472 'enabled' => true,
473 + 'supports' => $enabled ? array( 'Google Charts', 'DataTable' ) : array( 'DataTable' ),
299 474 ),
300 475 'pie' => array(
301 - 'name' => esc_html__( 'Pie', 'visualizer' ),
476 + 'name' => esc_html__( 'Pie/Donut', 'visualizer' ),
302 477 'enabled' => true,
478 + 'supports' => array( 'Google Charts', 'ChartJS' ),
303 479 ),
304 480 'line' => array(
305 481 'name' => esc_html__( 'Line', 'visualizer' ),
306 482 'enabled' => true,
483 + 'supports' => array( 'Google Charts', 'ChartJS' ),
307 484 ),
485 + 'bar' => array(
486 + 'name' => esc_html__( 'Bar', 'visualizer' ),
487 + 'enabled' => true,
488 + 'supports' => $enabled ? array( 'Google Charts', 'ChartJS' ) : array( 'Google Charts' ),
489 + ),
308 490 'area' => array(
309 491 'name' => esc_html__( 'Area', 'visualizer' ),
310 - 'enabled' => true,
492 + 'enabled' => $enabled,
493 + // in ChartJS, the fill option is used to make Line chart an area: https://www.chartjs.org/docs/latest/charts/area.html
494 + 'supports' => array( 'Google Charts' ),
495 + 'demo_url' => 'https://demo.themeisle.com/visualizer/area-chart/',
311 496 ),
312 497 'geo' => array(
313 498 'name' => esc_html__( 'Geo', 'visualizer' ),
314 - 'enabled' => true,
499 + 'enabled' => $enabled,
500 + 'supports' => array( 'Google Charts' ),
501 + 'demo_url' => 'https://demo.themeisle.com/visualizer/geo-chart/',
315 502 ),
316 - 'bar' => array(
317 - 'name' => esc_html__( 'Bar', 'visualizer' ),
318 - 'enabled' => true,
319 - ),
320 503 'column' => array(
321 504 'name' => esc_html__( 'Column', 'visualizer' ),
322 - 'enabled' => true,
505 + 'enabled' => $enabled,
506 + 'supports' => array( 'Google Charts', 'ChartJS' ),
507 + 'demo_url' => 'https://demo.themeisle.com/visualizer/column-chart/',
323 508 ),
509 + 'bubble' => array(
510 + 'name' => esc_html__( 'Bubble', 'visualizer' ),
511 + 'enabled' => $enabled,
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.
513 + 'supports' => array( 'Google Charts' ),
514 + 'demo_url' => 'https://demo.themeisle.com/visualizer/bubble-chart/',
515 + ),
324 516 'scatter' => array(
325 517 'name' => esc_html__( 'Scatter', 'visualizer' ),
326 - 'enabled' => true,
518 + 'enabled' => $enabled,
519 + 'supports' => array( 'Google Charts' ),
520 + 'demo_url' => 'https://demo.themeisle.com/visualizer/scatter-chart/',
327 521 ),
328 522 'gauge' => array(
329 523 'name' => esc_html__( 'Gauge', 'visualizer' ),
330 - 'enabled' => true,
524 + 'enabled' => $enabled,
525 + 'supports' => array( 'Google Charts' ),
526 + 'demo_url' => 'https://demo.themeisle.com/visualizer/gauge-chart/',
331 527 ),
332 528 'candlestick' => array(
333 529 'name' => esc_html__( 'Candlestick', 'visualizer' ),
334 - 'enabled' => true,
530 + 'enabled' => $enabled,
531 + 'supports' => array( 'Google Charts' ),
532 + 'demo_url' => 'https://demo.themeisle.com/visualizer/candlestick-chart/',
335 533 ),
336 534 // pro types
337 - 'table' => array(
338 - 'name' => esc_html__( 'Table (Deprecated)', 'visualizer' ),
339 - 'enabled' => false,
340 - ),
341 535 'timeline' => array(
342 536 'name' => esc_html__( 'Timeline', 'visualizer' ),
343 537 'enabled' => false,
538 + 'supports' => array( 'Google Charts', 'ChartJS' ),
539 + 'demo_url' => 'https://demo.themeisle.com/visualizer/timeline-chart/',
344 540 ),
345 541 'combo' => array(
346 542 'name' => esc_html__( 'Combo', 'visualizer' ),
347 543 'enabled' => false,
544 + 'supports' => array( 'Google Charts' ),
545 + 'demo_url' => 'https://demo.themeisle.com/visualizer/combo-chart/',
348 546 ),
547 + 'polarArea' => array(
548 + 'name' => esc_html__( 'Polar Area', 'visualizer' ),
549 + 'enabled' => false,
550 + 'supports' => array( 'ChartJS' ),
551 + 'demo_url' => 'https://demo.themeisle.com/visualizer/polar-area-chart/',
552 + ),
553 + 'radar' => array(
554 + 'name' => esc_html__( 'Radar/Spider', 'visualizer' ),
555 + 'enabled' => false,
556 + 'supports' => array( 'ChartJS' ),
557 + 'demo_url' => 'https://demo.themeisle.com/visualizer/radar-spider-chart/',
558 + ),
349 559 )
350 560 );
351 561 $types = apply_filters( 'visualizer_pro_chart_types', $types );
352 562 if ( $enabledOnly ) {
@@ -355,8 +565,12 @@
355 565 if ( ! is_array( $array ) ) {
356 566 // support for old pro
357 567 $array = array( 'enabled' => true, 'name' => $array );
358 568 }
569 + // backward compatibility for PRO before v1.9.0
570 + if ( ! array_key_exists( 'supports', $array ) ) {
571 + $array['supports'] = array( 'Google Charts' );
572 + }
359 573 if ( ! $array['enabled'] ) {
360 574 continue;
361 575 }
362 576 $filtered[ $type ] = $array;
@@ -369,8 +583,12 @@
369 583 if ( ! is_array( $array ) ) {
370 584 // support for old pro
371 585 $array = array( 'enabled' => true, 'name' => $array );
372 586 }
587 + // backward compatibility for PRO before v1.9.0
588 + if ( ! array_key_exists( 'supports', $array ) ) {
589 + $array['supports'] = array( 'Google Charts' );
590 + }
373 591 $doubleD[ $type ] = $array['name'];
374 592 }
375 593 $types = $doubleD;
376 594 }
@@ -384,21 +602,13 @@
384 602 private static function handleDeprecatedCharts( $types, $enabledOnly, $get2Darray, $where ) {
385 603 $deprecated = array();
386 604
387 605 switch ( $where ) {
606 + case 'types':
607 + // fall-through
388 608 case 'library':
389 - // if the user has a Google Table chart, show it as deprecated otherwise remove the option from the library.
390 - if ( ! self::hasChartType( 'table' ) ) {
391 - $deprecated[] = 'table';
392 - if ( $get2Darray ) {
393 - $types['dataTable'] = esc_html__( 'Table', 'visualizer' );
394 - } else {
395 - $types['dataTable']['name'] = esc_html__( 'Table', 'visualizer' );
396 - }
397 - }
398 -
399 609 // if a user has a Gauge/Candlestick chart, then let them keep using it.
400 - if ( ! VISUALIZER_PRO ) {
610 + if ( ! Visualizer_Module::is_pro() ) {
401 611 if ( ! self::hasChartType( 'gauge' ) ) {
402 612 if ( $get2Darray ) {
403 613 $deprecated[] = 'gauge';
404 614 } else {
@@ -414,20 +624,10 @@
414 624 }
415 625 }
416 626 break;
417 627 default:
418 - // remove the option to create a Google Table chart.
419 - $deprecated[] = 'table';
420 -
421 - // rename the new table chart type.
422 - if ( $get2Darray ) {
423 - $types['dataTable'] = esc_html__( 'Table', 'visualizer' );
424 - } else {
425 - $types['dataTable']['name'] = esc_html__( 'Table', 'visualizer' );
426 - }
427 -
428 628 // if a user has a Gauge/Candlestick chart, then let them keep using it.
429 - if ( ! VISUALIZER_PRO ) {
629 + if ( ! Visualizer_Module::is_pro() ) {
430 630 if ( ! self::hasChartType( 'gauge' ) ) {
431 631 if ( $get2Darray ) {
432 632 $deprecated[] = 'gauge';
433 633 } else {
@@ -462,9 +662,9 @@
462 662 * @access public
463 663 */
464 664 public function renderTemplates() {
465 665 global $pagenow;
466 - if ( 'post.php' != $pagenow && 'post-new.php' != $pagenow ) {
666 + if ( 'post.php' !== $pagenow && 'post-new.php' !== $pagenow ) {
467 667 return;
468 668 }
469 669 $render = new Visualizer_Render_Templates();
470 670 $render->render();
@@ -482,44 +682,159 @@
482 682 *
483 683 * @param string $suffix The current page suffix.
484 684 */
485 685 public function enqueueLibraryScripts( $suffix ) {
486 - if ( $suffix == $this->_libraryPage ) {
487 - wp_enqueue_style( 'visualizer-library', VISUALIZER_ABSURL . 'css/library.css', array(), Visualizer_Plugin::VERSION );
488 - $this->_addFilter( 'media_upload_tabs', 'setupVisualizerTab' );
489 - 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 + }
490 715 wp_enqueue_script(
491 - 'visualizer-library',
492 - VISUALIZER_ABSURL . 'js/library.js',
493 - array(
494 - 'jquery',
495 - 'media-views',
496 - ),
716 + "visualizer-render-$library",
717 + VISUALIZER_ABSURL . 'js/render-facade.js',
718 + apply_filters( 'visualizer_assets_render', array( 'visualizer-library', 'visualizer-customization' ), true ),
497 719 Visualizer_Plugin::VERSION,
498 720 true
499 721 );
722 + if ( 'd3' === $library ) {
723 + $has_d3 = true;
724 + }
725 + }
500 726
501 - 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 + }
502 753
503 - $query = $this->getQuery();
504 - while ( $query->have_posts() ) {
505 - $chart = $query->next_post();
506 - $library = $this->load_chart_type( $chart->ID );
507 - if ( is_null( $library ) ) {
508 - continue;
509 - }
510 - wp_enqueue_script(
511 - "visualizer-render-$library",
512 - VISUALIZER_ABSURL . 'js/render-facade.js',
513 - apply_filters( 'visualizer_assets_render', array( 'visualizer-library', 'visualizer-customization' ), true ),
514 - 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,
515 769 true
516 770 );
517 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 + );
518 819 }
820 +
821 + do_action( 'themeisle_internal_page', VISUALIZER_DIRNAME, 'library' );
519 822 }
520 823
521 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 + /**
522 837 * Adds visualizer tab for media upload tabs array.
523 838 *
524 839 * @since 1.0.0
525 840 *
@@ -542,14 +857,187 @@
542 857 *
543 858 * @access public
544 859 */
545 860 public function registerAdminMenu() {
546 - $title = esc_html__( 'Visualizer Library', 'visualizer' );
547 - $callback = array( $this, 'renderLibraryPage' );
548 - $this->_libraryPage = add_submenu_page( 'upload.php', $title, $title, 'edit_posts', Visualizer_Plugin::NAME, $callback );
861 + $svg_base64_icon = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iNzdweCIgaGVpZ2h0PSI3N3B4IiB2aWV3Qm94PSIwIDAgNzcgNzciIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDUyLjYgKDY3NDkxKSAtIGh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaCAtLT4KICAgIDx0aXRsZT5Db21iaW5lZCBTaGFwZTwvdGl0bGU+CiAgICA8ZGVzYz5DcmVhdGVkIHdpdGggU2tldGNoLjwvZGVzYz4KICAgIDxnIGlkPSJQcm9kdWN0LVBhZ2UiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPgogICAgICAgIDxnIGlkPSJXb3JkUHJlc3MtcGx1Z2lucyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTE5MC4wMDAwMDAsIC00MzUuMDAwMDAwKSIgZmlsbD0iIzM5QzNEMiI+CiAgICAgICAgICAgIDxwYXRoIGQ9Ik0yMjguNSw1MTIgQzIwNy4yMzcwMzcsNTEyIDE5MCw0OTQuNzYyOTYzIDE5MCw0NzMuNSBDMTkwLDQ1Mi4yMzcwMzcgMjA3LjIzNzAzNyw0MzUgMjI4LjUsNDM1IEMyNDkuNzYyOTYzLDQzNSAyNjcsNDUyLjIzNzAzNyAyNjcsNDczLjUgQzI2Nyw0OTQuNzYyOTYzIDI0OS43NjI5NjMsNTEyIDIyOC41LDUxMiBaIE0yNDYuODQzNzUsNDgzLjU5MjA1OSBMMjE1LjYyNSw0ODMuNTkyMDU5IEwyMTUuNjI1LDQ2MC44MjY1NDQgQzIxNS42MjUsNDYwLjE2NDU0NyAyMTUuMTA3NTc4LDQ1OS42MjgzNTkgMjE0LjQ2ODc1LDQ1OS42MjgzNTkgTDIxMi4xNTYyNSw0NTkuNjI4MzU5IEMyMTEuNTE3NDIyLDQ1OS42MjgzNTkgMjExLDQ2MC4xNjQ1NDcgMjExLDQ2MC44MjY1NDQgTDIxMSw0ODUuOTg4NDI5IEMyMTEsNDg3LjMxMTY3NCAyMTIuMDM1NTY2LDQ4OC4zODQ3OTggMjEzLjMxMjUsNDg4LjM4NDc5OCBMMjQ2Ljg0Mzc1LDQ4OC4zODQ3OTggQzI0Ny40ODI1NzgsNDg4LjM4NDc5OCAyNDgsNDg3Ljg0ODYxMSAyNDgsNDg3LjE4NjYxMyBMMjQ4LDQ4NC43OTAyNDQgQzI0OCw0ODQuMTI4MjQ2IDI0Ny40ODI1NzgsNDgzLjU5MjA1OSAyNDYuODQzNzUsNDgzLjU5MjA1OSBaIE0yNDQuNTMxMjUsNDYyLjAyNDcyOSBMMjM1Ljk5OTU3LDQ2Mi4wMjQ3MjkgQzIzNC40NTQ1MzEsNDYyLjAyNDcyOSAyMzMuNjgwNTY2LDQ2My45NjA1NDcgMjM0Ljc3MzIyMyw0NjUuMDkyODMyIEwyMzcuMTE0NjI5LDQ2Ny41MTkxNTYgTDIzMS44MTI1LDQ3My4wMTQzMzIgTDIyNi41MTAzNzEsNDY3LjUxOTkwNSBDMjI1LjYwNzA1MSw0NjYuNTgzODIzIDIyNC4xNDI5NDksNDY2LjU4MzgyMyAyMjMuMjQwMzUyLDQ2Ny41MTk5MDUgTDIxOC4yNzY0MjYsNDcyLjY2Mzg2MyBDMjE3LjgyNDc2Niw0NzMuMTMxOTA0IDIxNy44MjQ3NjYsNDczLjg5MDUwNSAyMTguMjc2NDI2LDQ3NC4zNTg1NDYgTDIxOS45MTEwNzQsNDc2LjA1MjQ4IEMyMjAuMzYyNzM0LDQ3Ni41MjA1MjEgMjIxLjA5NDc4NSw0NzYuNTIwNTIxIDIyMS41NDY0NDUsNDc2LjA1MjQ4IEwyMjQuODc1LDQ3Mi42MDI0NTYgTDIzMC4xNzcxMjksNDc4LjA5Njg4MyBDMjMxLjA4MDQ0OSw0NzkuMDMyOTY1IDIzMi41NDQ1NTEsNDc5LjAzMjk2NSAyMzMuNDQ3MTQ4LDQ3OC4wOTY4ODMgTDI0MC4zODQ2NDgsNDcwLjkwNzc3MyBMMjQyLjcyNjA1NSw0NzMuMzM0MDk4IEMyNDMuODE4NzExLDQ3NC40NjYzODIgMjQ1LjY4Njc3Nyw0NzMuNjY0MzQ3IDI0NS42ODY3NzcsNDcyLjA2MzI3MyBMMjQ1LjY4Njc3Nyw0NjMuMjIyOTE0IEMyNDUuNjg3NSw0NjIuNTYwOTE3IDI0NS4xNzAwNzgsNDYyLjAyNDcyOSAyNDQuNTMxMjUsNDYyLjAyNDcyOSBaIiBpZD0iQ29tYmluZWQtU2hhcGUiPjwvcGF0aD4KICAgICAgICA8L2c+CiAgICA8L2c+Cjwvc3ZnPg==';
862 +
863 + $this->_libraryPage = add_menu_page( __( 'Visualizer', 'visualizer' ), __( 'Visualizer', 'visualizer' ), 'edit_posts', Visualizer_Plugin::NAME, array( $this, 'renderLibraryPage' ), $svg_base64_icon, 99.7666 );
864 +
865 + add_submenu_page(
866 + Visualizer_Plugin::NAME,
867 + __( 'Chart Library', 'visualizer' ),
868 + __( 'Chart Library', 'visualizer' ),
869 + 'edit_posts',
870 + 'admin.php?page=' . Visualizer_Plugin::NAME
871 + );
872 + add_submenu_page(
873 + Visualizer_Plugin::NAME,
874 + __( 'Add New Chart', 'visualizer' ),
875 + __( 'Add New Chart', 'visualizer' ),
876 + 'edit_posts',
877 + 'admin.php?page=' . Visualizer_Plugin::NAME . '&vaction=addnew'
878 + );
879 +
880 + $this->_settingsPage = add_submenu_page(
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,
891 + __( 'Support', 'visualizer' ),
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>',
893 + 'edit_posts',
894 + 'viz-support',
895 + array( $this, 'renderSupportPage' )
896 + );
897 +
898 + remove_submenu_page( Visualizer_Plugin::NAME, Visualizer_Plugin::NAME );
899 +
900 + add_action( "load-{$this->_libraryPage}", array( $this, 'addScreenOptions' ) );
901 + add_action( 'admin_footer', array( $this, 'handleGetProSubMenu' ) );
549 902 }
550 903
551 904 /**
905 + * Handle get pro plugin submenu.
906 + */
907 + public function handleGetProSubMenu() {
908 + if ( ! Visualizer_Module::is_pro() ) {
909 + ?>
910 + <style type="text/css">
911 + #toplevel_page_visualizer ul.wp-submenu li:last-child {
912 + background: #FF7E65;
913 + font-size: 14px;
914 + font-weight: 600;
915 + color: #fff;
916 + }
917 + #toplevel_page_visualizer ul.wp-submenu li:last-child > a > span {
918 + color: #fff !important;
919 + }
920 + #toplevel_page_visualizer ul.wp-submenu li:last-child > a:hover {
921 + box-shadow: inherit;
922 + }
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
933 + }
934 + }
935 + }
936 +
937 + /**
938 + * Adds the screen options for pagination.
939 + */
940 + public function addScreenOptions() {
941 + $screen = get_current_screen();
942 +
943 + // bail if it's some other page.
944 + if ( ! is_object( $screen ) || $screen->id !== $this->_libraryPage ) {
945 + return;
946 + }
947 +
948 + $args = array(
949 + 'label' => __( 'Number of charts per page:', 'visualizer' ),
950 + 'default' => 6,
951 + 'option' => 'visualizer_library_per_page',
952 + );
953 + add_screen_option( 'per_page', $args );
954 + }
955 +
956 + /**
957 + * Returns the screen option for pagination.
958 + */
959 + public function setScreenOptions( $status, $option, $value ) {
960 + if ( 'visualizer_library_per_page' === $option ) {
961 + return $value;
962 + }
963 + }
964 +
965 + /**
966 + * Adds the display filters to be used in the meta_query while displaying the charts.
967 + */
968 + private function getDisplayFilters( &$query_args ) {
969 + $query = array();
970 + global $sitepress;
971 + if ( Visualizer_Module::is_pro() && ( function_exists( 'icl_get_languages' ) && $sitepress instanceof \SitePress ) ) {
972 + $current_lang = icl_get_current_language();
973 + if ( in_array( $current_lang, array( 'all', icl_get_default_language() ), true ) ) {
974 + $query[] = array(
975 + 'key' => 'chart_lang',
976 + 'compare' => 'NOT EXISTS',
977 + );
978 + } else {
979 + $query[] = array(
980 + 'key' => 'chart_lang',
981 + 'value' => $current_lang,
982 + 'compare' => '=',
983 + );
984 + }
985 + }
986 +
987 + // add chart type filter to the query arguments
988 + $type = filter_input( INPUT_GET, 'type' );
989 + if ( $type && in_array( $type, Visualizer_Plugin::getChartTypes(), true ) ) {
990 + $query[] = array(
991 + 'key' => Visualizer_Plugin::CF_CHART_TYPE,
992 + 'value' => $type,
993 + 'compare' => '=',
994 + );
995 + }
996 +
997 + // add chart library filter to the query arguments
998 + $library = filter_input( INPUT_GET, 'library' );
999 + if ( $library ) {
1000 + $query[] = array(
1001 + 'key' => Visualizer_Plugin::CF_CHART_LIBRARY,
1002 + 'value' => $library,
1003 + 'compare' => '=',
1004 + );
1005 + }
1006 +
1007 + // add date filter to the query arguments
1008 + $date = filter_input( INPUT_GET, 'date' );
1009 + $possible = array_keys( Visualizer_Plugin::getSupportedDateFilter() );
1010 + if ( $date && in_array( $date, $possible, true ) ) {
1011 + $query_args['date_query'] = array(
1012 + 'after' => "$date -1 day",
1013 + 'inclusive' => true,
1014 + );
1015 + }
1016 +
1017 + // add source filter to the query arguments
1018 + $source = filter_input( INPUT_GET, 'source' );
1019 + if ( $source ) {
1020 + $source = ucwords( $source );
1021 + $source = "Visualizer_Source_{$source}";
1022 + $query[] = array(
1023 + 'key' => Visualizer_Plugin::CF_SOURCE,
1024 + 'value' => $source,
1025 + 'compare' => '=',
1026 + );
1027 + }
1028 +
1029 + $query_args['meta_query'] = $query;
1030 +
1031 + $orderby = filter_input( INPUT_GET, 'orderby' );
1032 + $order = filter_input( INPUT_GET, 'order' );
1033 + if ( $orderby ) {
1034 + $query_args['order_by'] = $orderby;
1035 + }
1036 + $query_args['order'] = empty( $order ) ? 'desc' : $order;
1037 + }
1038 +
1039 + /**
552 1040 * Get the instance of WP_Query that fetches the charts as per the given criteria.
553 1041 */
554 1042 private function getQuery() {
555 1043 static $q;
@@ -568,29 +1056,34 @@
568 1056 'default' => 1,
569 1057 ),
570 1058 )
571 1059 );
1060 +
1061 + $per_page = 6;
1062 + $screen = get_current_screen();
1063 + if ( $screen ) {
1064 + // retrieve the per_page option
1065 + $screen_option = $screen->get_option( 'per_page', 'option' );
1066 + // retrieve the value stored for the current user
1067 + $user = get_current_user_id();
1068 + $per_page = get_user_meta( $user, $screen_option, true );
1069 + if ( empty( $per_page ) || $per_page < 1 ) {
1070 + // nothing set, get the default value
1071 + $per_page = $screen->get_option( 'per_page', 'default' );
1072 + }
1073 + }
1074 +
572 1075 // the initial query arguments to fetch charts
573 1076 $query_args = array(
574 1077 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
575 - 'posts_per_page' => 6,
1078 + 'posts_per_page' => $per_page,
576 1079 'paged' => $page,
577 1080 );
578 - // add chart type filter to the query arguments
579 - $filter = filter_input( INPUT_GET, 'type' );
580 - if ( $filter && in_array( $filter, Visualizer_Plugin::getChartTypes() ) ) {
581 - $query_args['meta_query'] = array(
582 - array(
583 - 'key' => Visualizer_Plugin::CF_CHART_TYPE,
584 - 'value' => $filter,
585 - 'compare' => '=',
586 - ),
587 - );
588 - } else {
589 - $filter = 'all';
590 - }
1081 +
1082 + $this->getDisplayFilters( $query_args );
1083 +
591 1084 // Added by Ash/Upwork
592 - $filterByMeta = filter_input( INPUT_GET, 's', FILTER_SANITIZE_STRING );
1085 + $filterByMeta = ! empty( $_GET['s'] ) ? sanitize_text_field( wp_unslash( $_GET['s'] ) ) : null; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
593 1086 if ( $filterByMeta ) {
594 1087 $query = array(
595 1088 'key' => Visualizer_Plugin::CF_SETTINGS,
596 1089 'value' => $filterByMeta,
@@ -599,13 +1092,108 @@
599 1092 $meta = isset( $query_args['meta_query'] ) ? $query_args['meta_query'] : array();
600 1093 $meta[] = $query;
601 1094 $query_args['meta_query'] = $meta;
602 1095 }
603 - $q = new WP_Query( $query_args );
1096 + $q = new WP_Query( apply_filters( 'visualizer_query_args', $query_args ) );
604 1097 return $q;
605 1098 }
606 1099
607 1100 /**
1101 + * Renders support page.
1102 + *
1103 + * @since 3.3.0
1104 + *
1105 + * @access public
1106 + */
1107 + public function renderSupportPage() {
1108 + wp_enqueue_style( 'visualizer-upsell', VISUALIZER_ABSURL . 'css/upsell.css', array(), Visualizer_Plugin::VERSION );
1109 + include_once VISUALIZER_ABSPATH . '/templates/support.php';
1110 + }
1111 +
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 + /**
608 1196 * Renders visualizer library page.
609 1197 *
610 1198 * @since 1.0.0
611 1199 *
@@ -628,9 +1216,9 @@
628 1216 )
629 1217 );
630 1218 // add chart type filter to the query arguments
631 1219 $filter = filter_input( INPUT_GET, 'type' );
632 - if ( ! ( $filter && in_array( $filter, Visualizer_Plugin::getChartTypes() ) ) ) {
1220 + if ( ! ( $filter && in_array( $filter, Visualizer_Plugin::getChartTypes(), true ) ) ) {
633 1221 $filter = 'all';
634 1222 }
635 1223
636 1224 $css = '';
@@ -651,12 +1239,27 @@
651 1239 if ( ! empty( $atts['settings'] ) ) {
652 1240 $settings = apply_filters( $atts['settings'], $settings, $chart->ID, $type );
653 1241 }
654 1242
655 - unset( $settings['height'], $settings['width'] );
1243 + if ( $settings ) {
1244 + unset( $settings['height'], $settings['width'], $settings['chartArea'] );
1245 + }
1246 +
656 1247 $series = apply_filters( Visualizer_Plugin::FILTER_GET_CHART_SERIES, get_post_meta( $chart->ID, Visualizer_Plugin::CF_SERIES, true ), $chart->ID, $type );
657 - $data = apply_filters( Visualizer_Plugin::FILTER_GET_CHART_DATA, unserialize( html_entity_decode( $chart->post_content ) ), $chart->ID, $type );
1248 + $data = self::get_chart_data( $chart, $type );
658 1249
1250 + // Set default setting series when add manual chart data - Only during first creation.
1251 + if ( isset( $settings['series'] ) && ! empty( $series ) ) {
1252 + $count_series = count( $series ) - count( $settings['series'] );
1253 + if ( $count_series > 1 ) {
1254 + $clone_last_series = end( $settings['series'] );
1255 + $clone_last_series = array_fill_keys( array_keys( $clone_last_series ), '' );
1256 + foreach ( range( 1, $count_series ) as $item ) {
1257 + $settings['series'][] = $clone_last_series;
1258 + }
1259 + }
1260 + }
1261 +
659 1262 $library = $this->load_chart_type( $chart->ID );
660 1263
661 1264 $id = 'visualizer-' . $chart->ID;
662 1265 $arguments = $this->get_inline_custom_css( $id, $settings );
@@ -664,11 +1267,19 @@
664 1267 $css .= $arguments[0];
665 1268 $settings = $arguments[1];
666 1269 }
667 1270
1271 + if ( isset( $settings['controls']['ui']['labelStacking'] ) ) {
1272 + unset( $settings['controls']['ui']['labelStacking'] );
1273 + }
1274 + if ( isset( $settings['controls']['ui']['orientation'] ) ) {
1275 + unset( $settings['controls']['ui']['orientation'] );
1276 + }
1277 +
668 1278 // add chart to the array
669 1279 $charts[ $id ] = array(
670 1280 'id' => $chart->ID,
1281 + 'title' => $chart->post_title,
671 1282 'type' => $type,
672 1283 'series' => $series,
673 1284 'settings' => $settings,
674 1285 'data' => $data,
@@ -673,11 +1284,15 @@
673 1284 'settings' => $settings,
674 1285 'data' => $data,
675 1286 'library' => $library,
676 1287 );
1288 + if ( 'd3' === $library ) {
1289 + $charts[ $id ]['code'] = get_post_meta( $chart->ID, Visualizer_Module_AIBuilder::CF_D3_CODE, true );
1290 + }
677 1291 }
678 1292 // enqueue charts array
679 1293 $ajaxurl = admin_url( 'admin-ajax.php' );
1294 +
680 1295 wp_localize_script(
681 1296 'visualizer-library',
682 1297 'visualizer',
683 1298 array(
@@ -684,26 +1299,39 @@
684 1299 'language' => $this->get_language(),
685 1300 'map_api_key' => get_option( 'visualizer-map-api-key' ),
686 1301 'charts' => $charts,
687 1302 'urls' => array(
688 - 'base' => add_query_arg( 'vpage', false ),
689 - 'create' => add_query_arg(
690 - array(
691 - 'action' => Visualizer_Plugin::ACTION_CREATE_CHART,
692 - 'library' => 'yes',
693 - 'type' => isset( $_GET['type'] ) ? $_GET['type'] : '',
694 - ),
695 - $ajaxurl
1303 + 'base' => esc_url( add_query_arg( array( 'vpage' => false, 'vaction' => false ) ) ),
1304 + 'create' => esc_url(
1305 + add_query_arg(
1306 + array(
1307 + 'action' => Visualizer_Plugin::ACTION_CREATE_CHART,
1308 + 'library' => 'yes',
1309 + 'type' => isset( $_GET['type'] ) ? $_GET['type'] : '',
1310 + 'chart-library' => isset( $_GET['chart-library'] ) ? $_GET['chart-library'] : '',
1311 + 'vaction' => false,
1312 + ),
1313 + $ajaxurl
1314 + )
696 1315 ),
697 - 'edit' => add_query_arg(
698 - array(
699 - 'action' => Visualizer_Plugin::ACTION_EDIT_CHART,
700 - 'library' => 'yes',
701 - ),
702 - $ajaxurl
1316 + 'edit' => esc_url(
1317 + add_query_arg(
1318 + array(
1319 + 'action' => Visualizer_Plugin::ACTION_EDIT_CHART,
1320 + 'library' => 'yes',
1321 + 'vaction' => false,
1322 + ),
1323 + $ajaxurl
1324 + )
703 1325 ),
704 1326 ),
705 1327 'page_type' => 'library',
1328 + 'is_front' => false,
1329 + 'i10n' => array(
1330 + 'copied' => __( 'The shortcode has been copied to your clipboard. Hit Ctrl-V/Cmd-V to paste it.', 'visualizer' ),
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' ),
1332 + ),
1333 + 'is_pro_user' => Visualizer_Module::is_pro(),
706 1334 )
707 1335 );
708 1336 // render library page
709 1337 $render = new Visualizer_Render_Library();
@@ -712,9 +1340,9 @@
712 1340 $render->types = self::_getChartTypesLocalized( false, false, false, true );
713 1341 $render->custom_css = $css;
714 1342 $render->pagination = paginate_links(
715 1343 array(
716 - 'base' => add_query_arg( 'vpage', '%#%' ),
1344 + 'base' => add_query_arg( array( 'vpage' => '%#%', 'vaction' => false ) ),
717 1345 'format' => '',
718 1346 'current' => $page,
719 1347 'total' => $query->max_num_pages,
720 1348 'type' => 'array',
@@ -719,8 +1347,9 @@
719 1347 'total' => $query->max_num_pages,
720 1348 'type' => 'array',
721 1349 )
722 1350 );
1351 +
723 1352 $render->render();
724 1353 }
725 1354
726 1355 /**
@@ -735,14 +1364,14 @@
735 1364 *
736 1365 * @return array Updated array of action links.
737 1366 */
738 1367 public function getPluginActionLinks( $links, $file ) {
739 - if ( $file == plugin_basename( VISUALIZER_BASEFILE ) ) {
1368 + if ( $file === plugin_basename( VISUALIZER_BASEFILE ) ) {
740 1369 array_unshift(
741 1370 $links,
742 1371 sprintf(
743 1372 '<a href="%s">%s</a>',
744 - admin_url( 'upload.php?page=' . Visualizer_Plugin::NAME ),
1373 + admin_url( 'admin.php?page=' . Visualizer_Plugin::NAME ),
745 1374 esc_html__( 'Library', 'visualizer' )
746 1375 )
747 1376 );
748 1377 }
@@ -762,21 +1391,268 @@
762 1391 *
763 1392 * @return array Updated array of plugin meta links.
764 1393 */
765 1394 public function getPluginMetaLinks( $plugin_meta, $plugin_file ) {
766 - if ( $plugin_file == plugin_basename( VISUALIZER_BASEFILE ) ) {
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 +
1409 + if ( $plugin_file === plugin_basename( VISUALIZER_BASEFILE ) ) {
1410 + $is_black_friday = apply_filters( 'themeisle_sdk_is_black_friday_sale', false );
767 1411 // knowledge base link
768 1412 $plugin_meta[] = sprintf(
769 - '<a href="https://github.com/codeinwp/visualizer/wiki" target="_blank">%s</a>',
770 - esc_html__( 'Knowledge Base', 'visualizer' )
1413 + '<a href="' . VISUALIZER_MAIN_DOC . '" target="_blank">%s</a>',
1414 + esc_html__( 'Docs', 'visualizer' )
771 1415 );
772 - // flattr link
773 - $plugin_meta[] = sprintf(
774 - '<a style="color:red" href="https://themeisle.com/plugins/visualizer-charts-and-graphs-pro-addon/" target="_blank">%s</a>',
775 - esc_html__( 'Pro Addon', 'visualizer' )
776 - );
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 + }
777 1423 }
778 1424
779 1425 return $plugin_meta;
780 1426 }
781 1427
1428 + /**
1429 + * Returns true when premium chart types should be enabled for the current user.
1430 + *
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
1440 + */
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 ) {
1446 + return true;
1447 + }
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;
1460 + }
1461 +
1462 + /**
1463 + * Multilingual Support.
1464 + *
1465 + * @param int $chart_id Chart ID.
1466 + * @return bool Default false
1467 + */
1468 + public function addMultilingualSupport( $chart_id ) {
1469 + global $sitepress;
1470 + if ( Visualizer_Module::is_pro() ) {
1471 + return;
1472 + }
1473 + if ( function_exists( 'icl_get_languages' ) && $sitepress instanceof \SitePress ) {
1474 + $language = icl_get_languages();
1475 + $current_lang = icl_get_current_language();
1476 + $default_lang = icl_get_default_language();
1477 + $post_info = wpml_get_language_information( null, $chart_id );
1478 + $translations = array();
1479 + if ( ! empty( $post_info ) && ( $default_lang === $post_info['language_code'] ) ) {
1480 + $trid = $sitepress->get_element_trid( $chart_id, 'post_' . Visualizer_Plugin::CPT_VISUALIZER );
1481 + $translations = $sitepress->get_element_translations( $trid );
1482 + }
1483 + if ( empty( $translations ) ) {
1484 + return;
1485 + }
1486 + ?>
1487 + <hr><div class="visualizer-languages-list only-pro">
1488 + <?php
1489 + foreach ( $language as $lang ) {
1490 + $lang_code = $lang['code'];
1491 + if ( $current_lang !== $lang_code ) {
1492 + ?>
1493 + <a href="javascript:;">
1494 + <img src="<?php echo esc_url( $lang['country_flag_url'] ); ?>" alt="<?php echo esc_attr( $lang['translated_name'] ); ?>">
1495 + </a>
1496 + <?php
1497 + }
1498 + }
1499 + ?>
1500 + <a href="<?php echo tsdk_utmify( Visualizer_Plugin::PRO_TEASER_URL, 'wpml-support', 'visualizer_render_char' ); ?>"target="_blank"><?php esc_html_e( 'Upgrade to PRO to active this translation for charts', 'visualizer' ); ?></a>
1501 + </div>
1502 + <?php
1503 + }
1504 + }
1505 +
1506 + /**
1507 + * Check chart is enabled or not.
1508 + *
1509 + * @param string $type Chart ID.
1510 + *
1511 + * @return bool Default false
1512 + */
1513 + public static function checkChartStatus( $type ) {
1514 + $types = self::_getChartTypesLocalized( false, false, false, true );
1515 + if ( isset( $types[ $type ] ) ) {
1516 + return ! empty( $types[ $type ]['enabled'] );
1517 + }
1518 + return false;
1519 + }
1520 +
1521 + /**
1522 + * Get the survey metadata.
1523 + *
1524 + * @param array $data The data for survey in Formbricks format.
1525 + * @param string $page_slug The slug of the loaded page.
1526 + *
1527 + * @return array The survey metadata.
1528 + */
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 );
1532 +
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 );
1536 +
1537 + $plugin_data = get_plugin_data( VISUALIZER_BASEFILE, false, false );
1538 + $plugin_version = '';
1539 +
1540 + if ( ! empty( $plugin_data['Version'] ) ) {
1541 + $plugin_version = $plugin_data['Version'];
1542 + }
1543 +
1544 + $count_charts_cache_key = 'visualizer_count_charts';
1545 + $charts_number = get_transient( $count_charts_cache_key );
1546 +
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 );
1552 + }
1553 +
1554 + $data = array(
1555 + 'environmentId' => 'cltef8cut1s7wyyfxy3rlxzs5',
1556 + 'attributes' => array(
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,
1562 + ),
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;
1574 + }
1575 +
1576 + /**
1577 + * Count the charts.
1578 + *
1579 + * @param int $limit The count limit (optional).
1580 + *
1581 + * @return int The number of charts.
1582 + */
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 + );
1590 +
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%' );
1634 + }
1635 +
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 + );
1641 +
1642 + if ( ( $is_pro || $is_expired ) && ! empty( $pro_product_slug ) ) {
1643 + $config['plugin_meta_targets'] = array( $pro_product_slug );
1644 + }
1645 +
1646 + $config['message'] = $message;
1647 + $config['cta_label'] = $cta_label;
1648 +
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;
1657 + }
782 1658 }