PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 4.0.8
Visualizer – Tables & Charts Manager with Built-in AI Generator v4.0.8
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 +1020 -131 3.1.04.0.8 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,138 @@
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 );
61 - $this->_addFilter( 'visualizer_logger_data', 'getLoggerData' );
62 - $this->_addFilter( 'visualizer_get_chart_counts', 'getChartCountsByTypeAndMeta' );
83 + $this->_addFilter( 'plugin_row_meta', 'getPluginMetaLinks', 10, 2 );
63 84 $this->_addFilter( 'visualizer_feedback_review_trigger', 'feedbackReviewTrigger' );
85 + $this->_addFilter( 'themeisle_sdk_blackfriday_data', 'add_black_friday_data' );
64 86
87 + // screen pagination
88 + $this->_addFilter( 'set-screen-option', 'setScreenOptions', 10, 3 );
89 +
65 90 // revision support.
66 91 $this->_addFilter( 'wp_revisions_to_keep', 'limitRevisions', null, 10, 2 );
67 92 $this->_addAction( '_wp_put_post_revision', 'addRevision', null, 10, 1 );
68 93 $this->_addAction( 'wp_restore_post_revision', 'restoreRevision', null, 10, 2 );
69 94
95 + $this->_addAction( 'visualizer_chart_schedules_spl', 'addSplChartSchedules', null, 10, 3 );
96 +
70 97 $this->_addAction( 'admin_init', 'init' );
98 +
99 + $this->_addAction( 'visualizer_chart_languages', 'addMultilingualSupport' );
100 +
101 + $this->_addFilter( 'admin_footer_text', 'render_review_notice' );
102 +
103 + if ( ! defined( 'TI_E2E_TESTING' ) ) {
104 + $this->_addFilter( 'themeisle-sdk/survey/' . VISUALIZER_DIRNAME, 'get_survey_metadata', 10, 2 );
105 + }
106 +
107 + if ( defined( 'TI_E2E_TESTING' ) ) {
108 + $this->load_cypress_hooks();
109 + }
71 110 }
111 + /**
112 + * Display review notice.
113 + */
114 + public function render_review_notice( $footer_text ) {
115 + $current_screen = get_current_screen();
72 116
117 + $visualizer_page_ids = array( 'toplevel_page_visualizer', 'visualizer_page_viz-support', 'visualizer_page_ti-about-visualizer' );
118 +
119 + if ( ! empty( $current_screen ) && isset( $current_screen->id ) ) {
120 + foreach ( $visualizer_page_ids as $page_to_check ) {
121 + if ( strpos( $current_screen->id, $page_to_check ) !== false ) {
122 + $footer_text = sprintf(
123 + // translators: %1$s - the name of the plugin (Visualizer), %2$s - message (You can help us by leaving a), %3$s - HTML entity code.
124 + __( 'Enjoying %1$s? %2$s %3$s rating. Thank you for being so supportive!', 'visualizer' ),
125 + '<b>Visualizer</b>',
126 + esc_html__( 'You can help us by leaving a', 'visualizer' ),
127 + '<a href="https://wordpress.org/support/plugin/visualizer/reviews/#new-post" target="_blank">&#9733;&#9733;&#9733;&#9733;&#9733;</a>'
128 + );
129 + break;
130 + }
131 + }
132 + }
133 +
134 + return $footer_text;
135 + }
136 +
137 +
73 138 /**
139 + * Define the hooks that are needed for cypress.
140 + *
141 + * @since 3.4.0
142 + * @access private
143 + */
144 + private function load_cypress_hooks() {
145 + // all charts should load on the same page without pagination.
146 + add_filter(
147 + 'visualizer_query_args', function ( $args ) {
148 + $args['posts_per_page'] = 20;
149 + return $args;
150 + }, 10, 1
151 + );
152 + }
153 +
154 + /**
155 + * Add disabled `optgroup` schedules to the drop downs.
156 + *
157 + * @since ?
158 + *
159 + * @access public
160 + *
161 + * @param string $feature The feature for which to add schedules.
162 + * @param int $chart_id The chart ID.
163 + * @param int $plan The plan number.
164 + */
165 + public function addSplChartSchedules( $feature, $chart_id, $plan ) {
166 + if ( apply_filters( 'visualizer_is_business', false ) ) {
167 + return;
168 + }
169 +
170 + $is_new_personal = apply_filters( 'visualizer_is_new_personal', false );
171 +
172 + $hours = array(
173 + '0.16' => __( '10 minutes', 'visualizer' ),
174 + '1' => __( 'Each hour', 'visualizer' ),
175 + '12' => __( 'Each 12 hours', 'visualizer' ),
176 + '24' => __( 'Each day', 'visualizer' ),
177 + '72' => __( 'Each 3 days', 'visualizer' ),
178 + );
179 +
180 + switch ( $feature ) {
181 + case 'json':
182 + case 'csv':
183 + // no more schedules if pro is already active.
184 + if ( Visualizer_Module::is_pro() && ! $is_new_personal ) {
185 + return;
186 + }
187 + break;
188 + case 'wp':
189 + // fall-through.
190 + case 'db':
191 + break;
192 + default:
193 + return;
194 + }
195 +
196 + echo '<optgroup disabled label="' . __( 'Upgrade required', 'visualizer' ) . '">';
197 + foreach ( $hours as $hour => $desc ) {
198 + echo '<option disabled>' . $desc . '</option>';
199 + }
200 + echo '</optgroup>';
201 + }
202 +
203 + /**
74 204 * No limits on revisions.
75 205 */
76 206 public function limitRevisions( $num, $post ) {
77 207 if ( Visualizer_Plugin::CPT_VISUALIZER === $post->post_type ) {
@@ -91,9 +221,9 @@
91 221
92 222 if ( $meta ) {
93 223 foreach ( $meta as $key => $value ) {
94 224 if ( 0 === strpos( $key, 'visualizer' ) ) {
95 - add_metadata( 'post', $revision_id, $key, maybe_unserialize( $value[0] ) );
225 + add_metadata( 'post', $revision_id, $key, self::maybe_decode_content( $value[0] ) );
96 226 }
97 227 }
98 228 }
99 229 }
@@ -119,9 +249,9 @@
119 249
120 250 if ( $meta ) {
121 251 foreach ( $meta as $key => $value ) {
122 252 if ( 0 === strpos( $key, 'visualizer' ) ) {
123 - add_post_meta( $post_id, $key, maybe_unserialize( $value[0] ) );
253 + add_post_meta( $post_id, $key, self::maybe_decode_content( $value[0] ) );
124 254 }
125 255 }
126 256 }
127 257 }
@@ -132,16 +262,30 @@
132 262 *
133 263 * @access public
134 264 */
135 265 public function init() {
136 - if ( current_user_can( 'edit_posts' ) && current_user_can( 'edit_pages' ) && 'true' == get_user_option( 'rich_editing' ) ) {
266 + if ( current_user_can( 'edit_posts' ) && current_user_can( 'edit_pages' ) && 'true' === get_user_option( 'rich_editing' ) ) {
137 267 $this->_addFilter( 'mce_external_languages', 'add_tinymce_lang', 10, 1 );
138 268 $this->_addFilter( 'mce_external_plugins', 'tinymce_plugin', 10, 1 );
139 269 $this->_addFilter( 'mce_buttons', 'register_mce_button', 10, 1 );
270 + $this->_addFilter( 'tiny_mce_before_init', 'get_strings_for_block', 10, 1 );
140 271 }
141 272 }
142 273
143 274 /**
275 + * Add the strings required for the TinyMCE buttons for the classic block (not the classic editor).
276 + *
277 + * @since ?
278 + * @access friendly
279 + */
280 + public function get_strings_for_block( $settings ) {
281 + $class = new Visualizer_Module_Language();
282 + $strings = $class->get_strings();
283 + $array = array( 'visualizer_tinymce_plugin' => json_encode( $strings ) );
284 + return array_merge( $settings, $array );
285 + }
286 +
287 + /**
144 288 * Load plugin translation for - TinyMCE API
145 289 *
146 290 * @access public
147 291 * @param array $arr The tinymce_lang array.
@@ -210,14 +354,41 @@
210 354 * @access public
211 355 */
212 356 public function enqueueMediaScripts() {
213 357 global $typenow;
214 - if ( post_type_supports( $typenow, 'editor' ) ) {
358 + global $current_screen;
359 +
360 + if ( post_type_supports( $typenow, 'editor' ) || $current_screen->id === 'widgets' || $current_screen->id === 'customize' ) {
215 361 wp_enqueue_style( 'visualizer-media', VISUALIZER_ABSURL . 'css/media.css', array( 'media-views' ), Visualizer_Plugin::VERSION );
216 362
363 + $d3_renderer_asset = VISUALIZER_ABSPATH . '/classes/Visualizer/D3Renderer/build/index.asset.php';
364 + if ( file_exists( $d3_renderer_asset ) && ! wp_script_is( 'visualizer-d3-renderer', 'registered' ) ) {
365 + // @phpstan-ignore-next-line
366 + $d3_asset = include $d3_renderer_asset;
367 + wp_register_script(
368 + 'visualizer-d3-renderer',
369 + VISUALIZER_ABSURL . 'classes/Visualizer/D3Renderer/build/index.js',
370 + array_merge( $d3_asset['dependencies'], array( 'jquery' ) ),
371 + $d3_asset['version'],
372 + true
373 + );
374 + }
375 + if ( wp_script_is( 'visualizer-d3-renderer', 'registered' ) ) {
376 + wp_enqueue_script( 'visualizer-d3-renderer' );
377 + wp_localize_script(
378 + 'visualizer-d3-renderer',
379 + 'vizD3Renderer',
380 + array(
381 + 'iframeJsUrl' => VISUALIZER_ABSURL . 'classes/Visualizer/D3Renderer/build/iframe.js',
382 + )
383 + );
384 + }
385 +
217 386 // 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' ) );
387 + $deps = array(
388 + Visualizer_Render_Sidebar_Google::enqueue_assets( array( 'media-editor' ) ),
389 + Visualizer_Render_Sidebar_Type_DataTable_Tabular::enqueue_assets( array( 'media-editor' ) ),
390 + );
220 391
221 392 wp_enqueue_script( 'visualizer-media-model', VISUALIZER_ABSURL . 'js/media/model.js', $deps, Visualizer_Plugin::VERSION, true );
222 393 wp_enqueue_script( 'visualizer-media-collection', VISUALIZER_ABSURL . 'js/media/collection.js', array( 'visualizer-media-model' ), Visualizer_Plugin::VERSION, true );
223 394 wp_enqueue_script( 'visualizer-media-controller', VISUALIZER_ABSURL . 'js/media/controller.js', array( 'visualizer-media-collection' ), Visualizer_Plugin::VERSION, true );
@@ -226,9 +397,9 @@
226 397 'visualizer-media-view',
227 398 'visualizer',
228 399 array(
229 400 'i10n' => array(
230 - 'insert' => __( 'Insert', 'visualizer' ),
401 + 'insert' => __( 'Insert Chart', 'visualizer' ),
231 402 ),
232 403 )
233 404 );
234 405 wp_enqueue_script( 'visualizer-media-toolbar', VISUALIZER_ABSURL . 'js/media/toolbar.js', array( 'visualizer-media-view' ), Visualizer_Plugin::VERSION, true );
@@ -257,9 +428,9 @@
257 428 'controller' => array(
258 429 'title' => esc_html__( 'Visualizations', 'visualizer' ),
259 430 ),
260 431 'routers' => array(
261 - 'library' => esc_html__( 'From Library', 'visualizer' ),
432 + 'library' => esc_html__( 'Insert from Library', 'visualizer' ),
262 433 'create' => esc_html__( 'Create New', 'visualizer' ),
263 434 ),
264 435 'library' => array(
265 436 'filters' => $chart_types,
@@ -264,10 +435,10 @@
264 435 'library' => array(
265 436 'filters' => $chart_types,
266 437 'types' => array_keys( $chart_types ),
267 438 ),
268 - 'nonce' => wp_create_nonce(),
269 - 'buildurl' => add_query_arg( 'action', Visualizer_Plugin::ACTION_CREATE_CHART, admin_url( 'admin-ajax.php' ) ),
439 + 'nonce' => wp_create_nonce( Visualizer_Plugin::ACTION_GET_CHARTS ),
440 + 'buildurl' => esc_url( add_query_arg( 'action', Visualizer_Plugin::ACTION_CREATE_CHART, admin_url( 'admin-ajax.php' ) ) ),
270 441 );
271 442
272 443 return $strings;
273 444 }
@@ -289,64 +460,102 @@
289 460 'enabled' => true,
290 461 );
291 462 }
292 463
464 + $enabled = self::proFeaturesEnabled();
465 +
293 466 $types = array_merge(
294 467 $additional,
295 468 array(
296 - 'dataTable' => array(
297 - 'name' => esc_html__( 'Table (New)', 'visualizer' ),
469 + 'tabular' => array(
470 + 'name' => esc_html__( 'Table', 'visualizer' ),
298 471 'enabled' => true,
472 + 'supports' => $enabled ? array( 'Google Charts', 'DataTable' ) : array( 'DataTable' ),
299 473 ),
300 474 'pie' => array(
301 - 'name' => esc_html__( 'Pie', 'visualizer' ),
475 + 'name' => esc_html__( 'Pie/Donut', 'visualizer' ),
302 476 'enabled' => true,
477 + 'supports' => array( 'Google Charts', 'ChartJS' ),
303 478 ),
304 479 'line' => array(
305 480 'name' => esc_html__( 'Line', 'visualizer' ),
306 481 'enabled' => true,
482 + 'supports' => array( 'Google Charts', 'ChartJS' ),
307 483 ),
484 + 'bar' => array(
485 + 'name' => esc_html__( 'Bar', 'visualizer' ),
486 + 'enabled' => true,
487 + 'supports' => $enabled ? array( 'Google Charts', 'ChartJS' ) : array( 'Google Charts' ),
488 + ),
308 489 'area' => array(
309 490 'name' => esc_html__( 'Area', 'visualizer' ),
310 - 'enabled' => true,
491 + 'enabled' => $enabled,
492 + // in ChartJS, the fill option is used to make Line chart an area: https://www.chartjs.org/docs/latest/charts/area.html
493 + 'supports' => array( 'Google Charts' ),
494 + 'demo_url' => 'https://demo.themeisle.com/visualizer/area-chart/',
311 495 ),
312 496 'geo' => array(
313 497 'name' => esc_html__( 'Geo', 'visualizer' ),
314 - 'enabled' => true,
498 + 'enabled' => $enabled,
499 + 'supports' => array( 'Google Charts' ),
500 + 'demo_url' => 'https://demo.themeisle.com/visualizer/geo-chart/',
315 501 ),
316 - 'bar' => array(
317 - 'name' => esc_html__( 'Bar', 'visualizer' ),
318 - 'enabled' => true,
319 - ),
320 502 'column' => array(
321 503 'name' => esc_html__( 'Column', 'visualizer' ),
322 - 'enabled' => true,
504 + 'enabled' => $enabled,
505 + 'supports' => array( 'Google Charts', 'ChartJS' ),
506 + 'demo_url' => 'https://demo.themeisle.com/visualizer/column-chart/',
323 507 ),
324 - 'gauge' => array(
325 - 'name' => esc_html__( 'Gauge', 'visualizer' ),
326 - 'enabled' => true,
508 + 'bubble' => array(
509 + 'name' => esc_html__( 'Bubble', 'visualizer' ),
510 + 'enabled' => $enabled,
511 + // chartjs' bubble is ugly looking (and it won't work off the default bubble.csv) so it is being excluded for the time being.
512 + 'supports' => array( 'Google Charts' ),
513 + 'demo_url' => 'https://demo.themeisle.com/visualizer/bubble-chart/',
327 514 ),
328 515 'scatter' => array(
329 516 'name' => esc_html__( 'Scatter', 'visualizer' ),
330 - 'enabled' => true,
517 + 'enabled' => $enabled,
518 + 'supports' => array( 'Google Charts' ),
519 + 'demo_url' => 'https://demo.themeisle.com/visualizer/scatter-chart/',
331 520 ),
521 + 'gauge' => array(
522 + 'name' => esc_html__( 'Gauge', 'visualizer' ),
523 + 'enabled' => $enabled,
524 + 'supports' => array( 'Google Charts' ),
525 + 'demo_url' => 'https://demo.themeisle.com/visualizer/gauge-chart/',
526 + ),
332 527 'candlestick' => array(
333 528 'name' => esc_html__( 'Candlestick', 'visualizer' ),
334 - 'enabled' => true,
529 + 'enabled' => $enabled,
530 + 'supports' => array( 'Google Charts' ),
531 + 'demo_url' => 'https://demo.themeisle.com/visualizer/candlestick-chart/',
335 532 ),
336 533 // pro types
337 - 'table' => array(
338 - 'name' => esc_html__( 'Table (Deprecated)', 'visualizer' ),
339 - 'enabled' => false,
340 - ),
341 534 'timeline' => array(
342 535 'name' => esc_html__( 'Timeline', 'visualizer' ),
343 536 'enabled' => false,
537 + 'supports' => array( 'Google Charts', 'ChartJS' ),
538 + 'demo_url' => 'https://demo.themeisle.com/visualizer/timeline-chart/',
344 539 ),
345 540 'combo' => array(
346 541 'name' => esc_html__( 'Combo', 'visualizer' ),
347 542 'enabled' => false,
543 + 'supports' => array( 'Google Charts' ),
544 + 'demo_url' => 'https://demo.themeisle.com/visualizer/combo-chart/',
348 545 ),
546 + 'polarArea' => array(
547 + 'name' => esc_html__( 'Polar Area', 'visualizer' ),
548 + 'enabled' => false,
549 + 'supports' => array( 'ChartJS' ),
550 + 'demo_url' => 'https://demo.themeisle.com/visualizer/polar-area-chart/',
551 + ),
552 + 'radar' => array(
553 + 'name' => esc_html__( 'Radar/Spider', 'visualizer' ),
554 + 'enabled' => false,
555 + 'supports' => array( 'ChartJS' ),
556 + 'demo_url' => 'https://demo.themeisle.com/visualizer/radar-spider-chart/',
557 + ),
349 558 )
350 559 );
351 560 $types = apply_filters( 'visualizer_pro_chart_types', $types );
352 561 if ( $enabledOnly ) {
@@ -355,8 +564,12 @@
355 564 if ( ! is_array( $array ) ) {
356 565 // support for old pro
357 566 $array = array( 'enabled' => true, 'name' => $array );
358 567 }
568 + // backward compatibility for PRO before v1.9.0
569 + if ( ! array_key_exists( 'supports', $array ) ) {
570 + $array['supports'] = array( 'Google Charts' );
571 + }
359 572 if ( ! $array['enabled'] ) {
360 573 continue;
361 574 }
362 575 $filtered[ $type ] = $array;
@@ -369,8 +582,12 @@
369 582 if ( ! is_array( $array ) ) {
370 583 // support for old pro
371 584 $array = array( 'enabled' => true, 'name' => $array );
372 585 }
586 + // backward compatibility for PRO before v1.9.0
587 + if ( ! array_key_exists( 'supports', $array ) ) {
588 + $array['supports'] = array( 'Google Charts' );
589 + }
373 590 $doubleD[ $type ] = $array['name'];
374 591 }
375 592 $types = $doubleD;
376 593 }
@@ -384,49 +601,45 @@
384 601 private static function handleDeprecatedCharts( $types, $enabledOnly, $get2Darray, $where ) {
385 602 $deprecated = array();
386 603
387 604 switch ( $where ) {
605 + case 'types':
606 + // fall-through
388 607 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 608 // if a user has a Gauge/Candlestick chart, then let them keep using it.
400 - if ( ! VISUALIZER_PRO ) {
609 + if ( ! Visualizer_Module::is_pro() ) {
401 610 if ( ! self::hasChartType( 'gauge' ) ) {
402 - $deprecated[] = 'gauge';
611 + if ( $get2Darray ) {
612 + $deprecated[] = 'gauge';
613 + } else {
614 + $types['gauge']['enabled'] = false;
615 + }
403 616 }
404 617 if ( ! self::hasChartType( 'candlestick' ) ) {
405 - $deprecated[] = 'candlestick';
618 + if ( $get2Darray ) {
619 + $deprecated[] = 'candlestick';
620 + } else {
621 + $types['candlestick']['enabled'] = false;
622 + }
406 623 }
407 624 }
408 625 break;
409 626 default:
410 - // remove the option to create a Google Table chart.
411 - $deprecated[] = 'table';
412 -
413 - // rename the new table chart type.
414 - if ( self::hasChartType( 'table' ) ) {
415 - if ( $get2Darray ) {
416 - $types['dataTable'] = esc_html__( 'Table', 'visualizer' );
417 - } else {
418 - $types['dataTable']['name'] = esc_html__( 'Table', 'visualizer' );
419 - }
420 - }
421 -
422 627 // if a user has a Gauge/Candlestick chart, then let them keep using it.
423 - if ( ! VISUALIZER_PRO ) {
628 + if ( ! Visualizer_Module::is_pro() ) {
424 629 if ( ! self::hasChartType( 'gauge' ) ) {
425 - $deprecated[] = 'gauge';
630 + if ( $get2Darray ) {
631 + $deprecated[] = 'gauge';
632 + } else {
633 + $types['gauge']['enabled'] = false;
634 + }
426 635 }
427 636 if ( ! self::hasChartType( 'candlestick' ) ) {
428 - $deprecated[] = 'candlestick';
637 + if ( $get2Darray ) {
638 + $deprecated[] = 'candlestick';
639 + } else {
640 + $types['candlestick']['enabled'] = false;
641 + }
429 642 }
430 643 }
431 644 }
432 645
@@ -448,9 +661,9 @@
448 661 * @access public
449 662 */
450 663 public function renderTemplates() {
451 664 global $pagenow;
452 - if ( 'post.php' != $pagenow && 'post-new.php' != $pagenow ) {
665 + if ( 'post.php' !== $pagenow && 'post-new.php' !== $pagenow ) {
453 666 return;
454 667 }
455 668 $render = new Visualizer_Render_Templates();
456 669 $render->render();
@@ -468,44 +681,159 @@
468 681 *
469 682 * @param string $suffix The current page suffix.
470 683 */
471 684 public function enqueueLibraryScripts( $suffix ) {
472 - if ( $suffix == $this->_libraryPage ) {
473 - wp_enqueue_style( 'visualizer-library', VISUALIZER_ABSURL . 'css/library.css', array(), Visualizer_Plugin::VERSION );
474 - $this->_addFilter( 'media_upload_tabs', 'setupVisualizerTab' );
475 - wp_enqueue_media();
685 + if ( $suffix !== $this->_libraryPage ) {
686 + return;
687 + }
688 +
689 + wp_enqueue_style( 'visualizer-library', VISUALIZER_ABSURL . 'css/library.css', array(), Visualizer_Plugin::VERSION );
690 + $this->_addFilter( 'media_upload_tabs', 'setupVisualizerTab' );
691 + wp_enqueue_media();
692 + wp_enqueue_script(
693 + 'visualizer-library',
694 + VISUALIZER_ABSURL . 'js/library.js',
695 + array(
696 + 'jquery',
697 + 'media-views',
698 + 'clipboard',
699 + ),
700 + Visualizer_Plugin::VERSION,
701 + true
702 + );
703 +
704 + wp_enqueue_script( 'visualizer-customization', $this->get_user_customization_js(), array(), null, true );
705 +
706 + $has_d3 = false;
707 + $query = $this->getQuery();
708 + while ( $query->have_posts() ) {
709 + $chart = $query->next_post();
710 + $library = $this->load_chart_type( $chart->ID );
711 + if ( is_null( $library ) ) {
712 + continue;
713 + }
476 714 wp_enqueue_script(
477 - 'visualizer-library',
478 - VISUALIZER_ABSURL . 'js/library.js',
479 - array(
480 - 'jquery',
481 - 'media-views',
482 - ),
715 + "visualizer-render-$library",
716 + VISUALIZER_ABSURL . 'js/render-facade.js',
717 + apply_filters( 'visualizer_assets_render', array( 'visualizer-library', 'visualizer-customization' ), true ),
483 718 Visualizer_Plugin::VERSION,
484 719 true
485 720 );
721 + if ( 'd3' === $library ) {
722 + $has_d3 = true;
723 + }
724 + }
486 725
487 - wp_enqueue_script( 'visualizer-customization', $this->get_user_customization_js(), array(), null, true );
726 + if ( $has_d3 ) {
727 + $d3_renderer_asset = VISUALIZER_ABSPATH . '/classes/Visualizer/D3Renderer/build/index.asset.php';
728 + if ( file_exists( $d3_renderer_asset ) && ! wp_script_is( 'visualizer-d3-renderer', 'registered' ) ) {
729 + /**
730 + * Ignore missing build asset in source checkout.
731 + *
732 + * @phpstan-ignore-next-line
733 + */
734 + $d3_asset = include $d3_renderer_asset;
735 + wp_register_script(
736 + 'visualizer-d3-renderer',
737 + VISUALIZER_ABSURL . 'classes/Visualizer/D3Renderer/build/index.js',
738 + array_merge( $d3_asset['dependencies'], array( 'jquery' ) ),
739 + $d3_asset['version'],
740 + true
741 + );
742 + wp_enqueue_script( 'visualizer-d3-renderer' );
743 + }
744 + wp_localize_script(
745 + 'visualizer-d3-renderer',
746 + 'vizD3Renderer',
747 + array(
748 + 'iframeJsUrl' => VISUALIZER_ABSURL . 'classes/Visualizer/D3Renderer/build/iframe.js',
749 + )
750 + );
751 + }
488 752
489 - $query = $this->getQuery();
490 - while ( $query->have_posts() ) {
491 - $chart = $query->next_post();
492 - $library = $this->load_chart_type( $chart->ID );
493 - if ( is_null( $library ) ) {
494 - continue;
495 - }
496 - wp_enqueue_script(
497 - "visualizer-render-$library",
498 - VISUALIZER_ABSURL . 'js/render-facade.js',
499 - apply_filters( 'visualizer_assets_render', array( 'visualizer-library', 'visualizer-customization' ), true ),
500 - Visualizer_Plugin::VERSION,
753 + $chart_builder_asset = VISUALIZER_ABSPATH . '/classes/Visualizer/ChartBuilder/build/index.asset.php';
754 + if ( file_exists( $chart_builder_asset ) ) {
755 + /**
756 + * Ignore missing build asset in source checkout.
757 + *
758 + * @phpstan-ignore-next-line
759 + */
760 + $asset = include $chart_builder_asset;
761 + $chart_builder_deps = $asset['dependencies'];
762 + if ( ! wp_script_is( 'visualizer-handsontable', 'registered' ) && defined( 'Visualizer_Pro_ABSURL' ) && defined( 'VISUALIZER_PRO_VERSION' ) ) {
763 + wp_register_script(
764 + 'visualizer-handsontable',
765 + Visualizer_Pro_ABSURL . 'vendor/handsontable/dist/handsontable.full.min.js',
766 + array(),
767 + VISUALIZER_PRO_VERSION,
501 768 true
502 769 );
503 770 }
771 + if ( ! wp_style_is( 'visualizer-handsontable', 'registered' ) && defined( 'Visualizer_Pro_ABSURL' ) && defined( 'VISUALIZER_PRO_VERSION' ) ) {
772 + wp_register_style(
773 + 'visualizer-handsontable',
774 + Visualizer_Pro_ABSURL . 'vendor/handsontable/dist/handsontable.full.min.css',
775 + array(),
776 + VISUALIZER_PRO_VERSION
777 + );
778 + }
779 + if ( wp_script_is( 'visualizer-handsontable', 'registered' ) ) {
780 + wp_enqueue_script( 'visualizer-handsontable' );
781 + $chart_builder_deps = array_unique( array_merge( $chart_builder_deps, array( 'visualizer-handsontable' ) ) );
782 + }
783 + if ( wp_style_is( 'visualizer-handsontable', 'registered' ) ) {
784 + wp_enqueue_style( 'visualizer-handsontable' );
785 + }
786 + wp_enqueue_script(
787 + 'visualizer-chart-builder',
788 + VISUALIZER_ABSURL . 'classes/Visualizer/ChartBuilder/build/index.js',
789 + $chart_builder_deps,
790 + $asset['version'],
791 + true
792 + );
793 + wp_enqueue_style(
794 + 'visualizer-chart-builder',
795 + VISUALIZER_ABSURL . 'classes/Visualizer/ChartBuilder/build/style-index.css',
796 + array(),
797 + $asset['version']
798 + );
799 + $chart_builder_css = VISUALIZER_ABSPATH . '/classes/Visualizer/ChartBuilder/build/index.css';
800 + if ( file_exists( $chart_builder_css ) ) {
801 + wp_enqueue_style(
802 + 'visualizer-chart-builder-runtime',
803 + VISUALIZER_ABSURL . 'classes/Visualizer/ChartBuilder/build/index.css',
804 + array( 'visualizer-chart-builder' ),
805 + $asset['version']
806 + );
807 + }
808 + wp_localize_script(
809 + 'visualizer-chart-builder',
810 + 'vizAIBuilder',
811 + array(
812 + 'ajaxUrl' => admin_url( 'admin-ajax.php' ),
813 + 'nonce' => wp_create_nonce( 'visualizer-ai-builder' ),
814 + 'isPro' => Visualizer_Module::is_pro(),
815 + 'upgradeUrl' => tsdk_utmify( Visualizer_Plugin::PRO_TEASER_URL, 'aiBuilderUpgrade', 'dataSource' ),
816 + )
817 + );
504 818 }
819 +
820 + do_action( 'themeisle_internal_page', VISUALIZER_DIRNAME, 'library' );
505 821 }
506 822
507 823 /**
824 + * Enqueue script for support page.
825 + *
826 + * @param string $hook_suffix Current hook.
827 + */
828 + public function enqueue_support_page( $hook_suffix ) {
829 + if ( $this->_supportPage !== $hook_suffix ) {
830 + return;
831 + }
832 + do_action( 'themeisle_internal_page', VISUALIZER_DIRNAME, 'support' );
833 + }
834 +
835 + /**
508 836 * Adds visualizer tab for media upload tabs array.
509 837 *
510 838 * @since 1.0.0
511 839 *
@@ -528,14 +856,187 @@
528 856 *
529 857 * @access public
530 858 */
531 859 public function registerAdminMenu() {
532 - $title = esc_html__( 'Visualizer Library', 'visualizer' );
533 - $callback = array( $this, 'renderLibraryPage' );
534 - $this->_libraryPage = add_submenu_page( 'upload.php', $title, $title, 'edit_posts', Visualizer_Plugin::NAME, $callback );
860 + $svg_base64_icon = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iNzdweCIgaGVpZ2h0PSI3N3B4IiB2aWV3Qm94PSIwIDAgNzcgNzciIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDUyLjYgKDY3NDkxKSAtIGh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaCAtLT4KICAgIDx0aXRsZT5Db21iaW5lZCBTaGFwZTwvdGl0bGU+CiAgICA8ZGVzYz5DcmVhdGVkIHdpdGggU2tldGNoLjwvZGVzYz4KICAgIDxnIGlkPSJQcm9kdWN0LVBhZ2UiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPgogICAgICAgIDxnIGlkPSJXb3JkUHJlc3MtcGx1Z2lucyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTE5MC4wMDAwMDAsIC00MzUuMDAwMDAwKSIgZmlsbD0iIzM5QzNEMiI+CiAgICAgICAgICAgIDxwYXRoIGQ9Ik0yMjguNSw1MTIgQzIwNy4yMzcwMzcsNTEyIDE5MCw0OTQuNzYyOTYzIDE5MCw0NzMuNSBDMTkwLDQ1Mi4yMzcwMzcgMjA3LjIzNzAzNyw0MzUgMjI4LjUsNDM1IEMyNDkuNzYyOTYzLDQzNSAyNjcsNDUyLjIzNzAzNyAyNjcsNDczLjUgQzI2Nyw0OTQuNzYyOTYzIDI0OS43NjI5NjMsNTEyIDIyOC41LDUxMiBaIE0yNDYuODQzNzUsNDgzLjU5MjA1OSBMMjE1LjYyNSw0ODMuNTkyMDU5IEwyMTUuNjI1LDQ2MC44MjY1NDQgQzIxNS42MjUsNDYwLjE2NDU0NyAyMTUuMTA3NTc4LDQ1OS42MjgzNTkgMjE0LjQ2ODc1LDQ1OS42MjgzNTkgTDIxMi4xNTYyNSw0NTkuNjI4MzU5IEMyMTEuNTE3NDIyLDQ1OS42MjgzNTkgMjExLDQ2MC4xNjQ1NDcgMjExLDQ2MC44MjY1NDQgTDIxMSw0ODUuOTg4NDI5IEMyMTEsNDg3LjMxMTY3NCAyMTIuMDM1NTY2LDQ4OC4zODQ3OTggMjEzLjMxMjUsNDg4LjM4NDc5OCBMMjQ2Ljg0Mzc1LDQ4OC4zODQ3OTggQzI0Ny40ODI1NzgsNDg4LjM4NDc5OCAyNDgsNDg3Ljg0ODYxMSAyNDgsNDg3LjE4NjYxMyBMMjQ4LDQ4NC43OTAyNDQgQzI0OCw0ODQuMTI4MjQ2IDI0Ny40ODI1NzgsNDgzLjU5MjA1OSAyNDYuODQzNzUsNDgzLjU5MjA1OSBaIE0yNDQuNTMxMjUsNDYyLjAyNDcyOSBMMjM1Ljk5OTU3LDQ2Mi4wMjQ3MjkgQzIzNC40NTQ1MzEsNDYyLjAyNDcyOSAyMzMuNjgwNTY2LDQ2My45NjA1NDcgMjM0Ljc3MzIyMyw0NjUuMDkyODMyIEwyMzcuMTE0NjI5LDQ2Ny41MTkxNTYgTDIzMS44MTI1LDQ3My4wMTQzMzIgTDIyNi41MTAzNzEsNDY3LjUxOTkwNSBDMjI1LjYwNzA1MSw0NjYuNTgzODIzIDIyNC4xNDI5NDksNDY2LjU4MzgyMyAyMjMuMjQwMzUyLDQ2Ny41MTk5MDUgTDIxOC4yNzY0MjYsNDcyLjY2Mzg2MyBDMjE3LjgyNDc2Niw0NzMuMTMxOTA0IDIxNy44MjQ3NjYsNDczLjg5MDUwNSAyMTguMjc2NDI2LDQ3NC4zNTg1NDYgTDIxOS45MTEwNzQsNDc2LjA1MjQ4IEMyMjAuMzYyNzM0LDQ3Ni41MjA1MjEgMjIxLjA5NDc4NSw0NzYuNTIwNTIxIDIyMS41NDY0NDUsNDc2LjA1MjQ4IEwyMjQuODc1LDQ3Mi42MDI0NTYgTDIzMC4xNzcxMjksNDc4LjA5Njg4MyBDMjMxLjA4MDQ0OSw0NzkuMDMyOTY1IDIzMi41NDQ1NTEsNDc5LjAzMjk2NSAyMzMuNDQ3MTQ4LDQ3OC4wOTY4ODMgTDI0MC4zODQ2NDgsNDcwLjkwNzc3MyBMMjQyLjcyNjA1NSw0NzMuMzM0MDk4IEMyNDMuODE4NzExLDQ3NC40NjYzODIgMjQ1LjY4Njc3Nyw0NzMuNjY0MzQ3IDI0NS42ODY3NzcsNDcyLjA2MzI3MyBMMjQ1LjY4Njc3Nyw0NjMuMjIyOTE0IEMyNDUuNjg3NSw0NjIuNTYwOTE3IDI0NS4xNzAwNzgsNDYyLjAyNDcyOSAyNDQuNTMxMjUsNDYyLjAyNDcyOSBaIiBpZD0iQ29tYmluZWQtU2hhcGUiPjwvcGF0aD4KICAgICAgICA8L2c+CiAgICA8L2c+Cjwvc3ZnPg==';
861 +
862 + $this->_libraryPage = add_menu_page( __( 'Visualizer', 'visualizer' ), __( 'Visualizer', 'visualizer' ), 'edit_posts', Visualizer_Plugin::NAME, array( $this, 'renderLibraryPage' ), $svg_base64_icon, 99.7666 );
863 +
864 + add_submenu_page(
865 + Visualizer_Plugin::NAME,
866 + __( 'Chart Library', 'visualizer' ),
867 + __( 'Chart Library', 'visualizer' ),
868 + 'edit_posts',
869 + 'admin.php?page=' . Visualizer_Plugin::NAME
870 + );
871 + add_submenu_page(
872 + Visualizer_Plugin::NAME,
873 + __( 'Add New Chart', 'visualizer' ),
874 + __( 'Add New Chart', 'visualizer' ),
875 + 'edit_posts',
876 + 'admin.php?page=' . Visualizer_Plugin::NAME . '&vaction=addnew'
877 + );
878 +
879 + $this->_settingsPage = add_submenu_page(
880 + Visualizer_Plugin::NAME,
881 + __( 'Settings', 'visualizer' ),
882 + __( 'Settings', 'visualizer' ),
883 + 'manage_options',
884 + 'viz-settings',
885 + array( $this, 'renderSettingsPage' )
886 + );
887 +
888 + $this->_supportPage = add_submenu_page(
889 + Visualizer_Plugin::NAME,
890 + __( 'Support', 'visualizer' ),
891 + __( '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>',
892 + 'edit_posts',
893 + 'viz-support',
894 + array( $this, 'renderSupportPage' )
895 + );
896 +
897 + remove_submenu_page( Visualizer_Plugin::NAME, Visualizer_Plugin::NAME );
898 +
899 + add_action( "load-{$this->_libraryPage}", array( $this, 'addScreenOptions' ) );
900 + add_action( 'admin_footer', array( $this, 'handleGetProSubMenu' ) );
535 901 }
536 902
537 903 /**
904 + * Handle get pro plugin submenu.
905 + */
906 + public function handleGetProSubMenu() {
907 + if ( ! Visualizer_Module::is_pro() ) {
908 + ?>
909 + <style type="text/css">
910 + #toplevel_page_visualizer ul.wp-submenu li:last-child {
911 + background: #FF7E65;
912 + font-size: 14px;
913 + font-weight: 600;
914 + color: #fff;
915 + }
916 + #toplevel_page_visualizer ul.wp-submenu li:last-child > a > span {
917 + color: #fff !important;
918 + }
919 + #toplevel_page_visualizer ul.wp-submenu li:last-child > a:hover {
920 + box-shadow: inherit;
921 + }
922 + </style>
923 + <?php
924 + if ( get_option( 'visualizer_fresh_install', false ) ) {
925 + ?>
926 + <style type="text/css">
927 + #toplevel_page_visualizer ul.wp-submenu li.wp-first-item + li + li + li {
928 + display: none;
929 + }
930 + </style>
931 + <?php
932 + }
933 + }
934 + }
935 +
936 + /**
937 + * Adds the screen options for pagination.
938 + */
939 + public function addScreenOptions() {
940 + $screen = get_current_screen();
941 +
942 + // bail if it's some other page.
943 + if ( ! is_object( $screen ) || $screen->id !== $this->_libraryPage ) {
944 + return;
945 + }
946 +
947 + $args = array(
948 + 'label' => __( 'Number of charts per page:', 'visualizer' ),
949 + 'default' => 6,
950 + 'option' => 'visualizer_library_per_page',
951 + );
952 + add_screen_option( 'per_page', $args );
953 + }
954 +
955 + /**
956 + * Returns the screen option for pagination.
957 + */
958 + public function setScreenOptions( $status, $option, $value ) {
959 + if ( 'visualizer_library_per_page' === $option ) {
960 + return $value;
961 + }
962 + }
963 +
964 + /**
965 + * Adds the display filters to be used in the meta_query while displaying the charts.
966 + */
967 + private function getDisplayFilters( &$query_args ) {
968 + $query = array();
969 + global $sitepress;
970 + if ( Visualizer_Module::is_pro() && ( function_exists( 'icl_get_languages' ) && $sitepress instanceof \SitePress ) ) {
971 + $current_lang = icl_get_current_language();
972 + if ( in_array( $current_lang, array( 'all', icl_get_default_language() ), true ) ) {
973 + $query[] = array(
974 + 'key' => 'chart_lang',
975 + 'compare' => 'NOT EXISTS',
976 + );
977 + } else {
978 + $query[] = array(
979 + 'key' => 'chart_lang',
980 + 'value' => $current_lang,
981 + 'compare' => '=',
982 + );
983 + }
984 + }
985 +
986 + // add chart type filter to the query arguments
987 + $type = filter_input( INPUT_GET, 'type' );
988 + if ( $type && in_array( $type, Visualizer_Plugin::getChartTypes(), true ) ) {
989 + $query[] = array(
990 + 'key' => Visualizer_Plugin::CF_CHART_TYPE,
991 + 'value' => $type,
992 + 'compare' => '=',
993 + );
994 + }
995 +
996 + // add chart library filter to the query arguments
997 + $library = filter_input( INPUT_GET, 'library' );
998 + if ( $library ) {
999 + $query[] = array(
1000 + 'key' => Visualizer_Plugin::CF_CHART_LIBRARY,
1001 + 'value' => $library,
1002 + 'compare' => '=',
1003 + );
1004 + }
1005 +
1006 + // add date filter to the query arguments
1007 + $date = filter_input( INPUT_GET, 'date' );
1008 + $possible = array_keys( Visualizer_Plugin::getSupportedDateFilter() );
1009 + if ( $date && in_array( $date, $possible, true ) ) {
1010 + $query_args['date_query'] = array(
1011 + 'after' => "$date -1 day",
1012 + 'inclusive' => true,
1013 + );
1014 + }
1015 +
1016 + // add source filter to the query arguments
1017 + $source = filter_input( INPUT_GET, 'source' );
1018 + if ( $source ) {
1019 + $source = ucwords( $source );
1020 + $source = "Visualizer_Source_{$source}";
1021 + $query[] = array(
1022 + 'key' => Visualizer_Plugin::CF_SOURCE,
1023 + 'value' => $source,
1024 + 'compare' => '=',
1025 + );
1026 + }
1027 +
1028 + $query_args['meta_query'] = $query;
1029 +
1030 + $orderby = filter_input( INPUT_GET, 'orderby' );
1031 + $order = filter_input( INPUT_GET, 'order' );
1032 + if ( $orderby ) {
1033 + $query_args['order_by'] = $orderby;
1034 + }
1035 + $query_args['order'] = empty( $order ) ? 'desc' : $order;
1036 + }
1037 +
1038 + /**
538 1039 * Get the instance of WP_Query that fetches the charts as per the given criteria.
539 1040 */
540 1041 private function getQuery() {
541 1042 static $q;
@@ -554,29 +1055,34 @@
554 1055 'default' => 1,
555 1056 ),
556 1057 )
557 1058 );
1059 +
1060 + $per_page = 6;
1061 + $screen = get_current_screen();
1062 + if ( $screen ) {
1063 + // retrieve the per_page option
1064 + $screen_option = $screen->get_option( 'per_page', 'option' );
1065 + // retrieve the value stored for the current user
1066 + $user = get_current_user_id();
1067 + $per_page = get_user_meta( $user, $screen_option, true );
1068 + if ( empty( $per_page ) || $per_page < 1 ) {
1069 + // nothing set, get the default value
1070 + $per_page = $screen->get_option( 'per_page', 'default' );
1071 + }
1072 + }
1073 +
558 1074 // the initial query arguments to fetch charts
559 1075 $query_args = array(
560 1076 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
561 - 'posts_per_page' => 6,
1077 + 'posts_per_page' => $per_page,
562 1078 'paged' => $page,
563 1079 );
564 - // add chart type filter to the query arguments
565 - $filter = filter_input( INPUT_GET, 'type' );
566 - if ( $filter && in_array( $filter, Visualizer_Plugin::getChartTypes() ) ) {
567 - $query_args['meta_query'] = array(
568 - array(
569 - 'key' => Visualizer_Plugin::CF_CHART_TYPE,
570 - 'value' => $filter,
571 - 'compare' => '=',
572 - ),
573 - );
574 - } else {
575 - $filter = 'all';
576 - }
1080 +
1081 + $this->getDisplayFilters( $query_args );
1082 +
577 1083 // Added by Ash/Upwork
578 - $filterByMeta = filter_input( INPUT_GET, 's', FILTER_SANITIZE_STRING );
1084 + $filterByMeta = ! empty( $_GET['s'] ) ? sanitize_text_field( wp_unslash( $_GET['s'] ) ) : null; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
579 1085 if ( $filterByMeta ) {
580 1086 $query = array(
581 1087 'key' => Visualizer_Plugin::CF_SETTINGS,
582 1088 'value' => $filterByMeta,
@@ -585,13 +1091,108 @@
585 1091 $meta = isset( $query_args['meta_query'] ) ? $query_args['meta_query'] : array();
586 1092 $meta[] = $query;
587 1093 $query_args['meta_query'] = $meta;
588 1094 }
589 - $q = new WP_Query( $query_args );
1095 + $q = new WP_Query( apply_filters( 'visualizer_query_args', $query_args ) );
590 1096 return $q;
591 1097 }
592 1098
593 1099 /**
1100 + * Renders support page.
1101 + *
1102 + * @since 3.3.0
1103 + *
1104 + * @access public
1105 + */
1106 + public function renderSupportPage() {
1107 + wp_enqueue_style( 'visualizer-upsell', VISUALIZER_ABSURL . 'css/upsell.css', array(), Visualizer_Plugin::VERSION );
1108 + include_once VISUALIZER_ABSPATH . '/templates/support.php';
1109 + }
1110 +
1111 + /**
1112 + * Enqueues scripts/styles for the Settings page.
1113 + *
1114 + * @access public
1115 + */
1116 + public function enqueueSettingsScripts( string $hook_suffix ): void {
1117 + if ( $this->_settingsPage !== $hook_suffix ) {
1118 + return;
1119 + }
1120 + wp_enqueue_style( 'wp-color-picker' );
1121 + wp_enqueue_script( 'wp-color-picker' );
1122 + }
1123 +
1124 + /**
1125 + * Renders the global style settings page.
1126 + *
1127 + * @access public
1128 + */
1129 + public function renderSettingsPage(): void {
1130 + if ( ! current_user_can( 'manage_options' ) ) {
1131 + wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'visualizer' ) );
1132 + }
1133 + $settings = self::getGlobalSettings();
1134 + include_once VISUALIZER_ABSPATH . '/templates/global-settings.php';
1135 + }
1136 +
1137 + /**
1138 + * Returns the global style settings option.
1139 + *
1140 + * @return array<string, string>
1141 + * @access public
1142 + * @static
1143 + */
1144 + public static function getGlobalSettings(): array {
1145 + $defaults = array(
1146 + 'color_primary' => '',
1147 + 'color_secondary' => '',
1148 + 'apply_existing' => '0',
1149 + );
1150 + $saved = get_option( self::OPTION_GLOBAL_SETTINGS, array() );
1151 + return wp_parse_args( $saved, $defaults );
1152 + }
1153 +
1154 + /**
1155 + * Handles saving of the global style settings.
1156 + *
1157 + * @access public
1158 + */
1159 + public function saveGlobalSettings(): void {
1160 + if ( ! current_user_can( 'manage_options' ) ) {
1161 + wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'visualizer' ) );
1162 + }
1163 +
1164 + check_admin_referer( 'visualizer_save_global_settings' );
1165 +
1166 + $color_primary = sanitize_hex_color( wp_unslash( $_POST['visualizer_color_primary'] ?? '' ) );
1167 + $color_secondary = sanitize_hex_color( wp_unslash( $_POST['visualizer_color_secondary'] ?? '' ) );
1168 + $apply_existing = ! empty( $_POST['visualizer_apply_existing'] ) ? '1' : '0';
1169 + $clear = ! empty( $_POST['visualizer_clear_settings'] );
1170 +
1171 + if ( $clear ) {
1172 + delete_option( self::OPTION_GLOBAL_SETTINGS );
1173 + } else {
1174 + $settings = array(
1175 + 'color_primary' => $color_primary ? $color_primary : '',
1176 + 'color_secondary' => $color_secondary ? $color_secondary : '',
1177 + 'apply_existing' => $apply_existing,
1178 + );
1179 + update_option( self::OPTION_GLOBAL_SETTINGS, $settings );
1180 + }
1181 +
1182 + wp_safe_redirect(
1183 + add_query_arg(
1184 + array(
1185 + 'page' => 'viz-settings',
1186 + 'updated' => $clear ? 'cleared' : 'true',
1187 + ),
1188 + admin_url( 'admin.php' )
1189 + )
1190 + );
1191 + exit;
1192 + }
1193 +
1194 + /**
594 1195 * Renders visualizer library page.
595 1196 *
596 1197 * @since 1.0.0
597 1198 *
@@ -614,9 +1215,9 @@
614 1215 )
615 1216 );
616 1217 // add chart type filter to the query arguments
617 1218 $filter = filter_input( INPUT_GET, 'type' );
618 - if ( ! ( $filter && in_array( $filter, Visualizer_Plugin::getChartTypes() ) ) ) {
1219 + if ( ! ( $filter && in_array( $filter, Visualizer_Plugin::getChartTypes(), true ) ) ) {
619 1220 $filter = 'all';
620 1221 }
621 1222
622 1223 $css = '';
@@ -637,12 +1238,27 @@
637 1238 if ( ! empty( $atts['settings'] ) ) {
638 1239 $settings = apply_filters( $atts['settings'], $settings, $chart->ID, $type );
639 1240 }
640 1241
641 - unset( $settings['height'], $settings['width'] );
1242 + if ( $settings ) {
1243 + unset( $settings['height'], $settings['width'], $settings['chartArea'] );
1244 + }
1245 +
642 1246 $series = apply_filters( Visualizer_Plugin::FILTER_GET_CHART_SERIES, get_post_meta( $chart->ID, Visualizer_Plugin::CF_SERIES, true ), $chart->ID, $type );
643 - $data = apply_filters( Visualizer_Plugin::FILTER_GET_CHART_DATA, unserialize( html_entity_decode( $chart->post_content ) ), $chart->ID, $type );
1247 + $data = self::get_chart_data( $chart, $type );
644 1248
1249 + // Set default setting series when add manual chart data - Only during first creation.
1250 + if ( isset( $settings['series'] ) && ! empty( $series ) ) {
1251 + $count_series = count( $series ) - count( $settings['series'] );
1252 + if ( $count_series > 1 ) {
1253 + $clone_last_series = end( $settings['series'] );
1254 + $clone_last_series = array_fill_keys( array_keys( $clone_last_series ), '' );
1255 + foreach ( range( 1, $count_series ) as $item ) {
1256 + $settings['series'][] = $clone_last_series;
1257 + }
1258 + }
1259 + }
1260 +
645 1261 $library = $this->load_chart_type( $chart->ID );
646 1262
647 1263 $id = 'visualizer-' . $chart->ID;
648 1264 $arguments = $this->get_inline_custom_css( $id, $settings );
@@ -650,11 +1266,19 @@
650 1266 $css .= $arguments[0];
651 1267 $settings = $arguments[1];
652 1268 }
653 1269
1270 + if ( isset( $settings['controls']['ui']['labelStacking'] ) ) {
1271 + unset( $settings['controls']['ui']['labelStacking'] );
1272 + }
1273 + if ( isset( $settings['controls']['ui']['orientation'] ) ) {
1274 + unset( $settings['controls']['ui']['orientation'] );
1275 + }
1276 +
654 1277 // add chart to the array
655 1278 $charts[ $id ] = array(
656 1279 'id' => $chart->ID,
1280 + 'title' => $chart->post_title,
657 1281 'type' => $type,
658 1282 'series' => $series,
659 1283 'settings' => $settings,
660 1284 'data' => $data,
@@ -659,11 +1283,15 @@
659 1283 'settings' => $settings,
660 1284 'data' => $data,
661 1285 'library' => $library,
662 1286 );
1287 + if ( 'd3' === $library ) {
1288 + $charts[ $id ]['code'] = get_post_meta( $chart->ID, Visualizer_Module_AIBuilder::CF_D3_CODE, true );
1289 + }
663 1290 }
664 1291 // enqueue charts array
665 1292 $ajaxurl = admin_url( 'admin-ajax.php' );
1293 +
666 1294 wp_localize_script(
667 1295 'visualizer-library',
668 1296 'visualizer',
669 1297 array(
@@ -670,26 +1298,39 @@
670 1298 'language' => $this->get_language(),
671 1299 'map_api_key' => get_option( 'visualizer-map-api-key' ),
672 1300 'charts' => $charts,
673 1301 'urls' => array(
674 - 'base' => add_query_arg( 'vpage', false ),
675 - 'create' => add_query_arg(
676 - array(
677 - 'action' => Visualizer_Plugin::ACTION_CREATE_CHART,
678 - 'library' => 'yes',
679 - 'type' => isset( $_GET['type'] ) ? $_GET['type'] : '',
680 - ),
681 - $ajaxurl
1302 + 'base' => esc_url( add_query_arg( array( 'vpage' => false, 'vaction' => false ) ) ),
1303 + 'create' => esc_url(
1304 + add_query_arg(
1305 + array(
1306 + 'action' => Visualizer_Plugin::ACTION_CREATE_CHART,
1307 + 'library' => 'yes',
1308 + 'type' => isset( $_GET['type'] ) ? $_GET['type'] : '',
1309 + 'chart-library' => isset( $_GET['chart-library'] ) ? $_GET['chart-library'] : '',
1310 + 'vaction' => false,
1311 + ),
1312 + $ajaxurl
1313 + )
682 1314 ),
683 - 'edit' => add_query_arg(
684 - array(
685 - 'action' => Visualizer_Plugin::ACTION_EDIT_CHART,
686 - 'library' => 'yes',
687 - ),
688 - $ajaxurl
1315 + 'edit' => esc_url(
1316 + add_query_arg(
1317 + array(
1318 + 'action' => Visualizer_Plugin::ACTION_EDIT_CHART,
1319 + 'library' => 'yes',
1320 + 'vaction' => false,
1321 + ),
1322 + $ajaxurl
1323 + )
689 1324 ),
690 1325 ),
691 1326 'page_type' => 'library',
1327 + 'is_front' => false,
1328 + 'i10n' => array(
1329 + 'copied' => __( 'The shortcode has been copied to your clipboard. Hit Ctrl-V/Cmd-V to paste it.', 'visualizer' ),
1330 + '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' ),
1331 + ),
1332 + 'is_pro_user' => Visualizer_Module::is_pro(),
692 1333 )
693 1334 );
694 1335 // render library page
695 1336 $render = new Visualizer_Render_Library();
@@ -698,9 +1339,9 @@
698 1339 $render->types = self::_getChartTypesLocalized( false, false, false, true );
699 1340 $render->custom_css = $css;
700 1341 $render->pagination = paginate_links(
701 1342 array(
702 - 'base' => add_query_arg( 'vpage', '%#%' ),
1343 + 'base' => add_query_arg( array( 'vpage' => '%#%', 'vaction' => false ) ),
703 1344 'format' => '',
704 1345 'current' => $page,
705 1346 'total' => $query->max_num_pages,
706 1347 'type' => 'array',
@@ -705,8 +1346,9 @@
705 1346 'total' => $query->max_num_pages,
706 1347 'type' => 'array',
707 1348 )
708 1349 );
1350 +
709 1351 $render->render();
710 1352 }
711 1353
712 1354 /**
@@ -721,14 +1363,14 @@
721 1363 *
722 1364 * @return array Updated array of action links.
723 1365 */
724 1366 public function getPluginActionLinks( $links, $file ) {
725 - if ( $file == plugin_basename( VISUALIZER_BASEFILE ) ) {
1367 + if ( $file === plugin_basename( VISUALIZER_BASEFILE ) ) {
726 1368 array_unshift(
727 1369 $links,
728 1370 sprintf(
729 1371 '<a href="%s">%s</a>',
730 - admin_url( 'upload.php?page=' . Visualizer_Plugin::NAME ),
1372 + admin_url( 'admin.php?page=' . Visualizer_Plugin::NAME ),
731 1373 esc_html__( 'Library', 'visualizer' )
732 1374 )
733 1375 );
734 1376 }
@@ -748,21 +1390,268 @@
748 1390 *
749 1391 * @return array Updated array of plugin meta links.
750 1392 */
751 1393 public function getPluginMetaLinks( $plugin_meta, $plugin_file ) {
752 - if ( $plugin_file == plugin_basename( VISUALIZER_BASEFILE ) ) {
1394 + if ( Visualizer_Module::is_pro() ) {
1395 + return $plugin_meta;
1396 + }
1397 +
1398 + // Also suppress the upsell when Pro is installed but not currently active.
1399 + if ( ! function_exists( 'get_plugins' ) ) {
1400 + require_once ABSPATH . 'wp-admin/includes/plugin.php';
1401 + }
1402 + foreach ( array_keys( get_plugins() ) as $installed_plugin ) {
1403 + if ( false !== strpos( $installed_plugin, 'visualizer-pro' ) ) {
1404 + return $plugin_meta;
1405 + }
1406 + }
1407 +
1408 + if ( $plugin_file === plugin_basename( VISUALIZER_BASEFILE ) ) {
1409 + $is_black_friday = apply_filters( 'themeisle_sdk_is_black_friday_sale', false );
753 1410 // knowledge base link
754 1411 $plugin_meta[] = sprintf(
755 - '<a href="https://github.com/codeinwp/visualizer/wiki" target="_blank">%s</a>',
756 - esc_html__( 'Knowledge Base', 'visualizer' )
1412 + '<a href="' . VISUALIZER_MAIN_DOC . '" target="_blank">%s</a>',
1413 + esc_html__( 'Docs', 'visualizer' )
757 1414 );
758 - // flattr link
759 - $plugin_meta[] = sprintf(
760 - '<a style="color:red" href="https://themeisle.com/plugins/visualizer-charts-and-graphs-pro-addon/" target="_blank">%s</a>',
761 - esc_html__( 'Pro Addon', 'visualizer' )
762 - );
1415 + if ( ! $is_black_friday ) {
1416 + // flattr link
1417 + $plugin_meta[] = sprintf(
1418 + '<a style="color:red" href="' . tsdk_utmify( Visualizer_Plugin::PRO_TEASER_URL, 'pluginrow' ) . '" target="_blank">%s</a>',
1419 + esc_html__( 'Get Visualizer Pro', 'visualizer' )
1420 + );
1421 + }
763 1422 }
764 1423
765 1424 return $plugin_meta;
766 1425 }
767 1426
1427 + /**
1428 + * Returns true when premium chart types should be enabled for the current user.
1429 + *
1430 + * Pro 1.9.0+ hooks the 'visualizer_is_pro' filter and returns true only when
1431 + * the license is valid. Pre-1.9.0 Pro defined a Visualizer_Pro class instead;
1432 + * we detect that as a fallback so those legacy installs still work.
1433 + *
1434 + * Passing false as the filter default ensures the constant VISUALIZER_PRO
1435 + * (which is set to true whenever the Pro class exists, regardless of license
1436 + * state) cannot bypass the license check.
1437 + *
1438 + * @return bool
1439 + */
1440 + public static function proFeaturesEnabled() {
1441 + $is_pro_filter = apply_filters( 'visualizer_is_pro', false );
1442 +
1443 + // Pro 1.9.0+: filter is hooked and returns true only with a valid license.
1444 + if ( $is_pro_filter ) {
1445 + return true;
1446 + }
1447 +
1448 + // Pro is installed (active) but the license check above did not pass.
1449 + // Do NOT fall through to the legacy "old user" path — that path exists
1450 + // only for sites that never had Pro installed. If Pro is present but
1451 + // the license is inactive we should lock, regardless of chart history.
1452 + if ( class_exists( 'Visualizer_Pro', false ) ) {
1453 + return false;
1454 + }
1455 +
1456 + // No Pro installed at all: grant legacy access to existing users so
1457 + // their charts are not suddenly broken when they upgrade the free plugin.
1458 + return 'yes' === get_option( 'visualizer-new-user' ) ? false : true;
1459 + }
1460 +
1461 + /**
1462 + * Multilingual Support.
1463 + *
1464 + * @param int $chart_id Chart ID.
1465 + * @return bool Default false
1466 + */
1467 + public function addMultilingualSupport( $chart_id ) {
1468 + global $sitepress;
1469 + if ( Visualizer_Module::is_pro() ) {
1470 + return;
1471 + }
1472 + if ( function_exists( 'icl_get_languages' ) && $sitepress instanceof \SitePress ) {
1473 + $language = icl_get_languages();
1474 + $current_lang = icl_get_current_language();
1475 + $default_lang = icl_get_default_language();
1476 + $post_info = wpml_get_language_information( null, $chart_id );
1477 + $translations = array();
1478 + if ( ! empty( $post_info ) && ( $default_lang === $post_info['language_code'] ) ) {
1479 + $trid = $sitepress->get_element_trid( $chart_id, 'post_' . Visualizer_Plugin::CPT_VISUALIZER );
1480 + $translations = $sitepress->get_element_translations( $trid );
1481 + }
1482 + if ( empty( $translations ) ) {
1483 + return;
1484 + }
1485 + ?>
1486 + <hr><div class="visualizer-languages-list only-pro">
1487 + <?php
1488 + foreach ( $language as $lang ) {
1489 + $lang_code = $lang['code'];
1490 + if ( $current_lang !== $lang_code ) {
1491 + ?>
1492 + <a href="javascript:;">
1493 + <img src="<?php echo esc_url( $lang['country_flag_url'] ); ?>" alt="<?php echo esc_attr( $lang['translated_name'] ); ?>">
1494 + </a>
1495 + <?php
1496 + }
1497 + }
1498 + ?>
1499 + <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>
1500 + </div>
1501 + <?php
1502 + }
1503 + }
1504 +
1505 + /**
1506 + * Check chart is enabled or not.
1507 + *
1508 + * @param string $type Chart ID.
1509 + *
1510 + * @return bool Default false
1511 + */
1512 + public static function checkChartStatus( $type ) {
1513 + $types = self::_getChartTypesLocalized( false, false, false, true );
1514 + if ( isset( $types[ $type ] ) ) {
1515 + return ! empty( $types[ $type ]['enabled'] );
1516 + }
1517 + return false;
1518 + }
1519 +
1520 + /**
1521 + * Get the survey metadata.
1522 + *
1523 + * @param array $data The data for survey in Formbricks format.
1524 + * @param string $page_slug The slug of the loaded page.
1525 + *
1526 + * @return array The survey metadata.
1527 + */
1528 + public function get_survey_metadata( $data, $page_slug ) {
1529 + $install_date = get_option( 'visualizer_install', time() );
1530 + $install_days_number = intval( ( time() - $install_date ) / DAY_IN_SECONDS );
1531 +
1532 + $license_status = apply_filters( 'product_visualizer_license_status', 'invalid' );
1533 + $license_plan = apply_filters( 'product_visualizer_license_plan', false );
1534 + $license_key = apply_filters( 'product_visualizer_license_key', false );
1535 +
1536 + $plugin_data = get_plugin_data( VISUALIZER_BASEFILE, false, false );
1537 + $plugin_version = '';
1538 +
1539 + if ( ! empty( $plugin_data['Version'] ) ) {
1540 + $plugin_version = $plugin_data['Version'];
1541 + }
1542 +
1543 + $count_charts_cache_key = 'visualizer_count_charts';
1544 + $charts_number = get_transient( $count_charts_cache_key );
1545 +
1546 + if ( false === $charts_number ) {
1547 + $charts_number = $this->count_charts( 100 );
1548 + set_transient( $count_charts_cache_key, $charts_number, 100 === $charts_number ? WEEK_IN_SECONDS : 6 * HOUR_IN_SECONDS );
1549 + } else {
1550 + $charts_number = strval( $charts_number );
1551 + }
1552 +
1553 + $data = array(
1554 + 'environmentId' => 'cltef8cut1s7wyyfxy3rlxzs5',
1555 + 'attributes' => array(
1556 + 'free_version' => $plugin_version,
1557 + 'pro_version' => defined( 'VISUALIZER_PRO_VERSION' ) ? VISUALIZER_PRO_VERSION : '',
1558 + 'license_status' => $license_status,
1559 + 'install_days_number' => $install_days_number,
1560 + 'charts_number' => $charts_number,
1561 + ),
1562 + );
1563 +
1564 + if ( ! empty( $license_plan ) ) {
1565 + $data['attributes']['plan'] = $license_plan;
1566 + }
1567 +
1568 + if ( ! empty( $license_key ) ) {
1569 + $data['attributes']['license_key'] = apply_filters( 'themeisle_sdk_secret_masking', $license_key );
1570 + }
1571 +
1572 + return $data;
1573 + }
1574 +
1575 + /**
1576 + * Count the charts.
1577 + *
1578 + * @param int $limit The count limit (optional).
1579 + *
1580 + * @return int The number of charts.
1581 + */
1582 + public function count_charts( $limit = -1 ) {
1583 + $args = array(
1584 + 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
1585 + 'post_status' => 'publish',
1586 + 'posts_per_page' => $limit,
1587 + 'fields' => 'ids',
1588 + );
1589 +
1590 + $query = new WP_Query( $args);
1591 + return $query->post_count;
1592 + }
1593 +
1594 + /**
1595 + * Set the black friday data.
1596 + *
1597 + * @param array $configs The configuration array for the loaded products.
1598 + * @return array
1599 + */
1600 + public function add_black_friday_data( $configs ) {
1601 + $config = $configs['default'];
1602 +
1603 + $message = __( 'Database queries, private charts, auto-sync. Go beyond basic charts. Exclusively for existing Visualizer users.', 'visualizer' );
1604 + $cta_label = __( 'Get Visualizer Pro', 'visualizer' );
1605 +
1606 + $plan = apply_filters( 'product_visualizer_license_plan', 0 );
1607 + $license = apply_filters( 'product_visualizer_license_key', false );
1608 + $status = apply_filters( 'product_visualizer_license_status', false );
1609 + $pro_product_slug = defined( 'VISUALIZER_PRO_BASEFILE' ) ? basename( dirname( VISUALIZER_PRO_BASEFILE ) ) : '';
1610 +
1611 + $is_pro = 'valid' === $status;
1612 + $is_expired = 'expired' === $status || 'active-expired' === $status;
1613 +
1614 + if ( $is_pro ) {
1615 + // translators: %s is the discount percentage.
1616 + $config['plugin_meta_message'] = sprintf( __( 'Black Friday Sale - up to %s off', 'visualizer' ), '30%' );
1617 + // translators: %1$s - discount, %2$s - discount.
1618 + $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%' );
1619 + $cta_label = __( 'See your options', 'visualizer' );
1620 + } elseif ( $is_expired ) {
1621 + // translators: %s is the discount percentage.
1622 + $config['upgrade_menu_text'] = sprintf( __( 'BF Sale - %s off', 'visualizer' ), '50%' );
1623 + // translators: %s is the discount percentage.
1624 + $config['plugin_meta_message'] = sprintf( __( 'Black Friday Sale - %s off', 'visualizer' ), '50%' );
1625 + $message = __( 'Your Visualizer Pro features are still here, just locked. Renew at a reduced rate this week.', 'visualizer' );
1626 + $cta_label = __( 'Reactivate now', 'visualizer' );
1627 + } else {
1628 + // translators: %s is the discount percentage.
1629 + $config['plugin_meta_message'] = sprintf( __( 'Black Friday Sale - %s off', 'visualizer' ), '60%' );
1630 + $config['title'] = __( 'Visualizer Pro: 60% off this week', 'visualizer' );
1631 + // translators: %s is the discount percentage.
1632 + $config['upgrade_menu_text'] = sprintf( __( 'BF Sale - %s off', 'visualizer' ), '60%' );
1633 + }
1634 +
1635 + $url_params = array(
1636 + 'utm_term' => $is_pro ? 'plan-' . $plan : 'free',
1637 + 'lkey' => ! empty( $license ) ? $license : false,
1638 + 'expired' => $is_expired ? '1' : false,
1639 + );
1640 +
1641 + if ( ( $is_pro || $is_expired ) && ! empty( $pro_product_slug ) ) {
1642 + $config['plugin_meta_targets'] = array( $pro_product_slug );
1643 + }
1644 +
1645 + $config['message'] = $message;
1646 + $config['cta_label'] = $cta_label;
1647 +
1648 + $config['sale_url'] = add_query_arg(
1649 + $url_params,
1650 + tsdk_translate_link( tsdk_utmify( 'https://themeisle.link/vizualizer-bf', 'bfcm', 'visualizer' ) )
1651 + );
1652 +
1653 + $configs[ VISUALIZER_DIRNAME ] = $config;
1654 +
1655 + return $configs;
1656 + }
768 1657 }