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 +1111 -101 3.0.94.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,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' );
56 - $this->_addAction( 'admin_footer', 'renderTempaltes' );
57 - $this->_addAction( 'admin_enqueue_scripts', 'enqueueLibraryScripts', null, 8 );
74 + $this->_addAction( 'enqueue_block_editor_assets', 'enqueueMediaScripts' );
75 + $this->_addAction( 'admin_footer', 'renderTemplates' );
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,20 +355,52 @@
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 - wp_enqueue_script( 'visualizer-google-jsapi-new', '//www.gstatic.com/charts/loader.js', array( 'media-editor' ), null, true );
217 - wp_enqueue_script( 'visualizer-google-jsapi-old', '//www.google.com/jsapi', array( 'visualizer-google-jsapi-new' ), null, true );
218 - wp_enqueue_script( 'visualizer-media-model', VISUALIZER_ABSURL . 'js/media/model.js', array( 'visualizer-google-jsapi-old' ), Visualizer_Plugin::VERSION, true );
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 +
387 + // Load all the assets for the different libraries we support.
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 + );
392 +
393 + wp_enqueue_script( 'visualizer-media-model', VISUALIZER_ABSURL . 'js/media/model.js', $deps, Visualizer_Plugin::VERSION, true );
219 394 wp_enqueue_script( 'visualizer-media-collection', VISUALIZER_ABSURL . 'js/media/collection.js', array( 'visualizer-media-model' ), Visualizer_Plugin::VERSION, true );
220 395 wp_enqueue_script( 'visualizer-media-controller', VISUALIZER_ABSURL . 'js/media/controller.js', array( 'visualizer-media-collection' ), Visualizer_Plugin::VERSION, true );
221 396 wp_enqueue_script( 'visualizer-media-view', VISUALIZER_ABSURL . 'js/media/view.js', array( 'visualizer-media-controller' ), Visualizer_Plugin::VERSION, true );
222 397 wp_localize_script(
223 - 'visualizer-media-view', 'visualizer', array(
398 + 'visualizer-media-view',
399 + 'visualizer',
400 + array(
224 401 'i10n' => array(
225 - 'insert' => __( 'Insert', 'visualizer' ),
402 + 'insert' => __( 'Insert Chart', 'visualizer' ),
226 403 ),
227 404 )
228 405 );
229 406 wp_enqueue_script( 'visualizer-media-toolbar', VISUALIZER_ABSURL . 'js/media/toolbar.js', array( 'visualizer-media-view' ), Visualizer_Plugin::VERSION, true );
@@ -252,9 +429,9 @@
252 429 'controller' => array(
253 430 'title' => esc_html__( 'Visualizations', 'visualizer' ),
254 431 ),
255 432 'routers' => array(
256 - 'library' => esc_html__( 'From Library', 'visualizer' ),
433 + 'library' => esc_html__( 'Insert from Library', 'visualizer' ),
257 434 'create' => esc_html__( 'Create New', 'visualizer' ),
258 435 ),
259 436 'library' => array(
260 437 'filters' => $chart_types,
@@ -260,9 +437,9 @@
260 437 'filters' => $chart_types,
261 438 'types' => array_keys( $chart_types ),
262 439 ),
263 440 'nonce' => wp_create_nonce(),
264 - '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' ) ) ),
265 442 );
266 443
267 444 return $strings;
268 445 }
@@ -275,9 +452,9 @@
275 452 * @static
276 453 * @access private
277 454 * @return array The associated array of chart types with localized names.
278 455 */
279 - public static function _getChartTypesLocalized( $enabledOnly = false, $get2Darray = false, $add_select = false ) {
456 + public static function _getChartTypesLocalized( $enabledOnly = false, $get2Darray = false, $add_select = false, $where = null ) {
280 457 $additional = array();
281 458 if ( $add_select ) {
282 459 $additional['select'] = array(
283 460 'name' => esc_html__( 'All', 'visualizer' ),
@@ -284,59 +461,102 @@
284 461 'enabled' => true,
285 462 );
286 463 }
287 464
465 + $enabled = self::proFeaturesEnabled();
466 +
288 467 $types = array_merge(
289 - $additional, array(
468 + $additional,
469 + array(
470 + 'tabular' => array(
471 + 'name' => esc_html__( 'Table', 'visualizer' ),
472 + 'enabled' => true,
473 + 'supports' => $enabled ? array( 'Google Charts', 'DataTable' ) : array( 'DataTable' ),
474 + ),
290 475 'pie' => array(
291 - 'name' => esc_html__( 'Pie', 'visualizer' ),
476 + 'name' => esc_html__( 'Pie/Donut', 'visualizer' ),
292 477 'enabled' => true,
478 + 'supports' => array( 'Google Charts', 'ChartJS' ),
293 479 ),
294 480 'line' => array(
295 481 'name' => esc_html__( 'Line', 'visualizer' ),
296 482 'enabled' => true,
483 + 'supports' => array( 'Google Charts', 'ChartJS' ),
297 484 ),
485 + 'bar' => array(
486 + 'name' => esc_html__( 'Bar', 'visualizer' ),
487 + 'enabled' => true,
488 + 'supports' => $enabled ? array( 'Google Charts', 'ChartJS' ) : array( 'Google Charts' ),
489 + ),
298 490 'area' => array(
299 491 'name' => esc_html__( 'Area', 'visualizer' ),
300 - '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/',
301 496 ),
302 497 'geo' => array(
303 498 'name' => esc_html__( 'Geo', 'visualizer' ),
304 - 'enabled' => true,
499 + 'enabled' => $enabled,
500 + 'supports' => array( 'Google Charts' ),
501 + 'demo_url' => 'https://demo.themeisle.com/visualizer/geo-chart/',
305 502 ),
306 - 'bar' => array(
307 - 'name' => esc_html__( 'Bar', 'visualizer' ),
308 - 'enabled' => true,
309 - ),
310 503 'column' => array(
311 504 'name' => esc_html__( 'Column', 'visualizer' ),
312 - 'enabled' => true,
505 + 'enabled' => $enabled,
506 + 'supports' => array( 'Google Charts', 'ChartJS' ),
507 + 'demo_url' => 'https://demo.themeisle.com/visualizer/column-chart/',
313 508 ),
314 - 'gauge' => array(
315 - 'name' => esc_html__( 'Gauge', 'visualizer' ),
316 - 'enabled' => true,
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/',
317 515 ),
318 516 'scatter' => array(
319 517 'name' => esc_html__( 'Scatter', 'visualizer' ),
320 - 'enabled' => true,
518 + 'enabled' => $enabled,
519 + 'supports' => array( 'Google Charts' ),
520 + 'demo_url' => 'https://demo.themeisle.com/visualizer/scatter-chart/',
321 521 ),
522 + 'gauge' => array(
523 + 'name' => esc_html__( 'Gauge', 'visualizer' ),
524 + 'enabled' => $enabled,
525 + 'supports' => array( 'Google Charts' ),
526 + 'demo_url' => 'https://demo.themeisle.com/visualizer/gauge-chart/',
527 + ),
322 528 'candlestick' => array(
323 529 'name' => esc_html__( 'Candlestick', 'visualizer' ),
324 - 'enabled' => true,
530 + 'enabled' => $enabled,
531 + 'supports' => array( 'Google Charts' ),
532 + 'demo_url' => 'https://demo.themeisle.com/visualizer/candlestick-chart/',
325 533 ),
326 534 // pro types
327 - 'table' => array(
328 - 'name' => esc_html__( 'Table', 'visualizer' ),
329 - 'enabled' => false,
330 - ),
331 535 'timeline' => array(
332 536 'name' => esc_html__( 'Timeline', 'visualizer' ),
333 537 'enabled' => false,
538 + 'supports' => array( 'Google Charts', 'ChartJS' ),
539 + 'demo_url' => 'https://demo.themeisle.com/visualizer/timeline-chart/',
334 540 ),
335 541 'combo' => array(
336 542 'name' => esc_html__( 'Combo', 'visualizer' ),
337 543 'enabled' => false,
544 + 'supports' => array( 'Google Charts' ),
545 + 'demo_url' => 'https://demo.themeisle.com/visualizer/combo-chart/',
338 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 + ),
339 559 )
340 560 );
341 561 $types = apply_filters( 'visualizer_pro_chart_types', $types );
342 562 if ( $enabledOnly ) {
@@ -345,8 +565,12 @@
345 565 if ( ! is_array( $array ) ) {
346 566 // support for old pro
347 567 $array = array( 'enabled' => true, 'name' => $array );
348 568 }
569 + // backward compatibility for PRO before v1.9.0
570 + if ( ! array_key_exists( 'supports', $array ) ) {
571 + $array['supports'] = array( 'Google Charts' );
572 + }
349 573 if ( ! $array['enabled'] ) {
350 574 continue;
351 575 }
352 576 $filtered[ $type ] = $array;
@@ -359,13 +583,74 @@
359 583 if ( ! is_array( $array ) ) {
360 584 // support for old pro
361 585 $array = array( 'enabled' => true, 'name' => $array );
362 586 }
587 + // backward compatibility for PRO before v1.9.0
588 + if ( ! array_key_exists( 'supports', $array ) ) {
589 + $array['supports'] = array( 'Google Charts' );
590 + }
363 591 $doubleD[ $type ] = $array['name'];
364 592 }
365 593 $types = $doubleD;
366 594 }
367 595
596 + return self::handleDeprecatedCharts( $types, $enabledOnly, $get2Darray, $where );
597 + }
598 +
599 + /**
600 + * Handle (soon-to-be) deprecated charts.
601 + */
602 + private static function handleDeprecatedCharts( $types, $enabledOnly, $get2Darray, $where ) {
603 + $deprecated = array();
604 +
605 + switch ( $where ) {
606 + case 'types':
607 + // fall-through
608 + case 'library':
609 + // if a user has a Gauge/Candlestick chart, then let them keep using it.
610 + if ( ! Visualizer_Module::is_pro() ) {
611 + if ( ! self::hasChartType( 'gauge' ) ) {
612 + if ( $get2Darray ) {
613 + $deprecated[] = 'gauge';
614 + } else {
615 + $types['gauge']['enabled'] = false;
616 + }
617 + }
618 + if ( ! self::hasChartType( 'candlestick' ) ) {
619 + if ( $get2Darray ) {
620 + $deprecated[] = 'candlestick';
621 + } else {
622 + $types['candlestick']['enabled'] = false;
623 + }
624 + }
625 + }
626 + break;
627 + default:
628 + // if a user has a Gauge/Candlestick chart, then let them keep using it.
629 + if ( ! Visualizer_Module::is_pro() ) {
630 + if ( ! self::hasChartType( 'gauge' ) ) {
631 + if ( $get2Darray ) {
632 + $deprecated[] = 'gauge';
633 + } else {
634 + $types['gauge']['enabled'] = false;
635 + }
636 + }
637 + if ( ! self::hasChartType( 'candlestick' ) ) {
638 + if ( $get2Darray ) {
639 + $deprecated[] = 'candlestick';
640 + } else {
641 + $types['candlestick']['enabled'] = false;
642 + }
643 + }
644 + }
645 + }
646 +
647 + if ( $deprecated ) {
648 + foreach ( $deprecated as $type ) {
649 + unset( $types[ $type ] );
650 + }
651 + }
652 +
368 653 return $types;
369 654 }
370 655
371 656 /**
@@ -375,11 +660,11 @@
375 660 * @global string $pagenow The name of the current page.
376 661 *
377 662 * @access public
378 663 */
379 - public function renderTempaltes() {
664 + public function renderTemplates() {
380 665 global $pagenow;
381 - if ( 'post.php' != $pagenow && 'post-new.php' != $pagenow ) {
666 + if ( 'post.php' !== $pagenow && 'post-new.php' !== $pagenow ) {
382 667 return;
383 668 }
384 669 $render = new Visualizer_Render_Templates();
385 670 $render->render();
@@ -397,30 +682,159 @@
397 682 *
398 683 * @param string $suffix The current page suffix.
399 684 */
400 685 public function enqueueLibraryScripts( $suffix ) {
401 - if ( $suffix == $this->_libraryPage ) {
402 - wp_enqueue_style( 'visualizer-library', VISUALIZER_ABSURL . 'css/library.css', array(), Visualizer_Plugin::VERSION );
403 - $this->_addFilter( 'media_upload_tabs', 'setupVisualizerTab' );
404 - 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 + }
405 715 wp_enqueue_script(
406 - 'visualizer-library', VISUALIZER_ABSURL . 'js/library.js', array(
407 - 'jquery',
408 - 'media-views',
409 - ), Visualizer_Plugin::VERSION, true
716 + "visualizer-render-$library",
717 + VISUALIZER_ABSURL . 'js/render-facade.js',
718 + apply_filters( 'visualizer_assets_render', array( 'visualizer-library', 'visualizer-customization' ), true ),
719 + Visualizer_Plugin::VERSION,
720 + true
410 721 );
411 - wp_enqueue_script( 'google-jsapi-new', '//www.gstatic.com/charts/loader.js', array(), null, true );
412 - wp_enqueue_script( 'google-jsapi-old', '//www.google.com/jsapi', array( 'google-jsapi-new' ), null, true );
722 + if ( 'd3' === $library ) {
723 + $has_d3 = true;
724 + }
725 + }
726 +
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 + }
753 +
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,
769 + true
770 + );
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 + }
413 787 wp_enqueue_script(
414 - 'visualizer-render', VISUALIZER_ABSURL . 'js/render.js', array(
415 - 'google-jsapi-old',
416 - 'visualizer-library',
417 - ), Visualizer_Plugin::VERSION, true
788 + 'visualizer-chart-builder',
789 + VISUALIZER_ABSURL . 'classes/Visualizer/ChartBuilder/build/index.js',
790 + $chart_builder_deps,
791 + $asset['version'],
792 + true
418 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 + );
419 819 }
820 +
821 + do_action( 'themeisle_internal_page', VISUALIZER_DIRNAME, 'library' );
420 822 }
421 823
422 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 + /**
423 837 * Adds visualizer tab for media upload tabs array.
424 838 *
425 839 * @since 1.0.0
426 840 *
@@ -443,24 +857,201 @@
443 857 *
444 858 * @access public
445 859 */
446 860 public function registerAdminMenu() {
447 - $title = esc_html__( 'Visualizer Library', 'visualizer' );
448 - $callback = array( $this, 'renderLibraryPage' );
449 - $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' ) );
450 902 }
451 903
452 904 /**
453 - * Renders visualizer library page.
454 - *
455 - * @since 1.0.0
456 - *
457 - * @access public
905 + * Handle get pro plugin submenu.
458 906 */
459 - public function renderLibraryPage() {
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 + /**
1040 + * Get the instance of WP_Query that fetches the charts as per the given criteria.
1041 + */
1042 + private function getQuery() {
1043 + static $q;
1044 + if ( ! is_null( $q ) ) {
1045 + return $q;
1046 + }
1047 +
460 1048 // get current page
461 1049 $page = filter_input(
462 - INPUT_GET, 'vpage', FILTER_VALIDATE_INT, array(
1050 + INPUT_GET,
1051 + 'vpage',
1052 + FILTER_VALIDATE_INT,
1053 + array(
463 1054 'options' => array(
464 1055 'min_range' => 1,
465 1056 'default' => 1,
466 1057 ),
@@ -465,29 +1056,34 @@
465 1056 'default' => 1,
466 1057 ),
467 1058 )
468 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 +
469 1075 // the initial query arguments to fetch charts
470 1076 $query_args = array(
471 1077 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
472 - 'posts_per_page' => 6,
1078 + 'posts_per_page' => $per_page,
473 1079 'paged' => $page,
474 1080 );
475 - // add chart type filter to the query arguments
476 - $filter = filter_input( INPUT_GET, 'type' );
477 - if ( $filter && in_array( $filter, Visualizer_Plugin::getChartTypes() ) ) {
478 - $query_args['meta_query'] = array(
479 - array(
480 - 'key' => Visualizer_Plugin::CF_CHART_TYPE,
481 - 'value' => $filter,
482 - 'compare' => '=',
483 - ),
484 - );
485 - } else {
486 - $filter = 'all';
487 - }
1081 +
1082 + $this->getDisplayFilters( $query_args );
1083 +
488 1084 // Added by Ash/Upwork
489 - $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
490 1086 if ( $filterByMeta ) {
491 1087 $query = array(
492 1088 'key' => Visualizer_Plugin::CF_SETTINGS,
493 1089 'value' => $filterByMeta,
@@ -496,56 +1092,246 @@
496 1092 $meta = isset( $query_args['meta_query'] ) ? $query_args['meta_query'] : array();
497 1093 $meta[] = $query;
498 1094 $query_args['meta_query'] = $meta;
499 1095 }
500 - // Added by Ash/Upwork
501 - // fetch charts
1096 + $q = new WP_Query( apply_filters( 'visualizer_query_args', $query_args ) );
1097 + return $q;
1098 + }
1099 +
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 + /**
1196 + * Renders visualizer library page.
1197 + *
1198 + * @since 1.0.0
1199 + *
1200 + * @access public
1201 + */
1202 + public function renderLibraryPage() {
502 1203 $charts = array();
503 - $query = new WP_Query( $query_args );
1204 + $query = $this->getQuery();
1205 +
1206 + // get current page
1207 + $page = filter_input(
1208 + INPUT_GET,
1209 + 'vpage',
1210 + FILTER_VALIDATE_INT,
1211 + array(
1212 + 'options' => array(
1213 + 'min_range' => 1,
1214 + 'default' => 1,
1215 + ),
1216 + )
1217 + );
1218 + // add chart type filter to the query arguments
1219 + $filter = filter_input( INPUT_GET, 'type' );
1220 + if ( ! ( $filter && in_array( $filter, Visualizer_Plugin::getChartTypes(), true ) ) ) {
1221 + $filter = 'all';
1222 + }
1223 +
1224 + $css = '';
504 1225 while ( $query->have_posts() ) {
505 1226 $chart = $query->next_post();
506 1227
507 1228 // if the user has updated a chart and instead of saving it, has closed the modal. If the user refreshes, they should get the original chart.
508 1229 $chart = $this->handleExistingRevisions( $chart->ID, $chart );
1230 + // refresh a "live" db query chart.
1231 + $chart = apply_filters( 'visualizer_schedule_refresh_chart', $chart, $chart->ID, false );
509 1232
1233 + $type = get_post_meta( $chart->ID, Visualizer_Plugin::CF_CHART_TYPE, true );
1234 +
510 1235 // fetch and update settings
511 1236 $settings = get_post_meta( $chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
512 - unset( $settings['height'], $settings['width'] );
513 - $type = get_post_meta( $chart->ID, Visualizer_Plugin::CF_CHART_TYPE, true );
1237 +
1238 + $settings = apply_filters( Visualizer_Plugin::FILTER_GET_CHART_SETTINGS, $settings, $chart->ID, $type );
1239 + if ( ! empty( $atts['settings'] ) ) {
1240 + $settings = apply_filters( $atts['settings'], $settings, $chart->ID, $type );
1241 + }
1242 +
1243 + if ( $settings ) {
1244 + unset( $settings['height'], $settings['width'], $settings['chartArea'] );
1245 + }
1246 +
514 1247 $series = apply_filters( Visualizer_Plugin::FILTER_GET_CHART_SERIES, get_post_meta( $chart->ID, Visualizer_Plugin::CF_SERIES, true ), $chart->ID, $type );
515 - $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 );
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 +
1262 + $library = $this->load_chart_type( $chart->ID );
1263 +
1264 + $id = 'visualizer-' . $chart->ID;
1265 + $arguments = $this->get_inline_custom_css( $id, $settings );
1266 + if ( ! empty( $arguments ) ) {
1267 + $css .= $arguments[0];
1268 + $settings = $arguments[1];
1269 + }
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 +
516 1278 // add chart to the array
517 - $charts[ 'visualizer-' . $chart->ID ] = array(
1279 + $charts[ $id ] = array(
518 1280 'id' => $chart->ID,
1281 + 'title' => $chart->post_title,
519 1282 'type' => $type,
520 1283 'series' => $series,
521 1284 'settings' => $settings,
522 1285 'data' => $data,
1286 + 'library' => $library,
523 1287 );
1288 + if ( 'd3' === $library ) {
1289 + $charts[ $id ]['code'] = get_post_meta( $chart->ID, Visualizer_Module_AIBuilder::CF_D3_CODE, true );
1290 + }
524 1291 }
525 1292 // enqueue charts array
526 1293 $ajaxurl = admin_url( 'admin-ajax.php' );
1294 +
527 1295 wp_localize_script(
528 - 'visualizer-library', 'visualizer', array(
1296 + 'visualizer-library',
1297 + 'visualizer',
1298 + array(
529 1299 'language' => $this->get_language(),
530 1300 'map_api_key' => get_option( 'visualizer-map-api-key' ),
531 1301 'charts' => $charts,
532 1302 'urls' => array(
533 - 'base' => add_query_arg( 'vpage', false ),
534 - 'create' => add_query_arg(
535 - array(
536 - 'action' => Visualizer_Plugin::ACTION_CREATE_CHART,
537 - 'library' => 'yes',
538 - 'type' => isset( $_GET['type'] ) ? $_GET['type'] : '',
539 - ), $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 + )
540 1315 ),
541 - 'edit' => add_query_arg(
542 - array(
543 - 'action' => Visualizer_Plugin::ACTION_EDIT_CHART,
544 - 'library' => 'yes',
545 - ), $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 + )
546 1325 ),
547 1326 ),
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(),
548 1334 )
549 1335 );
550 1336 // render library page
551 1337 $render = new Visualizer_Render_Library();
@@ -550,12 +1336,13 @@
550 1336 // render library page
551 1337 $render = new Visualizer_Render_Library();
552 1338 $render->charts = $charts;
553 1339 $render->type = $filter;
554 - $render->types = self::_getChartTypesLocalized();
1340 + $render->types = self::_getChartTypesLocalized( false, false, false, true );
1341 + $render->custom_css = $css;
555 1342 $render->pagination = paginate_links(
556 1343 array(
557 - 'base' => add_query_arg( 'vpage', '%#%' ),
1344 + 'base' => add_query_arg( array( 'vpage' => '%#%', 'vaction' => false ) ),
558 1345 'format' => '',
559 1346 'current' => $page,
560 1347 'total' => $query->max_num_pages,
561 1348 'type' => 'array',
@@ -560,8 +1347,9 @@
560 1347 'total' => $query->max_num_pages,
561 1348 'type' => 'array',
562 1349 )
563 1350 );
1351 +
564 1352 $render->render();
565 1353 }
566 1354
567 1355 /**
@@ -576,14 +1364,14 @@
576 1364 *
577 1365 * @return array Updated array of action links.
578 1366 */
579 1367 public function getPluginActionLinks( $links, $file ) {
580 - if ( $file == plugin_basename( VISUALIZER_BASEFILE ) ) {
1368 + if ( $file === plugin_basename( VISUALIZER_BASEFILE ) ) {
581 1369 array_unshift(
582 1370 $links,
583 1371 sprintf(
584 1372 '<a href="%s">%s</a>',
585 - admin_url( 'upload.php?page=' . Visualizer_Plugin::NAME ),
1373 + admin_url( 'admin.php?page=' . Visualizer_Plugin::NAME ),
586 1374 esc_html__( 'Library', 'visualizer' )
587 1375 )
588 1376 );
589 1377 }
@@ -603,18 +1391,32 @@
603 1391 *
604 1392 * @return array Updated array of plugin meta links.
605 1393 */
606 1394 public function getPluginMetaLinks( $plugin_meta, $plugin_file ) {
607 - 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 ) ) {
608 1410 // knowledge base link
609 1411 $plugin_meta[] = sprintf(
610 - '<a href="https://github.com/codeinwp/visualizer/wiki" target="_blank">%s</a>',
611 - esc_html__( 'Knowledge Base', 'visualizer' )
1412 + '<a href="' . VISUALIZER_MAIN_DOC . '" target="_blank">%s</a>',
1413 + esc_html__( 'Docs', 'visualizer' )
612 1414 );
613 1415 // flattr link
614 1416 $plugin_meta[] = sprintf(
615 - '<a style="color:red" href="https://themeisle.com/plugins/visualizer-charts-and-graphs-pro-addon/" target="_blank">%s</a>',
616 - esc_html__( 'Pro Addon', 'visualizer' )
1417 + '<a style="color:red" href="' . tsdk_utmify( Visualizer_Plugin::PRO_TEASER_URL, 'pluginrow' ) . '" target="_blank">%s</a>',
1418 + esc_html__( 'Get Visualizer Pro', 'visualizer' )
617 1419 );
618 1420 }
619 1421
620 1422 return $plugin_meta;
@@ -619,5 +1421,213 @@
619 1421
620 1422 return $plugin_meta;
621 1423 }
622 1424
1425 + /**
1426 + * Returns true when premium chart types should be enabled for the current user.
1427 + *
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
1437 + */
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 ) {
1443 + return true;
1444 + }
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;
1457 + }
1458 +
1459 + /**
1460 + * Multilingual Support.
1461 + *
1462 + * @param int $chart_id Chart ID.
1463 + * @return bool Default false
1464 + */
1465 + public function addMultilingualSupport( $chart_id ) {
1466 + global $sitepress;
1467 + if ( Visualizer_Module::is_pro() ) {
1468 + return;
1469 + }
1470 + if ( function_exists( 'icl_get_languages' ) && $sitepress instanceof \SitePress ) {
1471 + $language = icl_get_languages();
1472 + $current_lang = icl_get_current_language();
1473 + $default_lang = icl_get_default_language();
1474 + $post_info = wpml_get_language_information( null, $chart_id );
1475 + $translations = array();
1476 + if ( ! empty( $post_info ) && ( $default_lang === $post_info['language_code'] ) ) {
1477 + $trid = $sitepress->get_element_trid( $chart_id, 'post_' . Visualizer_Plugin::CPT_VISUALIZER );
1478 + $translations = $sitepress->get_element_translations( $trid );
1479 + }
1480 + if ( empty( $translations ) ) {
1481 + return;
1482 + }
1483 + ?>
1484 + <hr><div class="visualizer-languages-list only-pro">
1485 + <?php
1486 + foreach ( $language as $lang ) {
1487 + $lang_code = $lang['code'];
1488 + if ( $current_lang !== $lang_code ) {
1489 + ?>
1490 + <a href="javascript:;">
1491 + <img src="<?php echo esc_url( $lang['country_flag_url'] ); ?>" alt="<?php echo esc_attr( $lang['translated_name'] ); ?>">
1492 + </a>
1493 + <?php
1494 + }
1495 + }
1496 + ?>
1497 + <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>
1498 + </div>
1499 + <?php
1500 + }
1501 + }
1502 +
1503 + /**
1504 + * Check chart is enabled or not.
1505 + *
1506 + * @param string $type Chart ID.
1507 + *
1508 + * @return bool Default false
1509 + */
1510 + public static function checkChartStatus( $type ) {
1511 + $types = self::_getChartTypesLocalized( false, false, false, true );
1512 + if ( isset( $types[ $type ] ) ) {
1513 + return ! empty( $types[ $type ]['enabled'] );
1514 + }
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;
1632 + }
623 1633 }