PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.10.8
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.10.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 3.10.4 All 148 releases
← All changes | classes/Visualizer/Module/Chart.php +127 -319 4.0.73.10.8 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,
@@ -172,12 +170,8 @@
172 170 */
173 171 public function getJsonRoots() {
174 172 check_ajax_referer( Visualizer_Plugin::ACTION_JSON_GET_ROOTS . Visualizer_Plugin::VERSION, 'security' );
175 173
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 174 $params = wp_parse_args( $_POST['params'] );
181 175
182 176 $source = new Visualizer_Source_Json( $params );
183 177
@@ -198,18 +192,13 @@
198 192 */
199 193 public function getJsonData() {
200 194 check_ajax_referer( Visualizer_Plugin::ACTION_JSON_GET_DATA . Visualizer_Plugin::VERSION, 'security' );
201 195
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 196 $params = wp_parse_args( $_POST['params'] );
207 197
208 198 $chart_id = $params['chart'];
209 199
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 ) ) {
200 + if ( empty( $chart_id ) ) {
212 201 wp_die();
213 202 }
214 203
215 204 $source = new Visualizer_Source_Json( $params );
@@ -234,12 +223,12 @@
234 223 public function setJsonData() {
235 224 check_ajax_referer( Visualizer_Plugin::ACTION_JSON_SET_DATA . Visualizer_Plugin::VERSION, 'security' );
236 225
237 226 $params = $_POST;
238 - $chart_id = isset( $_GET['chart'] ) ? absint( $_GET['chart'] ) : 0;
227 + $chart_id = $_GET['chart'];
239 228
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 ) );
229 + if ( empty( $chart_id ) ) {
230 + wp_die();
242 231 }
243 232
244 233 $chart = get_post( $chart_id );
245 234
@@ -307,9 +296,9 @@
307 296 $render->data = json_encode( $source->getRawData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ) );
308 297 $render->series = json_encode( $source->getSeries() );
309 298 $render->render();
310 299
311 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
300 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
312 301 }
313 302
314 303
315 304 /**
@@ -321,14 +310,8 @@
321 310 *
322 311 * @access public
323 312 */
324 313 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 314 $query_args = array(
332 315 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
333 316 'posts_per_page' => 9,
334 317 'paged' => filter_input(
@@ -342,11 +325,8 @@
342 325 ),
343 326 )
344 327 ),
345 328 );
346 - if ( ! current_user_can( 'edit_others_posts' ) ) {
347 - $query_args['author'] = get_current_user_id();
348 - }
349 329 $filter = filter_input( INPUT_GET, 's', FILTER_SANITIZE_STRING );
350 330 if ( empty( $filter ) ) {
351 331 // 'filter' is from the modal from the add media button.
352 332 $filter = filter_input( INPUT_GET, 'filter', FILTER_SANITIZE_STRING );
@@ -394,13 +374,13 @@
394 374 * @since 1.0.0
395 375 *
396 376 * @access private
397 377 *
398 - * @param WP_Post|null $chart The chart object.
378 + * @param WP_Post $chart The chart object.
399 379 *
400 380 * @return array The array of chart data.
401 381 */
402 - private function _getChartArray( $chart = null ) {
382 + private function _getChartArray( WP_Post $chart = null ) {
403 383 if ( is_null( $chart ) ) {
404 384 $chart = $this->_chart;
405 385 }
406 386 $type = get_post_meta( $chart->ID, Visualizer_Plugin::CF_CHART_TYPE, true );
@@ -422,13 +402,8 @@
422 402 }
423 403
424 404 $date_formats = Visualizer_Source::get_date_formats_if_exists( $series, $data );
425 405
426 - $code = '';
427 - if ( 'd3' === $library ) {
428 - $code = get_post_meta( $chart->ID, Visualizer_Module_AIBuilder::CF_D3_CODE, true );
429 - }
430 -
431 406 return array(
432 407 'type' => $type,
433 408 'series' => $series,
434 409 'settings' => $settings,
@@ -433,9 +408,8 @@
433 408 'series' => $series,
434 409 'settings' => $settings,
435 410 'data' => $data,
436 411 'library' => $library,
437 - 'code' => $code,
438 412 'css' => $css,
439 413 'date_formats' => $date_formats,
440 414 );
441 415 }
@@ -452,9 +426,9 @@
452 426 public static function _sendResponse( $results ) {
453 427 header( 'Content-type: application/json' );
454 428 nocache_headers();
455 429 echo json_encode( $results );
456 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
430 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
457 431 }
458 432
459 433 /**
460 434 * Deletes a chart from database.
@@ -465,14 +439,16 @@
465 439 * @access public
466 440 */
467 441 public function deleteChart() {
468 442 $is_post = $_SERVER['REQUEST_METHOD'] === 'POST';
469 - $input = $is_post ? $_POST : $_GET;
443 + $input_method = $is_post ? INPUT_POST : INPUT_GET;
470 444 $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'],
445 + $nonce = wp_verify_nonce( filter_input( $input_method, 'nonce' ) );
446 + $capable = current_user_can( 'delete_posts' );
447 + if ( $nonce && $capable ) {
448 + $chart_id = filter_input(
449 + $input_method,
450 + 'chart',
475 451 FILTER_VALIDATE_INT,
476 452 array(
477 453 'options' => array(
478 454 'min_range' => 1,
@@ -477,17 +453,12 @@
477 453 'options' => array(
478 454 'min_range' => 1,
479 455 ),
480 456 )
481 - ) : false;
457 + );
482 458 if ( $chart_id ) {
483 459 $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 - );
460 + $success = $chart && $chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
490 461 }
491 462 }
492 463 if ( $success ) {
493 464 global $sitepress;
@@ -552,12 +523,8 @@
552 523 *
553 524 * @access public
554 525 */
555 526 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 527 defined( 'IFRAME_REQUEST' ) || define( 'IFRAME_REQUEST', 1 );
561 528 if ( ! defined( 'ET_BUILDER_PRODUCT_VERSION' ) && function_exists( 'et_get_theme_version' ) ) {
562 529 define( 'ET_BUILDER_PRODUCT_VERSION', et_get_theme_version() );
563 530 }
@@ -567,13 +534,9 @@
567 534 $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
568 535 if ( ! empty( $_POST ) ) {
569 536 $_POST = map_deep( $_POST, 'wp_strip_all_tags' );
570 537 }
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 ) {
538 + if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ) {
576 539 if ( empty( $_GET['lang'] ) || empty( $_GET['parent_chart_id'] ) ) {
577 540 $this->deleteOldCharts();
578 541 $default_type = isset( $_GET['type'] ) && ! empty( $_GET['type'] ) ? $_GET['type'] : 'line';
579 542 $chart_status = Visualizer_Module_Admin::checkChartStatus( $default_type );
@@ -607,33 +570,35 @@
607 570
608 571 do_action( 'visualizer_pro_new_chart_defaults', $chart_id );
609 572 }
610 573 } 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 - );
574 + if ( current_user_can( 'edit_posts' ) ) {
575 + $parent_chart_id = isset( $_GET['parent_chart_id'] ) ? filter_var( $_GET['parent_chart_id'], FILTER_VALIDATE_INT ) : '';
576 + $success = false;
577 + if ( $parent_chart_id ) {
578 + $parent_chart = get_post( $parent_chart_id );
579 + $success = $parent_chart && $parent_chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
580 + }
581 + if ( $success ) {
582 + $new_chart_id = wp_insert_post(
583 + array(
584 + 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
585 + 'post_title' => 'Visualization',
586 + 'post_author' => get_current_user_id(),
587 + 'post_status' => $parent_chart->post_status,
588 + 'post_content' => $parent_chart->post_content,
589 + )
590 + );
627 591
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] ) );
592 + if ( is_wp_error( $new_chart_id ) ) {
593 + 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__ );
594 + } else {
595 + $post_meta = get_post_meta( $parent_chart_id );
596 + $chart_id = $new_chart_id;
597 + foreach ( $post_meta as $key => $value ) {
598 + if ( strpos( $key, 'visualizer-' ) !== false ) {
599 + add_post_meta( $new_chart_id, $key, maybe_unserialize( $value[0] ) );
600 + }
636 601 }
637 602 }
638 603 }
639 604 }
@@ -640,9 +605,9 @@
640 605 do_action( 'visualizer_pro_new_chart_defaults', $chart_id );
641 606 }
642 607 wp_redirect( esc_url_raw( add_query_arg( 'chart', (int) $chart_id ) ) );
643 608
644 - if ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) {
609 + if ( defined( 'WP_TESTS_DOMAIN' ) ) {
645 610 wp_die();
646 611 }
647 612 exit();
648 613 }
@@ -738,9 +703,9 @@
738 703 default:
739 704 // this should never happen.
740 705 break;
741 706 }
742 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
707 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
743 708 }
744 709
745 710 /**
746 711 * Load code editor assets.
@@ -770,9 +735,9 @@
770 735 wp_register_script( 'visualizer-codemirror-matchbrackets', '//codemirror.net/addon/edit/matchbrackets.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
771 736 wp_register_script( 'visualizer-codemirror-closebrackets', '//codemirror.net/addon/edit/closebrackets.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
772 737 wp_register_script( 'visualizer-codemirror-sql', '//codemirror.net/mode/sql/sql.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
773 738 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 );
739 + 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 740 wp_register_style( 'visualizer-codemirror-core', '//codemirror.net/lib/codemirror.css', array(), Visualizer_Plugin::VERSION );
776 741 wp_register_style( 'visualizer-codemirror-hint', '//codemirror.net/addon/hint/show-hint.css', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
777 742
778 743 wp_enqueue_script( 'visualizer-codemirror-hint' );
@@ -786,9 +751,9 @@
786 751 'lineWrapping' => true,
787 752 'dragDrop' => false,
788 753 'matchBrackets' => true,
789 754 'autoCloseBrackets' => true,
790 - 'extraKeys' => array( 'Shift-Space' => 'autocomplete' ),
755 + 'extraKeys' => array( 'Ctrl-Space' => 'autocomplete' ),
791 756 'hintOptions' => array( 'tables' => $table_col_mapping ),
792 757 ),
793 758 )
794 759 );
@@ -817,17 +782,14 @@
817 782 /**
818 783 * Handle data and settings page
819 784 */
820 785 private function _handleDataAndSettingsPage() {
786 + if ( isset( $_POST['map_api_key'] ) ) {
787 + update_option( 'visualizer-map-api-key', $_POST['map_api_key'] );
788 + }
789 +
821 790 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 ) {
791 + if ( $this->_chart->post_status === 'auto-draft' ) {
830 792 $this->_chart->post_status = 'publish';
831 793
832 794 // 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 795 // we do not want any difference in data so disable revisions temporarily.
@@ -835,16 +797,10 @@
835 797
836 798 wp_update_post( $this->_chart->to_array() );
837 799 }
838 800 // 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 );
801 + if ( ! ( isset( $_POST['cancel'] ) && 1 === intval( $_POST['cancel'] ) ) ) {
802 + update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, $_POST );
847 803
848 804 // we will keep a parameter called 'internal_title' that will be set to the given title or, if empty, the chart ID
849 805 // this will help in searching with the chart id.
850 806 $settings = get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
@@ -891,9 +847,8 @@
891 847 wp_enqueue_style( 'visualizer-frame' );
892 848 wp_enqueue_script( 'visualizer-preview' );
893 849 wp_enqueue_script( 'visualizer-chosen' );
894 850 wp_enqueue_script( 'visualizer-render' );
895 - wp_enqueue_code_editor( array( 'type' => 'application/json' ) );
896 851
897 852 if ( Visualizer_Module::can_show_feature( 'simple-editor' ) ) {
898 853 wp_enqueue_script( 'visualizer-editor-simple' );
899 854 wp_localize_script(
@@ -901,10 +856,12 @@
901 856 'visualizer1',
902 857 array(
903 858 'ajax' => array(
904 859 'url' => admin_url( 'admin-ajax.php' ),
905 - 'nonces' => array(),
906 - 'actions' => array(),
860 + 'nonces' => array(
861 + ),
862 + 'actions' => array(
863 + ),
907 864 ),
908 865 )
909 866 );
910 867 }
@@ -915,15 +872,13 @@
915 872 'visualizer-render',
916 873 'visualizer',
917 874 array(
918 875 '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' ),
876 + 'invalid_source' => esc_html__( 'You have entered an invalid URL. Please provide a valid URL.', 'visualizer' ),
877 + 'loading' => esc_html__( 'Loading...', 'visualizer' ),
878 + 'json_error' => esc_html__( 'An error occured in fetching data.', 'visualizer' ),
879 + 'select_columns' => esc_html__( 'Please select a few columns to include in the chart.', 'visualizer' ),
880 + '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 881 ),
927 882 'charts' => array(
928 883 'canvas' => $data,
929 884 'id' => $this->_chart->ID,
@@ -967,10 +922,11 @@
967 922 if ( filter_input( INPUT_GET, 'library', FILTER_VALIDATE_BOOLEAN ) ) {
968 923 $render->button = filter_input( INPUT_GET, 'action' ) === Visualizer_Plugin::ACTION_EDIT_CHART
969 924 ? esc_html__( 'Save Chart', 'visualizer' )
970 925 : esc_html__( 'Create Chart', 'visualizer' );
971 -
972 - $render->cancel_button = esc_html__( 'Cancel', 'visualizer' );
926 + if ( filter_input( INPUT_GET, 'action' ) === Visualizer_Plugin::ACTION_EDIT_CHART ) {
927 + $render->cancel_button = esc_html__( 'Cancel', 'visualizer' );
928 + }
973 929 } else {
974 930 $render->button = esc_attr__( 'Insert Chart', 'visualizer' );
975 931 }
976 932
@@ -993,9 +949,9 @@
993 949 * @access private
994 950 */
995 951 private function _handleTypesPage() {
996 952 // process post request
997 - if ( $_SERVER['REQUEST_METHOD'] === 'POST' && wp_verify_nonce( filter_input( INPUT_POST, 'nonce' ), 'visualizer-upload-data' ) ) {
953 + if ( $_SERVER['REQUEST_METHOD'] === 'POST' && wp_verify_nonce( filter_input( INPUT_POST, 'nonce' ) ) ) {
998 954 $type = filter_input( INPUT_POST, 'type' );
999 955 $library = filter_input( INPUT_POST, 'chart-library' );
1000 956 if ( Visualizer_Module_Admin::checkChartStatus( $type ) ) {
1001 957 if ( empty( $library ) ) {
@@ -1034,35 +990,8 @@
1034 990 wp_iframe( array( $render, 'render' ) );
1035 991 }
1036 992
1037 993 /**
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 994 * Renders flattr script in the iframe <head>
1066 995 *
1067 996 * @since 1.4.2
1068 997 * @action admin_head
@@ -1077,81 +1006,8 @@
1077 1006 * Processes the CSV that is sent in the request as a string.
1078 1007 *
1079 1008 * @since 3.2.0
1080 1009 */
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 1010 private function handleCSVasString( $data, $editor_type ) {
1155 1011 $source = null;
1156 1012
1157 1013 switch ( $editor_type ) {
@@ -1166,9 +1022,9 @@
1166 1022 continue;
1167 1023 }
1168 1024 $row = explode( ',', $row );
1169 1025 $row = array_map(
1170 - function ( $r ) {
1026 + function( $r ) {
1171 1027 return '' === $r ? ' ' : $r;
1172 1028 },
1173 1029 $row
1174 1030 );
@@ -1217,9 +1073,9 @@
1217 1073 foreach ( $types as $type ) {
1218 1074 if ( empty( $type ) ) {
1219 1075 $exclude[] = $index;
1220 1076 }
1221 - ++$index;
1077 + $index++;
1222 1078 }
1223 1079
1224 1080 // when N headers are being renamed, the number of headers increases by N
1225 1081 // because of the way datatable duplicates header information
@@ -1277,16 +1133,11 @@
1277 1133 public function uploadData() {
1278 1134 // if this is being called internally from pro and VISUALIZER_DO_NOT_DIE is set.
1279 1135 // otherwise, assume this is a normal web request.
1280 1136 $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 1137
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 - ) {
1138 + // validate nonce
1139 + if ( ! isset( $_GET['nonce'] ) || ! wp_verify_nonce( $_GET['nonce'] ) ) {
1289 1140 if ( ! $can_die ) {
1290 1141 return;
1291 1142 }
1292 1143 status_header( 403 );
@@ -1295,15 +1146,9 @@
1295 1146
1296 1147 // check chart, if chart exists
1297 1148 // do not use filter_input as it does not work for phpunit test cases, use filter_var instead
1298 1149 $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 - ) {
1150 + if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ) {
1306 1151 if ( ! $can_die ) {
1307 1152 return;
1308 1153 }
1309 1154 status_header( 400 );
@@ -1350,24 +1195,15 @@
1350 1195 if ( isset( $_POST['remote_data'] ) && function_exists( 'wp_http_validate_url' ) ) {
1351 1196 $remote_data = wp_http_validate_url( $_POST['remote_data'] );
1352 1197 }
1353 1198 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 - }
1199 + $source = new Visualizer_Source_Csv_Remote( $remote_data );
1360 1200 if ( isset( $_POST['vz-import-time'] ) ) {
1361 1201 apply_filters( 'visualizer_pro_chart_schedule', $chart_id, $remote_data, $_POST['vz-import-time'] );
1362 1202 }
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 - }
1203 + // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
1204 + } elseif ( isset( $_FILES['local_data'] ) && $_FILES['local_data']['error'] == 0 ) {
1205 + $source = new Visualizer_Source_Csv( $_FILES['local_data']['tmp_name'] );
1370 1206 } elseif ( isset( $_POST['chart_data'] ) && strlen( $_POST['chart_data'] ) > 0 ) {
1371 1207 $source = $this->handleCSVasString( $_POST['chart_data'], $_POST['editor-type'] );
1372 1208 update_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR, $_POST['editor-type'] );
1373 1209 } elseif ( isset( $_POST['table_data'] ) && 'yes' === $_POST['table_data'] ) {
@@ -1374,10 +1210,10 @@
1374 1210 $source = $this->handleTabularData();
1375 1211 update_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR, $_POST['editor-type'] );
1376 1212 } else {
1377 1213 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' ) );
1214 + $render->message = esc_html__( 'CSV file with chart data was not uploaded. Please try again.', 'visualizer' );
1215 + update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, esc_html__( 'CSV file with chart data was not uploaded. Please try again.', 'visualizer' ) );
1380 1216 }
1381 1217
1382 1218 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 1219
@@ -1384,10 +1220,10 @@
1384 1220 if ( $source ) {
1385 1221 if ( $source->fetch() ) {
1386 1222 $content = $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) );
1387 1223 $populate = true;
1388 - $json = self::decode_content( $content );
1389 - if ( is_array( $json ) ) {
1224 + if ( is_string( $content ) && is_array( unserialize( $content ) ) ) {
1225 + $json = unserialize( $content );
1390 1226 // if source exists, so should data. if source exists but data is blank, do not populate the chart.
1391 1227 // if we populate the data even if it is empty, the chart will show "Table has no columns".
1392 1228 if ( array_key_exists( 'source', $json ) && ! empty( $json['source'] ) && ( ! array_key_exists( 'data', $json ) || empty( $json['data'] ) ) ) {
1393 1229 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 +1261,9 @@
1425 1261 $render->settings = json_encode( $settings );
1426 1262 } else {
1427 1263 $error = $source->get_error();
1428 1264 if ( empty( $error ) ) {
1429 - $error = esc_html__( 'The CSV file couldn\'t be read. Check that it\'s properly formatted and try again.', 'visualizer' );
1265 + $error = esc_html__( 'CSV file is broken or invalid. Please try again.', 'visualizer' );
1430 1266 }
1431 1267 $render->message = $error;
1432 1268 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( '%s for chart %d.', $error, $chart_id ), 'error', __FILE__, __LINE__ );
1433 1269 update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, $error );
@@ -1438,9 +1274,9 @@
1438 1274 $render->render();
1439 1275 if ( ! $can_die ) {
1440 1276 return;
1441 1277 }
1442 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
1278 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1443 1279 }
1444 1280
1445 1281 /**
1446 1282 * Clones the chart.
@@ -1451,11 +1287,12 @@
1451 1287 */
1452 1288 public function cloneChart() {
1453 1289 $chart_id = $success = false;
1454 1290 $nonce = isset( $_GET['nonce'] ) && wp_verify_nonce( $_GET['nonce'], Visualizer_Plugin::ACTION_CLONE_CHART );
1455 - if ( $nonce ) {
1291 + $capable = current_user_can( 'edit_posts' );
1292 + if ( $nonce && $capable ) {
1456 1293 $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
1457 - if ( $chart_id && self::can_edit_chart( $chart_id ) ) {
1294 + if ( $chart_id ) {
1458 1295 $chart = get_post( $chart_id );
1459 1296 $success = $chart && $chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
1460 1297 }
1461 1298 }
@@ -1475,9 +1312,9 @@
1475 1312 } else {
1476 1313 $post_meta = get_post_meta( $chart_id );
1477 1314 foreach ( $post_meta as $key => $value ) {
1478 1315 if ( strpos( $key, 'visualizer-' ) !== false ) {
1479 - add_post_meta( $new_chart_id, $key, self::maybe_decode_content( $value[0] ) );
1316 + add_post_meta( $new_chart_id, $key, maybe_unserialize( $value[0] ) );
1480 1317 }
1481 1318 }
1482 1319 $redirect = esc_url(
1483 1320 add_query_arg(
@@ -1493,9 +1330,9 @@
1493 1330 );
1494 1331 }
1495 1332 }
1496 1333
1497 - if ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) {
1334 + if ( defined( 'WP_TESTS_DOMAIN' ) ) {
1498 1335 wp_die();
1499 1336 }
1500 1337 wp_redirect( $redirect );
1501 1338 exit;
@@ -1509,25 +1346,28 @@
1509 1346 * @access public
1510 1347 */
1511 1348 public function exportData() {
1512 1349 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 );
1350 + $capable = current_user_can( 'edit_posts' );
1351 + if ( $capable ) {
1352 + $chart_id = isset( $_GET['chart'] ) ? filter_var(
1353 + $_GET['chart'],
1354 + FILTER_VALIDATE_INT,
1355 + array(
1356 + 'options' => array(
1357 + 'min_range' => 1,
1358 + ),
1359 + )
1360 + ) : '';
1361 + if ( $chart_id ) {
1362 + $data = $this->_getDataAs( $chart_id, 'csv' );
1363 + if ( $data ) {
1364 + echo wp_send_json_success( $data );
1365 + }
1526 1366 }
1527 1367 }
1528 1368
1529 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
1369 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1530 1370 }
1531 1371
1532 1372 /**
1533 1373 * Handles chart data page.
@@ -1551,11 +1391,10 @@
1551 1391 'visualizer-render',
1552 1392 'visualizer',
1553 1393 array(
1554 1394 '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' ),
1395 + 'invalid_source' => esc_html__( 'You have entered an invalid URL. Please provide a valid URL.', 'visualizer' ),
1396 + 'loading' => esc_html__( 'Loading...', 'visualizer' ),
1558 1397 ),
1559 1398 'charts' => array(
1560 1399 'canvas' => $data,
1561 1400 ),
@@ -1581,24 +1420,12 @@
1581 1420 */
1582 1421 public function getQueryData() {
1583 1422 check_ajax_referer( Visualizer_Plugin::ACTION_FETCH_DB_DATA . Visualizer_Plugin::VERSION, 'security' );
1584 1423
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 1424 $params = wp_parse_args( $_POST['params'] );
1597 1425 $chart_id = filter_var( $params['chart_id'], FILTER_VALIDATE_INT );
1598 - $query = trim( $params['query'], ';' );
1599 1426
1600 - $source = new Visualizer_Source_Query( stripslashes( $query ), $chart_id, $params );
1427 + $source = new Visualizer_Source_Query( stripslashes( $params['query'] ), $chart_id, $params );
1601 1428 $html = $source->fetch( true );
1602 1429 $error = $source->get_error();
1603 1430 if ( ! empty( $error ) ) {
1604 1431 wp_send_json_error( array( 'msg' => $error ) );
@@ -1613,19 +1440,8 @@
1613 1440 */
1614 1441 public function saveQuery() {
1615 1442 check_ajax_referer( Visualizer_Plugin::ACTION_SAVE_DB_QUERY . Visualizer_Plugin::VERSION, 'security' );
1616 1443
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 1444 $chart_id = filter_input(
1629 1445 INPUT_GET,
1630 1446 'chart',
1631 1447 FILTER_VALIDATE_INT,
@@ -1654,14 +1470,13 @@
1654 1470
1655 1471 $render = new Visualizer_Render_Page_Update();
1656 1472 if ( $chart_id ) {
1657 1473 $params = wp_parse_args( $_POST['params'] );
1658 - $query = trim( $params['query'], ';' );
1659 - $source = new Visualizer_Source_Query( stripslashes( $query ), $chart_id, $params );
1474 + $source = new Visualizer_Source_Query( stripslashes( $params['query'] ), $chart_id, $params );
1660 1475 $source->fetch( false );
1661 1476 $error = $source->get_error();
1662 1477 if ( empty( $error ) ) {
1663 - update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY, stripslashes( $query ) );
1478 + update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY, stripslashes( $params['query'] ) );
1664 1479 update_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
1665 1480 update_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
1666 1481 update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_SCHEDULE, $hours );
1667 1482 update_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, 0 );
@@ -1690,9 +1505,9 @@
1690 1505 }
1691 1506 }
1692 1507 $render->render();
1693 1508 if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
1694 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
1509 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1695 1510 }
1696 1511 }
1697 1512
1698 1513
@@ -1703,10 +1518,11 @@
1703 1518 */
1704 1519 public function saveFilter() {
1705 1520 check_ajax_referer( Visualizer_Plugin::ACTION_SAVE_FILTER_QUERY . Visualizer_Plugin::VERSION, 'security' );
1706 1521
1707 - $chart_id = isset( $_GET['chart'] ) ? filter_var(
1708 - $_GET['chart'],
1522 + $chart_id = filter_input(
1523 + INPUT_GET,
1524 + 'chart',
1709 1525 FILTER_VALIDATE_INT,
1710 1526 array(
1711 1527 'options' => array(
1712 1528 'min_range' => 1,
@@ -1711,18 +1527,14 @@
1711 1527 'options' => array(
1712 1528 'min_range' => 1,
1713 1529 ),
1714 1530 )
1715 - ) : false;
1531 + );
1716 1532
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 1533 $hours = filter_input(
1722 1534 INPUT_POST,
1723 1535 'refresh',
1724 - FILTER_VALIDATE_FLOAT,
1536 + FILTER_VALIDATE_INT,
1725 1537 array(
1726 1538 'options' => array(
1727 1539 'min_range' => -1,
1728 1540 'max_range' => apply_filters( 'visualizer_is_business', false ) ? PHP_INT_MAX : -1,
@@ -1729,9 +1541,9 @@
1729 1541 ),
1730 1542 )
1731 1543 );
1732 1544
1733 - if ( ! is_numeric( $hours ) ) {
1545 + if ( 0 !== $hours && empty( $hours ) ) {
1734 1546 $hours = -1;
1735 1547 }
1736 1548
1737 1549 do_action( 'visualizer_save_filter', $chart_id, $hours );
@@ -1736,9 +1548,9 @@
1736 1548
1737 1549 do_action( 'visualizer_save_filter', $chart_id, $hours );
1738 1550
1739 1551 if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
1740 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
1552 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1741 1553 }
1742 1554 }
1743 1555
1744 1556 /**
@@ -1746,9 +1558,9 @@
1746 1558 *
1747 1559 * @param string $base64_img Chart image.
1748 1560 * @param int $chart_id Chart ID.
1749 1561 * @param bool $save_attachment Save attachment.
1750 - * @return int Attachment ID, or 0 when no attachment was saved.
1562 + * @return attachment ID
1751 1563 */
1752 1564 public function save_chart_image( $base64_img, $chart_id, $save_attachment = true ) {
1753 1565 // Delete old chart image.
1754 1566 $old_attachment_id = get_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_IMAGE, true );
@@ -1763,15 +1575,11 @@
1763 1575 // Upload dir.
1764 1576 $upload_dir = wp_upload_dir();
1765 1577 $upload_path = str_replace( '/', DIRECTORY_SEPARATOR, $upload_dir['path'] ) . DIRECTORY_SEPARATOR;
1766 1578
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 - }
1579 + $img = str_replace( 'data:image/png;base64,', '', $base64_img );
1580 + $img = str_replace( ' ', '+', $img );
1581 + $decoded = base64_decode( $img );
1774 1582 $filename = 'visualization-' . $chart_id . '.png';
1775 1583 $file_type = 'image/png';
1776 1584 $hashed_filename = $filename;
1777 1585