PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.7.7
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.7.7
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 3.10.4 All 148 releases
← All changes | classes/Visualizer/Module/Chart.php +130 -417 4.0.73.7.7 View file →
@@ -69,8 +69,9 @@
69 69
70 70 $this->_addAjaxAction( Visualizer_Plugin::ACTION_SAVE_FILTER_QUERY, 'saveFilter' );
71 71
72 72 $this->_addFilter( 'visualizer_get_sidebar', 'getSidebar', 10, 2 );
73 +
73 74 }
74 75
75 76 /**
76 77 * Generates the HTML of the sidebar for the chart.
@@ -107,10 +108,11 @@
107 108 */
108 109 public function setJsonSchedule() {
109 110 check_ajax_referer( Visualizer_Plugin::ACTION_JSON_SET_SCHEDULE . Visualizer_Plugin::VERSION, 'security' );
110 111
111 - $chart_id = isset( $_POST['chart'] ) ? filter_var(
112 - $_POST['chart'],
112 + $chart_id = filter_input(
113 + INPUT_POST,
114 + 'chart',
113 115 FILTER_VALIDATE_INT,
114 116 array(
115 117 'options' => array(
116 118 'min_range' => 1,
@@ -115,18 +117,14 @@
115 117 'options' => array(
116 118 'min_range' => 1,
117 119 ),
118 120 )
119 - ) : false;
121 + );
120 122
121 123 if ( ! $chart_id ) {
122 124 wp_send_json_error();
123 125 }
124 126
125 - if ( ! self::can_edit_chart( $chart_id ) ) {
126 - wp_send_json_error( array( 'msg' => esc_html__( 'You do not have permission to perform this action.', 'visualizer' ) ), 403 );
127 - }
128 -
129 127 $time = filter_input(
130 128 INPUT_POST,
131 129 'time',
132 130 FILTER_VALIDATE_INT,
@@ -136,22 +134,8 @@
136 134 ),
137 135 )
138 136 );
139 137
140 - if ( Visualizer_Module::is_pro() ) {
141 - $is_woocommerce_report = filter_input(
142 - INPUT_POST,
143 - 'is_woocommerce_report',
144 - FILTER_VALIDATE_BOOLEAN
145 - );
146 -
147 - if ( $is_woocommerce_report ) {
148 - update_post_meta( $chart_id, Visualizer_Plugin::CF_IS_WOOCOMMERCE_SOURCE, true );
149 - } else {
150 - delete_post_meta( $chart_id, Visualizer_Plugin::CF_IS_WOOCOMMERCE_SOURCE );
151 - }
152 - }
153 -
154 138 delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_SCHEDULE );
155 139
156 140 if ( -1 < $time ) {
157 141 add_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_SCHEDULE, $time );
@@ -172,12 +156,8 @@
172 156 */
173 157 public function getJsonRoots() {
174 158 check_ajax_referer( Visualizer_Plugin::ACTION_JSON_GET_ROOTS . Visualizer_Plugin::VERSION, 'security' );
175 159
176 - if ( ! current_user_can( 'edit_posts' ) ) {
177 - wp_send_json_error( array( 'msg' => esc_html__( 'You do not have permission to perform this action.', 'visualizer' ) ) );
178 - }
179 -
180 160 $params = wp_parse_args( $_POST['params'] );
181 161
182 162 $source = new Visualizer_Source_Json( $params );
183 163
@@ -198,18 +178,13 @@
198 178 */
199 179 public function getJsonData() {
200 180 check_ajax_referer( Visualizer_Plugin::ACTION_JSON_GET_DATA . Visualizer_Plugin::VERSION, 'security' );
201 181
202 - if ( ! current_user_can( 'edit_posts' ) ) {
203 - wp_send_json_error( array( 'msg' => esc_html__( 'You do not have permission to perform this action.', 'visualizer' ) ) );
204 - }
205 -
206 182 $params = wp_parse_args( $_POST['params'] );
207 183
208 184 $chart_id = $params['chart'];
209 185
210 - $chart = $chart_id ? get_post( $chart_id ) : null;
211 - if ( ! $chart || Visualizer_Plugin::CPT_VISUALIZER !== $chart->post_type || ! current_user_can( 'edit_post', $chart_id ) ) {
186 + if ( empty( $chart_id ) ) {
212 187 wp_die();
213 188 }
214 189
215 190 $source = new Visualizer_Source_Json( $params );
@@ -234,12 +209,12 @@
234 209 public function setJsonData() {
235 210 check_ajax_referer( Visualizer_Plugin::ACTION_JSON_SET_DATA . Visualizer_Plugin::VERSION, 'security' );
236 211
237 212 $params = $_POST;
238 - $chart_id = isset( $_GET['chart'] ) ? absint( $_GET['chart'] ) : 0;
213 + $chart_id = $_GET['chart'];
239 214
240 - if ( ! self::can_edit_chart( $chart_id ) ) {
241 - wp_die( esc_html__( 'You do not have permission to perform this action.', 'visualizer' ), '', array( 'response' => 403 ) );
215 + if ( empty( $chart_id ) ) {
216 + wp_die();
242 217 }
243 218
244 219 $chart = get_post( $chart_id );
245 220
@@ -270,16 +245,8 @@
270 245 if ( ! empty( $params['paging'] ) ) {
271 246 add_post_meta( $chart->ID, Visualizer_Plugin::CF_JSON_PAGING, $params['paging'] );
272 247 }
273 248
274 - if ( Visualizer_Module::is_pro() ) {
275 - if ( ! empty( $params['vz_woo_source'] ) ) {
276 - update_post_meta( $chart->ID, Visualizer_Plugin::CF_JSON_WOOCOMMERCE_SOURCE, $params['vz_woo_source'] );
277 - } else {
278 - delete_post_meta( $chart->ID, Visualizer_Plugin::CF_JSON_WOOCOMMERCE_SOURCE );
279 - }
280 - }
281 -
282 249 $time = filter_input(
283 250 INPUT_POST,
284 251 'time',
285 252 FILTER_VALIDATE_INT,
@@ -307,9 +274,9 @@
307 274 $render->data = json_encode( $source->getRawData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ) );
308 275 $render->series = json_encode( $source->getSeries() );
309 276 $render->render();
310 277
311 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
278 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
312 279 }
313 280
314 281
315 282 /**
@@ -321,14 +288,8 @@
321 288 *
322 289 * @access public
323 290 */
324 291 public function getCharts() {
325 - check_ajax_referer( Visualizer_Plugin::ACTION_GET_CHARTS, 'nonce' );
326 -
327 - if ( ! current_user_can( 'edit_posts' ) ) {
328 - wp_send_json_error( array( 'msg' => esc_html__( 'You do not have permission to perform this action.', 'visualizer' ) ), 403 );
329 - }
330 -
331 292 $query_args = array(
332 293 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
333 294 'posts_per_page' => 9,
334 295 'paged' => filter_input(
@@ -342,11 +303,8 @@
342 303 ),
343 304 )
344 305 ),
345 306 );
346 - if ( ! current_user_can( 'edit_others_posts' ) ) {
347 - $query_args['author'] = get_current_user_id();
348 - }
349 307 $filter = filter_input( INPUT_GET, 's', FILTER_SANITIZE_STRING );
350 308 if ( empty( $filter ) ) {
351 309 // 'filter' is from the modal from the add media button.
352 310 $filter = filter_input( INPUT_GET, 'filter', FILTER_SANITIZE_STRING );
@@ -394,13 +352,13 @@
394 352 * @since 1.0.0
395 353 *
396 354 * @access private
397 355 *
398 - * @param WP_Post|null $chart The chart object.
356 + * @param WP_Post $chart The chart object.
399 357 *
400 358 * @return array The array of chart data.
401 359 */
402 - private function _getChartArray( $chart = null ) {
360 + private function _getChartArray( WP_Post $chart = null ) {
403 361 if ( is_null( $chart ) ) {
404 362 $chart = $this->_chart;
405 363 }
406 364 $type = get_post_meta( $chart->ID, Visualizer_Plugin::CF_CHART_TYPE, true );
@@ -422,13 +380,8 @@
422 380 }
423 381
424 382 $date_formats = Visualizer_Source::get_date_formats_if_exists( $series, $data );
425 383
426 - $code = '';
427 - if ( 'd3' === $library ) {
428 - $code = get_post_meta( $chart->ID, Visualizer_Module_AIBuilder::CF_D3_CODE, true );
429 - }
430 -
431 384 return array(
432 385 'type' => $type,
433 386 'series' => $series,
434 387 'settings' => $settings,
@@ -433,9 +386,8 @@
433 386 'series' => $series,
434 387 'settings' => $settings,
435 388 'data' => $data,
436 389 'library' => $library,
437 - 'code' => $code,
438 390 'css' => $css,
439 391 'date_formats' => $date_formats,
440 392 );
441 393 }
@@ -452,9 +404,9 @@
452 404 public static function _sendResponse( $results ) {
453 405 header( 'Content-type: application/json' );
454 406 nocache_headers();
455 407 echo json_encode( $results );
456 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
408 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
457 409 }
458 410
459 411 /**
460 412 * Deletes a chart from database.
@@ -465,14 +417,16 @@
465 417 * @access public
466 418 */
467 419 public function deleteChart() {
468 420 $is_post = $_SERVER['REQUEST_METHOD'] === 'POST';
469 - $input = $is_post ? $_POST : $_GET;
421 + $input_method = $is_post ? INPUT_POST : INPUT_GET;
470 422 $chart_id = $success = false;
471 - $nonce = isset( $input['nonce'] ) && wp_verify_nonce( $input['nonce'] );
472 - if ( $nonce ) {
473 - $chart_id = isset( $input['chart'] ) ? filter_var(
474 - $input['chart'],
423 + $nonce = wp_verify_nonce( filter_input( $input_method, 'nonce' ) );
424 + $capable = current_user_can( 'delete_posts' );
425 + if ( $nonce && $capable ) {
426 + $chart_id = filter_input(
427 + $input_method,
428 + 'chart',
475 429 FILTER_VALIDATE_INT,
476 430 array(
477 431 'options' => array(
478 432 'min_range' => 1,
@@ -477,34 +431,16 @@
477 431 'options' => array(
478 432 'min_range' => 1,
479 433 ),
480 434 )
481 - ) : false;
435 + );
482 436 if ( $chart_id ) {
483 437 $chart = get_post( $chart_id );
484 - $success = $chart
485 - && $chart->post_type === Visualizer_Plugin::CPT_VISUALIZER
486 - && (
487 - current_user_can( 'delete_post', $chart_id )
488 - || ( (int) $chart->post_author === get_current_user_id() && current_user_can( 'delete_posts' ) )
489 - );
438 + $success = $chart && $chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
490 439 }
491 440 }
492 441 if ( $success ) {
493 - global $sitepress;
494 - if ( Visualizer_Module::is_pro() && ( function_exists( 'icl_get_languages' ) && $sitepress instanceof \SitePress ) ) {
495 - $trid = $sitepress->get_element_trid( $chart_id, 'post_' . Visualizer_Plugin::CPT_VISUALIZER );
496 - $translations = $sitepress->get_element_translations( $trid );
497 - if ( ! empty( $translations ) ) {
498 - foreach ( $translations as $translated_post ) {
499 - wp_delete_post( $translated_post->element_id, true );
500 - }
501 - } else {
502 - wp_delete_post( $chart_id, true );
503 - }
504 - } else {
505 - wp_delete_post( $chart_id, true );
506 - }
442 + wp_delete_post( $chart_id, true );
507 443 }
508 444 if ( $is_post ) {
509 445 self::_sendResponse(
510 446 array(
@@ -552,12 +488,8 @@
552 488 *
553 489 * @access public
554 490 */
555 491 public function renderChartPages() {
556 - if ( ! current_user_can( 'edit_posts' ) ) {
557 - wp_die( __( 'You do not have permission to access this page.', 'visualizer' ) );
558 - }
559 -
560 492 defined( 'IFRAME_REQUEST' ) || define( 'IFRAME_REQUEST', 1 );
561 493 if ( ! defined( 'ET_BUILDER_PRODUCT_VERSION' ) && function_exists( 'et_get_theme_version' ) ) {
562 494 define( 'ET_BUILDER_PRODUCT_VERSION', et_get_theme_version() );
563 495 }
@@ -564,85 +496,40 @@
564 496 // Set current screen for the render chart.
565 497 set_current_screen( 'visualizer_render_chart' );
566 498 // check chart, if chart not exists, will create new one and redirects to the same page with proper chart id
567 499 $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
568 - if ( ! empty( $_POST ) ) {
569 - $_POST = map_deep( $_POST, 'wp_strip_all_tags' );
570 - }
571 - $chart = $chart_id ? get_post( $chart_id ) : null;
572 - if ( $chart && ! self::can_edit_chart( $chart_id ) ) {
573 - wp_die( esc_html__( 'You do not have permission to access this page.', 'visualizer' ), '', array( 'response' => 403 ) );
574 - }
575 - if ( ! $chart_id || ! $chart || $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ) {
576 - if ( empty( $_GET['lang'] ) || empty( $_GET['parent_chart_id'] ) ) {
577 - $this->deleteOldCharts();
578 - $default_type = isset( $_GET['type'] ) && ! empty( $_GET['type'] ) ? $_GET['type'] : 'line';
579 - $chart_status = Visualizer_Module_Admin::checkChartStatus( $default_type );
580 - if ( ! $chart_status ) {
581 - $default_type = 'line';
582 - }
583 - $source = new Visualizer_Source_Csv( VISUALIZER_ABSPATH . DIRECTORY_SEPARATOR . 'samples' . DIRECTORY_SEPARATOR . $default_type . '.csv' );
584 - $source->fetch();
585 - $chart_id = wp_insert_post(
500 + if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ) {
501 + $this->deleteOldCharts();
502 + $default_type = isset( $_GET['type'] ) && ! empty( $_GET['type'] ) ? $_GET['type'] : 'line';
503 + $source = new Visualizer_Source_Csv( VISUALIZER_ABSPATH . DIRECTORY_SEPARATOR . 'samples' . DIRECTORY_SEPARATOR . $default_type . '.csv' );
504 + $source->fetch();
505 + $chart_id = wp_insert_post(
506 + array(
507 + 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
508 + 'post_title' => 'Visualization',
509 + 'post_author' => get_current_user_id(),
510 + 'post_status' => 'auto-draft',
511 + 'post_content' => $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ),
512 + )
513 + );
514 + if ( $chart_id && ! is_wp_error( $chart_id ) ) {
515 + add_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_TYPE, $default_type );
516 + add_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, 1 );
517 + add_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
518 + add_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
519 + add_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_LIBRARY, '' );
520 + add_post_meta(
521 + $chart_id,
522 + Visualizer_Plugin::CF_SETTINGS,
586 523 array(
587 - 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
588 - 'post_title' => 'Visualization',
589 - 'post_author' => get_current_user_id(),
590 - 'post_status' => 'auto-draft',
591 - 'post_content' => $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ),
524 + 'focusTarget' => 'datum',
592 525 )
593 526 );
594 - if ( $chart_id && ! is_wp_error( $chart_id ) ) {
595 - add_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_TYPE, $default_type );
596 - add_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, 1 );
597 - add_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
598 - add_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
599 - add_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_LIBRARY, '' );
600 - add_post_meta(
601 - $chart_id,
602 - Visualizer_Plugin::CF_SETTINGS,
603 - array(
604 - 'focusTarget' => 'datum',
605 - )
606 - );
607 -
608 - do_action( 'visualizer_pro_new_chart_defaults', $chart_id );
609 - }
610 - } else {
611 - $parent_chart_id = filter_var( $_GET['parent_chart_id'], FILTER_VALIDATE_INT );
612 - $success = false;
613 - if ( $parent_chart_id && self::can_edit_chart( $parent_chart_id ) ) {
614 - $parent_chart = get_post( $parent_chart_id );
615 - $success = $parent_chart && $parent_chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
616 - }
617 - if ( $success ) {
618 - $new_chart_id = wp_insert_post(
619 - array(
620 - 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
621 - 'post_title' => 'Visualization',
622 - 'post_author' => get_current_user_id(),
623 - 'post_status' => $parent_chart->post_status,
624 - 'post_content' => $parent_chart->post_content,
625 - )
626 - );
627 -
628 - if ( is_wp_error( $new_chart_id ) ) {
629 - do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Error while cloning chart %d = %s', $parent_chart_id, print_r( $new_chart_id, true ) ), 'error', __FILE__, __LINE__ );
630 - } else {
631 - $post_meta = get_post_meta( $parent_chart_id );
632 - $chart_id = $new_chart_id;
633 - foreach ( $post_meta as $key => $value ) {
634 - if ( strpos( $key, 'visualizer-' ) !== false ) {
635 - add_post_meta( $new_chart_id, $key, self::maybe_decode_content( $value[0] ) );
636 - }
637 - }
638 - }
639 - }
640 527 do_action( 'visualizer_pro_new_chart_defaults', $chart_id );
641 528 }
642 529 wp_redirect( esc_url_raw( add_query_arg( 'chart', (int) $chart_id ) ) );
643 530
644 - if ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) {
531 + if ( defined( 'WP_TESTS_DOMAIN' ) ) {
645 532 wp_die();
646 533 }
647 534 exit();
648 535 }
@@ -738,9 +625,9 @@
738 625 default:
739 626 // this should never happen.
740 627 break;
741 628 }
742 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
629 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
743 630 }
744 631
745 632 /**
746 633 * Load code editor assets.
@@ -770,9 +657,9 @@
770 657 wp_register_script( 'visualizer-codemirror-matchbrackets', '//codemirror.net/addon/edit/matchbrackets.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
771 658 wp_register_script( 'visualizer-codemirror-closebrackets', '//codemirror.net/addon/edit/closebrackets.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
772 659 wp_register_script( 'visualizer-codemirror-sql', '//codemirror.net/mode/sql/sql.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
773 660 wp_register_script( 'visualizer-codemirror-sql-hint', '//codemirror.net/addon/hint/sql-hint.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
774 - wp_register_script( 'visualizer-codemirror-hint', '//codemirror.net/addon/hint/show-hint.js', array( 'visualizer-codemirror-sql', 'visualizer-codemirror-sql-hint', 'visualizer-codemirror-placeholder', 'visualizer-codemirror-matchbrackets', 'visualizer-codemirror-closebrackets' ), Visualizer_Plugin::VERSION );
661 + wp_register_script( 'visualizer-codemirror-hint', '//codemirror.net/addon/hint/show-hint.js', array( 'visualizer-codemirror-sql', 'visualizer-codemirror-sql-hint', 'visualizer-codemirror-placeholder', 'visualizer-codemirror-matchbrackets', 'visualizer-codemirror-closebrackets' ), Visualizer_Plugin::VERSION );
775 662 wp_register_style( 'visualizer-codemirror-core', '//codemirror.net/lib/codemirror.css', array(), Visualizer_Plugin::VERSION );
776 663 wp_register_style( 'visualizer-codemirror-hint', '//codemirror.net/addon/hint/show-hint.css', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
777 664
778 665 wp_enqueue_script( 'visualizer-codemirror-hint' );
@@ -786,9 +673,9 @@
786 673 'lineWrapping' => true,
787 674 'dragDrop' => false,
788 675 'matchBrackets' => true,
789 676 'autoCloseBrackets' => true,
790 - 'extraKeys' => array( 'Shift-Space' => 'autocomplete' ),
677 + 'extraKeys' => array( 'Ctrl-Space' => 'autocomplete' ),
791 678 'hintOptions' => array( 'tables' => $table_col_mapping ),
792 679 ),
793 680 )
794 681 );
@@ -817,17 +704,14 @@
817 704 /**
818 705 * Handle data and settings page
819 706 */
820 707 private function _handleDataAndSettingsPage() {
708 + if ( isset( $_POST['map_api_key'] ) ) {
709 + update_option( 'visualizer-map-api-key', $_POST['map_api_key'] );
710 + }
711 +
821 712 if ( $_SERVER['REQUEST_METHOD'] === 'POST' && isset( $_GET['nonce'] ) && wp_verify_nonce( $_GET['nonce'] ) ) {
822 - $is_canceled = isset( $_POST['cancel'] ) && 1 === intval( $_POST['cancel'] );
823 - $is_newly_created = $this->_chart->post_status === 'auto-draft';
824 -
825 - if ( isset( $_POST['map_api_key'] ) && current_user_can( 'manage_options' ) ) {
826 - update_option( 'visualizer-map-api-key', sanitize_text_field( wp_unslash( $_POST['map_api_key'] ) ) );
827 - }
828 -
829 - if ( $is_newly_created && ! $is_canceled ) {
713 + if ( $this->_chart->post_status === 'auto-draft' ) {
830 714 $this->_chart->post_status = 'publish';
831 715
832 716 // ensure that a revision is not created. If a revision is created it will have the proper data and the parent of the revision will have default data.
833 717 // we do not want any difference in data so disable revisions temporarily.
@@ -835,16 +719,10 @@
835 719
836 720 wp_update_post( $this->_chart->to_array() );
837 721 }
838 722 // save meta data only when it is NOT being canceled.
839 - if ( ! $is_canceled ) {
840 - $post_settings = $_POST;
841 - $existing = get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
842 - if ( isset( $existing['colors'] ) && is_array( $existing['colors'] ) && ! isset( $post_settings['colors'] ) ) {
843 - $post_settings['colors'] = $existing['colors'];
844 - }
845 - $post_settings = $this->sanitizeSettings( $post_settings );
846 - update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, $post_settings );
723 + if ( ! ( isset( $_POST['cancel'] ) && 1 === intval( $_POST['cancel'] ) ) ) {
724 + update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, $_POST );
847 725
848 726 // we will keep a parameter called 'internal_title' that will be set to the given title or, if empty, the chart ID
849 727 // this will help in searching with the chart id.
850 728 $settings = get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
@@ -858,14 +736,8 @@
858 736 if ( empty( $title ) ) {
859 737 $title = $this->_chart->ID;
860 738 }
861 739 $settings['internal_title'] = $title;
862 - $settings_label = isset( $settings['pieResidueSliceLabel'] ) ? $settings['pieResidueSliceLabel'] : '';
863 - if ( empty( $settings_label ) ) {
864 - $settings['pieResidueSliceLabel'] = esc_html__( 'Other', 'visualizer' );
865 - } else {
866 - $settings['pieResidueSliceLabel'] = $settings_label;
867 - }
868 740 update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, $settings );
869 741 }
870 742 $render = new Visualizer_Render_Page_Send();
871 743 $render->text = sprintf( '[visualizer id="%d"]', $this->_chart->ID );
@@ -891,9 +763,8 @@
891 763 wp_enqueue_style( 'visualizer-frame' );
892 764 wp_enqueue_script( 'visualizer-preview' );
893 765 wp_enqueue_script( 'visualizer-chosen' );
894 766 wp_enqueue_script( 'visualizer-render' );
895 - wp_enqueue_code_editor( array( 'type' => 'application/json' ) );
896 767
897 768 if ( Visualizer_Module::can_show_feature( 'simple-editor' ) ) {
898 769 wp_enqueue_script( 'visualizer-editor-simple' );
899 770 wp_localize_script(
@@ -901,10 +772,12 @@
901 772 'visualizer1',
902 773 array(
903 774 'ajax' => array(
904 775 'url' => admin_url( 'admin-ajax.php' ),
905 - 'nonces' => array(),
906 - 'actions' => array(),
776 + 'nonces' => array(
777 + ),
778 + 'actions' => array(
779 + ),
907 780 ),
908 781 )
909 782 );
910 783 }
@@ -915,15 +788,13 @@
915 788 'visualizer-render',
916 789 'visualizer',
917 790 array(
918 791 'l10n' => array(
919 - 'invalid_source' => esc_html__( 'The URL you entered is invalid. Please enter a valid URL.', 'visualizer' ),
920 - 'loading' => esc_html__( 'Loading...', 'visualizer' ),
921 - 'json_error' => esc_html__( 'An error occured in fetching data.', 'visualizer' ),
922 - 'select_columns' => esc_html__( 'Please select a few columns to include in the chart.', 'visualizer' ),
923 - 'save_settings' => __( 'You have modified the chart\'s settings. To modify the source/data again, you must save this chart and reopen it for editing. If you continue without saving the chart, you may lose your changes.', 'visualizer' ),
924 - 'copied' => __( 'The data has been copied to your clipboard. Hit Ctrl-V/Cmd-V in your spreadsheet editor to paste the data.', 'visualizer' ),
925 - 'invalid_format' => esc_html__( 'This format pattern is not supported in the series settings field. Use the Manual Configuration option instead.', 'visualizer' ),
792 + 'invalid_source' => esc_html__( 'You have entered an invalid URL. Please provide a valid URL.', 'visualizer' ),
793 + 'loading' => esc_html__( 'Loading...', 'visualizer' ),
794 + 'json_error' => esc_html__( 'An error occured in fetching data.', 'visualizer' ),
795 + 'select_columns' => esc_html__( 'Please select a few columns to include in the chart.', 'visualizer' ),
796 + 'save_settings' => __( 'You have modified the chart\'s settings. To modify the source/data again, you must save this chart and reopen it for editing. If you continue without saving the chart, you may lose your changes.', 'visualizer' ),
926 797 ),
927 798 'charts' => array(
928 799 'canvas' => $data,
929 800 'id' => $this->_chart->ID,
@@ -954,9 +825,8 @@
954 825 'page_type' => 'chart',
955 826 'json_tag_separator' => Visualizer_Source_Json::TAG_SEPARATOR,
956 827 'json_tag_separator_view' => Visualizer_Source_Json::TAG_SEPARATOR_VIEW,
957 828 'is_front' => false,
958 - 'rest_base' => get_rest_url( null, 'wc/v3/reports/' ),
959 829 )
960 830 );
961 831
962 832 $render = new Visualizer_Render_Page_Data();
@@ -967,10 +837,11 @@
967 837 if ( filter_input( INPUT_GET, 'library', FILTER_VALIDATE_BOOLEAN ) ) {
968 838 $render->button = filter_input( INPUT_GET, 'action' ) === Visualizer_Plugin::ACTION_EDIT_CHART
969 839 ? esc_html__( 'Save Chart', 'visualizer' )
970 840 : esc_html__( 'Create Chart', 'visualizer' );
971 -
972 - $render->cancel_button = esc_html__( 'Cancel', 'visualizer' );
841 + if ( filter_input( INPUT_GET, 'action' ) === Visualizer_Plugin::ACTION_EDIT_CHART ) {
842 + $render->cancel_button = esc_html__( 'Cancel', 'visualizer' );
843 + }
973 844 } else {
974 845 $render->button = esc_attr__( 'Insert Chart', 'visualizer' );
975 846 }
976 847
@@ -993,12 +864,12 @@
993 864 * @access private
994 865 */
995 866 private function _handleTypesPage() {
996 867 // process post request
997 - if ( $_SERVER['REQUEST_METHOD'] === 'POST' && wp_verify_nonce( filter_input( INPUT_POST, 'nonce' ), 'visualizer-upload-data' ) ) {
868 + if ( $_SERVER['REQUEST_METHOD'] === 'POST' && wp_verify_nonce( filter_input( INPUT_POST, 'nonce' ) ) ) {
998 869 $type = filter_input( INPUT_POST, 'type' );
999 870 $library = filter_input( INPUT_POST, 'chart-library' );
1000 - if ( Visualizer_Module_Admin::checkChartStatus( $type ) ) {
871 + if ( in_array( $type, Visualizer_Plugin::getChartTypes(), true ) ) {
1001 872 if ( empty( $library ) ) {
1002 873 // library cannot be empty.
1003 874 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, 'Chart library empty while creating the chart! Aborting...', 'error', __FILE__, __LINE__ );
1004 875 return;
@@ -1034,35 +905,8 @@
1034 905 wp_iframe( array( $render, 'render' ) );
1035 906 }
1036 907
1037 908 /**
1038 - * Sanitize settings data from the request.
1039 - *
1040 - * @param array<string, mixed> $post_data The POST data to sanitize.
1041 - * @return array<string, mixed> The sanitized settings data.
1042 - */
1043 - private function sanitizeSettings( $post_data ): array {
1044 - $chart_img = '';
1045 - if ( isset( $post_data['chart-img'] ) ) {
1046 - $chart_img = wp_unslash( $post_data['chart-img'] );
1047 - unset( $post_data['chart-img'] );
1048 - }
1049 -
1050 - $post_data = map_deep(
1051 - $post_data,
1052 - 'sanitize_textarea_field'
1053 - );
1054 -
1055 - // The value is a client-side canvas export; keep it only when it is a
1056 - // base64 image data URI so nothing else is ever stored unsanitized.
1057 - if ( is_string( $chart_img ) && preg_match( '#^data:image/(png|jpeg|webp);base64,[A-Za-z0-9+/ ]+=*$#', $chart_img ) ) {
1058 - $post_data['chart-img'] = $chart_img;
1059 - }
1060 -
1061 - return $post_data;
1062 - }
1063 -
1064 - /**
1065 909 * Renders flattr script in the iframe <head>
1066 910 *
1067 911 * @since 1.4.2
1068 912 * @action admin_head
@@ -1077,81 +921,8 @@
1077 921 * Processes the CSV that is sent in the request as a string.
1078 922 *
1079 923 * @since 3.2.0
1080 924 */
1081 - /**
1082 - * Determines whether a remote URL serves an XLSX file.
1083 - *
1084 - * Used as a fallback when the URL path has no recognisable file extension
1085 - * (e.g. SharePoint, signed S3 URLs, or "download?id=…" endpoints).
1086 - *
1087 - * Uses the shared remote-fetch policy to block non-public destinations,
1088 - * and streams the response to a temp file so no body data is held in memory
1089 - * regardless of whether the server honours the Range header.
1090 - *
1091 - * The check relies on the ZIP magic number (PK\x03\x04) that every XLSX
1092 - * file begins with, making it immune to misleading Content-Type headers
1093 - * such as application/octet-stream. Content-Type is used as a last-resort
1094 - * fallback only when the temp file is empty (e.g. a HEAD-only server).
1095 - *
1096 - * @access private
1097 - * @param string $url The remote URL to probe.
1098 - * @return bool TRUE if the file appears to be XLSX, FALSE otherwise.
1099 - */
1100 - private static function _url_is_xlsx( $url ) {
1101 - $tmpfile = wp_tempnam( 'visualizer_xlsx_probe' );
1102 - if ( ! $tmpfile ) {
1103 - return false;
1104 - }
1105 -
1106 - $response = Visualizer_Remote_Fetch::request(
1107 - $url,
1108 - array(
1109 - 'timeout' => 10,
1110 - 'user-agent' => 'WordPress/' . get_bloginfo( 'version' ),
1111 - 'headers' => array( 'Range' => 'bytes=0-3' ),
1112 - 'stream' => true,
1113 - 'filename' => $tmpfile,
1114 - 'limit_response_size' => 4,
1115 - )
1116 - );
1117 -
1118 - if ( is_wp_error( $response ) ) {
1119 - @unlink( $tmpfile ); // phpcs:ignore WordPress.PHP.NoSilencedErrors
1120 - return false;
1121 - }
1122 -
1123 - $magic = '';
1124 - // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen
1125 - $fh = @fopen( $tmpfile, 'rb' ); // phpcs:ignore WordPress.PHP.NoSilencedErrors
1126 - if ( $fh ) {
1127 - $magic = fread( $fh, 4 ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fread
1128 - fclose( $fh ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose
1129 - }
1130 - @unlink( $tmpfile ); // phpcs:ignore WordPress.PHP.NoSilencedErrors
1131 -
1132 - if ( strlen( $magic ) >= 4 ) {
1133 - // XLSX (and all ZIP-based Office formats) start with PK\x03\x04.
1134 - return $magic === "PK\x03\x04";
1135 - }
1136 -
1137 - // Last resort: server returned an empty body (e.g. ignored Range and
1138 - // returned only headers). Check Content-Type from the same response.
1139 - // application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
1140 - return false !== strpos(
1141 - wp_remote_retrieve_header( $response, 'content-type' ),
1142 - 'spreadsheetml'
1143 - );
1144 - }
1145 -
1146 - /**
1147 - * Parses a raw CSV string or editor payload and returns a source object.
1148 - *
1149 - * @access private
1150 - * @param string $data The raw CSV data string.
1151 - * @param string $editor_type The editor type ('text' or 'tabular').
1152 - * @return Visualizer_Source|null The populated source object, or null on failure.
1153 - */
1154 925 private function handleCSVasString( $data, $editor_type ) {
1155 926 $source = null;
1156 927
1157 928 switch ( $editor_type ) {
@@ -1166,9 +937,9 @@
1166 937 continue;
1167 938 }
1168 939 $row = explode( ',', $row );
1169 940 $row = array_map(
1170 - function ( $r ) {
941 + function( $r ) {
1171 942 return '' === $r ? ' ' : $r;
1172 943 },
1173 944 $row
1174 945 );
@@ -1217,9 +988,9 @@
1217 988 foreach ( $types as $type ) {
1218 989 if ( empty( $type ) ) {
1219 990 $exclude[] = $index;
1220 991 }
1221 - ++$index;
992 + $index++;
1222 993 }
1223 994
1224 995 // when N headers are being renamed, the number of headers increases by N
1225 996 // because of the way datatable duplicates header information
@@ -1277,16 +1048,11 @@
1277 1048 public function uploadData() {
1278 1049 // if this is being called internally from pro and VISUALIZER_DO_NOT_DIE is set.
1279 1050 // otherwise, assume this is a normal web request.
1280 1051 $can_die = ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE );
1281 - // $can_die also gates the capability checks below, so VISUALIZER_DO_NOT_DIE must stay internal-only (never set from request input or globally).
1282 1052
1283 - // validate nonce; capability check applies to web requests only, not trusted internal calls.
1284 - if (
1285 - ! isset( $_GET['nonce'] ) ||
1286 - ! wp_verify_nonce( $_GET['nonce'], 'visualizer-upload-data' ) ||
1287 - ( $can_die && ! current_user_can( 'edit_posts' ) )
1288 - ) {
1053 + // validate nonce
1054 + if ( ! isset( $_GET['nonce'] ) || ! wp_verify_nonce( $_GET['nonce'] ) ) {
1289 1055 if ( ! $can_die ) {
1290 1056 return;
1291 1057 }
1292 1058 status_header( 403 );
@@ -1295,15 +1061,9 @@
1295 1061
1296 1062 // check chart, if chart exists
1297 1063 // do not use filter_input as it does not work for phpunit test cases, use filter_var instead
1298 1064 $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
1299 - $chart = $chart_id ? get_post( $chart_id ) : null;
1300 - if (
1301 - ! $chart_id ||
1302 - ! $chart ||
1303 - $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ||
1304 - ( $can_die && ! current_user_can( 'edit_post', $chart_id ) )
1305 - ) {
1065 + if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ) {
1306 1066 if ( ! $can_die ) {
1307 1067 return;
1308 1068 }
1309 1069 status_header( 400 );
@@ -1344,30 +1104,16 @@
1344 1104 delete_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE );
1345 1105
1346 1106 $source = null;
1347 1107 $render = new Visualizer_Render_Page_Update();
1348 -
1349 - $remote_data = false;
1350 - if ( isset( $_POST['remote_data'] ) && function_exists( 'wp_http_validate_url' ) ) {
1351 - $remote_data = wp_http_validate_url( $_POST['remote_data'] );
1352 - }
1353 - if ( false !== $remote_data ) {
1354 - $remote_ext = strtolower( pathinfo( parse_url( $remote_data, PHP_URL_PATH ), PATHINFO_EXTENSION ) );
1355 - if ( 'xlsx' === $remote_ext || ( 'csv' !== $remote_ext && self::_url_is_xlsx( $remote_data ) ) ) {
1356 - $source = new Visualizer_Source_Xlsx_Remote( $remote_data );
1357 - } else {
1358 - $source = new Visualizer_Source_Csv_Remote( $remote_data );
1359 - }
1108 + if ( isset( $_POST['remote_data'] ) && filter_var( $_POST['remote_data'], FILTER_VALIDATE_URL ) ) {
1109 + $source = new Visualizer_Source_Csv_Remote( $_POST['remote_data'] );
1360 1110 if ( isset( $_POST['vz-import-time'] ) ) {
1361 - apply_filters( 'visualizer_pro_chart_schedule', $chart_id, $remote_data, $_POST['vz-import-time'] );
1111 + apply_filters( 'visualizer_pro_chart_schedule', $chart_id, $_POST['remote_data'], $_POST['vz-import-time'] );
1362 1112 }
1363 - } elseif ( isset( $_FILES['local_data'] ) && $_FILES['local_data']['error'] === 0 ) {
1364 - $local_ext = strtolower( pathinfo( isset( $_FILES['local_data']['name'] ) ? $_FILES['local_data']['name'] : '', PATHINFO_EXTENSION ) );
1365 - if ( 'xlsx' === $local_ext ) {
1366 - $source = new Visualizer_Source_Xlsx( $_FILES['local_data']['tmp_name'] );
1367 - } else {
1368 - $source = new Visualizer_Source_Csv( $_FILES['local_data']['tmp_name'] );
1369 - }
1113 + // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
1114 + } elseif ( isset( $_FILES['local_data'] ) && $_FILES['local_data']['error'] == 0 ) {
1115 + $source = new Visualizer_Source_Csv( $_FILES['local_data']['tmp_name'] );
1370 1116 } elseif ( isset( $_POST['chart_data'] ) && strlen( $_POST['chart_data'] ) > 0 ) {
1371 1117 $source = $this->handleCSVasString( $_POST['chart_data'], $_POST['editor-type'] );
1372 1118 update_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR, $_POST['editor-type'] );
1373 1119 } elseif ( isset( $_POST['table_data'] ) && 'yes' === $_POST['table_data'] ) {
@@ -1374,10 +1120,10 @@
1374 1120 $source = $this->handleTabularData();
1375 1121 update_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR, $_POST['editor-type'] );
1376 1122 } else {
1377 1123 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'CSV file with chart data was not uploaded for chart %d.', $chart_id ), 'error', __FILE__, __LINE__ );
1378 - $render->message = esc_html__( 'No CSV file was received. Select a file and try uploading again.', 'visualizer' );
1379 - update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, esc_html__( 'No CSV file was received. Select a file and try uploading again.', 'visualizer' ) );
1124 + $render->message = esc_html__( 'CSV file with chart data was not uploaded. Please try again.', 'visualizer' );
1125 + update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, esc_html__( 'CSV file with chart data was not uploaded. Please try again.', 'visualizer' ) );
1380 1126 }
1381 1127
1382 1128 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Uploaded data for chart %d with source %s', $chart_id, print_r( $source, true ) ), 'debug', __FILE__, __LINE__ );
1383 1129
@@ -1384,10 +1130,10 @@
1384 1130 if ( $source ) {
1385 1131 if ( $source->fetch() ) {
1386 1132 $content = $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) );
1387 1133 $populate = true;
1388 - $json = self::decode_content( $content );
1389 - if ( is_array( $json ) ) {
1134 + if ( is_string( $content ) && is_array( unserialize( $content ) ) ) {
1135 + $json = unserialize( $content );
1390 1136 // if source exists, so should data. if source exists but data is blank, do not populate the chart.
1391 1137 // if we populate the data even if it is empty, the chart will show "Table has no columns".
1392 1138 if ( array_key_exists( 'source', $json ) && ! empty( $json['source'] ) && ( ! array_key_exists( 'data', $json ) || empty( $json['data'] ) ) ) {
1393 1139 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Not populating chart data as source exists (%s) but data is empty!', $json['source'] ), 'warn', __FILE__, __LINE__ );
@@ -1425,9 +1171,9 @@
1425 1171 $render->settings = json_encode( $settings );
1426 1172 } else {
1427 1173 $error = $source->get_error();
1428 1174 if ( empty( $error ) ) {
1429 - $error = esc_html__( 'The CSV file couldn\'t be read. Check that it\'s properly formatted and try again.', 'visualizer' );
1175 + $error = esc_html__( 'CSV file is broken or invalid. Please try again.', 'visualizer' );
1430 1176 }
1431 1177 $render->message = $error;
1432 1178 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( '%s for chart %d.', $error, $chart_id ), 'error', __FILE__, __LINE__ );
1433 1179 update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, $error );
@@ -1438,9 +1184,9 @@
1438 1184 $render->render();
1439 1185 if ( ! $can_die ) {
1440 1186 return;
1441 1187 }
1442 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
1188 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1443 1189 }
1444 1190
1445 1191 /**
1446 1192 * Clones the chart.
@@ -1451,11 +1197,12 @@
1451 1197 */
1452 1198 public function cloneChart() {
1453 1199 $chart_id = $success = false;
1454 1200 $nonce = isset( $_GET['nonce'] ) && wp_verify_nonce( $_GET['nonce'], Visualizer_Plugin::ACTION_CLONE_CHART );
1455 - if ( $nonce ) {
1201 + $capable = current_user_can( 'edit_posts' );
1202 + if ( $nonce && $capable ) {
1456 1203 $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
1457 - if ( $chart_id && self::can_edit_chart( $chart_id ) ) {
1204 + if ( $chart_id ) {
1458 1205 $chart = get_post( $chart_id );
1459 1206 $success = $chart && $chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
1460 1207 }
1461 1208 }
@@ -1475,9 +1222,9 @@
1475 1222 } else {
1476 1223 $post_meta = get_post_meta( $chart_id );
1477 1224 foreach ( $post_meta as $key => $value ) {
1478 1225 if ( strpos( $key, 'visualizer-' ) !== false ) {
1479 - add_post_meta( $new_chart_id, $key, self::maybe_decode_content( $value[0] ) );
1226 + add_post_meta( $new_chart_id, $key, maybe_unserialize( $value[0] ) );
1480 1227 }
1481 1228 }
1482 1229 $redirect = esc_url(
1483 1230 add_query_arg(
@@ -1486,18 +1233,17 @@
1486 1233 'type' => filter_input( INPUT_GET, 'type' ),
1487 1234 'vaction' => false,
1488 1235 ),
1489 1236 admin_url( 'admin.php' )
1490 - ),
1491 - null,
1492 - 'db'
1237 + )
1493 1238 );
1494 1239 }
1495 1240 }
1496 1241
1497 - if ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) {
1242 + if ( defined( 'WP_TESTS_DOMAIN' ) ) {
1498 1243 wp_die();
1499 1244 }
1245 +
1500 1246 wp_redirect( $redirect );
1501 1247 exit;
1502 1248 }
1503 1249
@@ -1509,25 +1255,28 @@
1509 1255 * @access public
1510 1256 */
1511 1257 public function exportData() {
1512 1258 check_ajax_referer( Visualizer_Plugin::ACTION_EXPORT_DATA . Visualizer_Plugin::VERSION, 'security' );
1513 - $chart_id = isset( $_GET['chart'] ) ? filter_var(
1514 - $_GET['chart'],
1515 - FILTER_VALIDATE_INT,
1516 - array(
1517 - 'options' => array(
1518 - 'min_range' => 1,
1519 - ),
1520 - )
1521 - ) : '';
1522 - if ( $chart_id && self::can_edit_chart( $chart_id ) ) {
1523 - $data = $this->_getDataAs( $chart_id, 'csv' );
1524 - if ( $data ) {
1525 - echo wp_send_json_success( $data );
1259 + $capable = current_user_can( 'edit_posts' );
1260 + if ( $capable ) {
1261 + $chart_id = isset( $_GET['chart'] ) ? filter_var(
1262 + $_GET['chart'],
1263 + FILTER_VALIDATE_INT,
1264 + array(
1265 + 'options' => array(
1266 + 'min_range' => 1,
1267 + ),
1268 + )
1269 + ) : '';
1270 + if ( $chart_id ) {
1271 + $data = $this->_getDataAs( $chart_id, 'csv' );
1272 + if ( $data ) {
1273 + echo wp_send_json_success( $data );
1274 + }
1526 1275 }
1527 1276 }
1528 1277
1529 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
1278 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1530 1279 }
1531 1280
1532 1281 /**
1533 1282 * Handles chart data page.
@@ -1551,11 +1300,10 @@
1551 1300 'visualizer-render',
1552 1301 'visualizer',
1553 1302 array(
1554 1303 'l10n' => array(
1555 - 'invalid_source' => esc_html__( 'The URL you entered is invalid. Please enter a valid URL.', 'visualizer' ),
1556 - 'loading' => esc_html__( 'Loading...', 'visualizer' ),
1557 - 'invalid_format' => esc_html__( 'This format pattern is not supported in the series settings field. To display percentages, use the Manual Configuration option instead.', 'visualizer' ),
1304 + 'invalid_source' => esc_html__( 'You have entered an invalid URL. Please provide a valid URL.', 'visualizer' ),
1305 + 'loading' => esc_html__( 'Loading...', 'visualizer' ),
1558 1306 ),
1559 1307 'charts' => array(
1560 1308 'canvas' => $data,
1561 1309 ),
@@ -1581,24 +1329,12 @@
1581 1329 */
1582 1330 public function getQueryData() {
1583 1331 check_ajax_referer( Visualizer_Plugin::ACTION_FETCH_DB_DATA . Visualizer_Plugin::VERSION, 'security' );
1584 1332
1585 - if ( ! current_user_can( 'administrator' ) ) {
1586 - wp_send_json_error( array( 'msg' => __( 'Action not allowed for this user.', 'visualizer' ) ) );
1587 - }
1588 - if ( ! is_super_admin() ) {
1589 - wp_send_json_error( array( 'msg' => __( 'Action not allowed for this user.', 'visualizer' ) ) );
1590 - }
1591 -
1592 - if ( ! Visualizer_Module::is_pro() ) {
1593 - wp_send_json_error( array( 'msg' => __( 'Feature is not available.', 'visualizer' ) ) );
1594 - }
1595 -
1596 1333 $params = wp_parse_args( $_POST['params'] );
1597 1334 $chart_id = filter_var( $params['chart_id'], FILTER_VALIDATE_INT );
1598 - $query = trim( $params['query'], ';' );
1599 1335
1600 - $source = new Visualizer_Source_Query( stripslashes( $query ), $chart_id, $params );
1336 + $source = new Visualizer_Source_Query( stripslashes( $params['query'] ), $chart_id, $params );
1601 1337 $html = $source->fetch( true );
1602 1338 $error = $source->get_error();
1603 1339 if ( ! empty( $error ) ) {
1604 1340 wp_send_json_error( array( 'msg' => $error ) );
@@ -1613,19 +1349,8 @@
1613 1349 */
1614 1350 public function saveQuery() {
1615 1351 check_ajax_referer( Visualizer_Plugin::ACTION_SAVE_DB_QUERY . Visualizer_Plugin::VERSION, 'security' );
1616 1352
1617 - if ( ! current_user_can( 'administrator' ) ) {
1618 - wp_send_json_error( array( 'msg' => __( 'Action not allowed for this user.', 'visualizer' ) ) );
1619 - }
1620 - if ( ! is_super_admin() ) {
1621 - wp_send_json_error( array( 'msg' => __( 'Action not allowed for this user.', 'visualizer' ) ) );
1622 - }
1623 -
1624 - if ( ! Visualizer_Module::is_pro() ) {
1625 - wp_send_json_error( array( 'msg' => __( 'Feature is not available.', 'visualizer' ) ) );
1626 - }
1627 -
1628 1353 $chart_id = filter_input(
1629 1354 INPUT_GET,
1630 1355 'chart',
1631 1356 FILTER_VALIDATE_INT,
@@ -1654,14 +1379,13 @@
1654 1379
1655 1380 $render = new Visualizer_Render_Page_Update();
1656 1381 if ( $chart_id ) {
1657 1382 $params = wp_parse_args( $_POST['params'] );
1658 - $query = trim( $params['query'], ';' );
1659 - $source = new Visualizer_Source_Query( stripslashes( $query ), $chart_id, $params );
1383 + $source = new Visualizer_Source_Query( stripslashes( $params['query'] ), $chart_id, $params );
1660 1384 $source->fetch( false );
1661 1385 $error = $source->get_error();
1662 1386 if ( empty( $error ) ) {
1663 - update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY, stripslashes( $query ) );
1387 + update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY, stripslashes( $params['query'] ) );
1664 1388 update_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
1665 1389 update_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
1666 1390 update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_SCHEDULE, $hours );
1667 1391 update_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, 0 );
@@ -1690,9 +1414,9 @@
1690 1414 }
1691 1415 }
1692 1416 $render->render();
1693 1417 if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
1694 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
1418 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1695 1419 }
1696 1420 }
1697 1421
1698 1422
@@ -1703,10 +1427,11 @@
1703 1427 */
1704 1428 public function saveFilter() {
1705 1429 check_ajax_referer( Visualizer_Plugin::ACTION_SAVE_FILTER_QUERY . Visualizer_Plugin::VERSION, 'security' );
1706 1430
1707 - $chart_id = isset( $_GET['chart'] ) ? filter_var(
1708 - $_GET['chart'],
1431 + $chart_id = filter_input(
1432 + INPUT_GET,
1433 + 'chart',
1709 1434 FILTER_VALIDATE_INT,
1710 1435 array(
1711 1436 'options' => array(
1712 1437 'min_range' => 1,
@@ -1711,18 +1436,14 @@
1711 1436 'options' => array(
1712 1437 'min_range' => 1,
1713 1438 ),
1714 1439 )
1715 - ) : false;
1440 + );
1716 1441
1717 - if ( ! self::can_edit_chart( $chart_id ) ) {
1718 - wp_send_json_error( array( 'msg' => esc_html__( 'You do not have permission to perform this action.', 'visualizer' ) ), 403 );
1719 - }
1720 -
1721 1442 $hours = filter_input(
1722 1443 INPUT_POST,
1723 1444 'refresh',
1724 - FILTER_VALIDATE_FLOAT,
1445 + FILTER_VALIDATE_INT,
1725 1446 array(
1726 1447 'options' => array(
1727 1448 'min_range' => -1,
1728 1449 'max_range' => apply_filters( 'visualizer_is_business', false ) ? PHP_INT_MAX : -1,
@@ -1729,9 +1450,9 @@
1729 1450 ),
1730 1451 )
1731 1452 );
1732 1453
1733 - if ( ! is_numeric( $hours ) ) {
1454 + if ( 0 !== $hours && empty( $hours ) ) {
1734 1455 $hours = -1;
1735 1456 }
1736 1457
1737 1458 do_action( 'visualizer_save_filter', $chart_id, $hours );
@@ -1736,9 +1457,9 @@
1736 1457
1737 1458 do_action( 'visualizer_save_filter', $chart_id, $hours );
1738 1459
1739 1460 if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
1740 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
1461 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1741 1462 }
1742 1463 }
1743 1464
1744 1465 /**
@@ -1746,9 +1467,9 @@
1746 1467 *
1747 1468 * @param string $base64_img Chart image.
1748 1469 * @param int $chart_id Chart ID.
1749 1470 * @param bool $save_attachment Save attachment.
1750 - * @return int Attachment ID, or 0 when no attachment was saved.
1471 + * @return attachment ID
1751 1472 */
1752 1473 public function save_chart_image( $base64_img, $chart_id, $save_attachment = true ) {
1753 1474 // Delete old chart image.
1754 1475 $old_attachment_id = get_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_IMAGE, true );
@@ -1763,15 +1484,11 @@
1763 1484 // Upload dir.
1764 1485 $upload_dir = wp_upload_dir();
1765 1486 $upload_path = str_replace( '/', DIRECTORY_SEPARATOR, $upload_dir['path'] ) . DIRECTORY_SEPARATOR;
1766 1487
1767 - $img = str_replace( 'data:image/png;base64,', '', (string) $base64_img );
1768 - $img = str_replace( ' ', '+', $img );
1769 - $decoded = base64_decode( $img, true );
1770 - // The value comes from an untrusted request; only write real PNG bytes to uploads.
1771 - if ( false === $decoded || 0 !== strncmp( $decoded, "\x89PNG\r\n\x1a\n", 8 ) ) {
1772 - return 0;
1773 - }
1488 + $img = str_replace( 'data:image/png;base64,', '', $base64_img );
1489 + $img = str_replace( ' ', '+', $img );
1490 + $decoded = base64_decode( $img );
1774 1491 $filename = 'visualization-' . $chart_id . '.png';
1775 1492 $file_type = 'image/png';
1776 1493 $hashed_filename = $filename;
1777 1494
@@ -1778,12 +1495,8 @@
1778 1495 // Save the image in the uploads directory.
1779 1496 require_once ABSPATH . '/wp-admin/includes/file.php';
1780 1497 \WP_Filesystem();
1781 1498 global $wp_filesystem;
1782 - if ( ! is_a( $wp_filesystem, 'WP_Filesystem_Base' ) ) {
1783 - $creds = request_filesystem_credentials( site_url() );
1784 - wp_filesystem( $creds );
1785 - }
1786 1499 $upload_file = $wp_filesystem->put_contents( $upload_path . $hashed_filename, $decoded );
1787 1500
1788 1501 // Insert new chart image.
1789 1502 $attachment = array(