PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.9.2
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.9.2
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 +128 -328 4.0.63.9.2 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,20 +534,12 @@
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 - $chart_status = Visualizer_Module_Admin::checkChartStatus( $default_type );
580 - if ( ! $chart_status ) {
581 - $default_type = 'line';
582 - }
583 542 $source = new Visualizer_Source_Csv( VISUALIZER_ABSPATH . DIRECTORY_SEPARATOR . 'samples' . DIRECTORY_SEPARATOR . $default_type . '.csv' );
584 543 $source->fetch();
585 544 $chart_id = wp_insert_post(
586 545 array(
@@ -607,33 +566,35 @@
607 566
608 567 do_action( 'visualizer_pro_new_chart_defaults', $chart_id );
609 568 }
610 569 } 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 - );
570 + if ( current_user_can( 'edit_posts' ) ) {
571 + $parent_chart_id = isset( $_GET['parent_chart_id'] ) ? filter_var( $_GET['parent_chart_id'], FILTER_VALIDATE_INT ) : '';
572 + $success = false;
573 + if ( $parent_chart_id ) {
574 + $parent_chart = get_post( $parent_chart_id );
575 + $success = $parent_chart && $parent_chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
576 + }
577 + if ( $success ) {
578 + $new_chart_id = wp_insert_post(
579 + array(
580 + 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
581 + 'post_title' => 'Visualization',
582 + 'post_author' => get_current_user_id(),
583 + 'post_status' => $parent_chart->post_status,
584 + 'post_content' => $parent_chart->post_content,
585 + )
586 + );
627 587
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] ) );
588 + if ( is_wp_error( $new_chart_id ) ) {
589 + 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__ );
590 + } else {
591 + $post_meta = get_post_meta( $parent_chart_id );
592 + $chart_id = $new_chart_id;
593 + foreach ( $post_meta as $key => $value ) {
594 + if ( strpos( $key, 'visualizer-' ) !== false ) {
595 + add_post_meta( $new_chart_id, $key, maybe_unserialize( $value[0] ) );
596 + }
636 597 }
637 598 }
638 599 }
639 600 }
@@ -640,9 +601,9 @@
640 601 do_action( 'visualizer_pro_new_chart_defaults', $chart_id );
641 602 }
642 603 wp_redirect( esc_url_raw( add_query_arg( 'chart', (int) $chart_id ) ) );
643 604
644 - if ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) {
605 + if ( defined( 'WP_TESTS_DOMAIN' ) ) {
645 606 wp_die();
646 607 }
647 608 exit();
648 609 }
@@ -738,9 +699,9 @@
738 699 default:
739 700 // this should never happen.
740 701 break;
741 702 }
742 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
703 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
743 704 }
744 705
745 706 /**
746 707 * Load code editor assets.
@@ -770,9 +731,9 @@
770 731 wp_register_script( 'visualizer-codemirror-matchbrackets', '//codemirror.net/addon/edit/matchbrackets.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
771 732 wp_register_script( 'visualizer-codemirror-closebrackets', '//codemirror.net/addon/edit/closebrackets.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
772 733 wp_register_script( 'visualizer-codemirror-sql', '//codemirror.net/mode/sql/sql.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
773 734 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 );
735 + 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 736 wp_register_style( 'visualizer-codemirror-core', '//codemirror.net/lib/codemirror.css', array(), Visualizer_Plugin::VERSION );
776 737 wp_register_style( 'visualizer-codemirror-hint', '//codemirror.net/addon/hint/show-hint.css', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
777 738
778 739 wp_enqueue_script( 'visualizer-codemirror-hint' );
@@ -786,9 +747,9 @@
786 747 'lineWrapping' => true,
787 748 'dragDrop' => false,
788 749 'matchBrackets' => true,
789 750 'autoCloseBrackets' => true,
790 - 'extraKeys' => array( 'Shift-Space' => 'autocomplete' ),
751 + 'extraKeys' => array( 'Ctrl-Space' => 'autocomplete' ),
791 752 'hintOptions' => array( 'tables' => $table_col_mapping ),
792 753 ),
793 754 )
794 755 );
@@ -817,17 +778,14 @@
817 778 /**
818 779 * Handle data and settings page
819 780 */
820 781 private function _handleDataAndSettingsPage() {
782 + if ( isset( $_POST['map_api_key'] ) ) {
783 + update_option( 'visualizer-map-api-key', $_POST['map_api_key'] );
784 + }
785 +
821 786 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 ) {
787 + if ( $this->_chart->post_status === 'auto-draft' ) {
830 788 $this->_chart->post_status = 'publish';
831 789
832 790 // 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 791 // we do not want any difference in data so disable revisions temporarily.
@@ -835,16 +793,10 @@
835 793
836 794 wp_update_post( $this->_chart->to_array() );
837 795 }
838 796 // 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 );
797 + if ( ! ( isset( $_POST['cancel'] ) && 1 === intval( $_POST['cancel'] ) ) ) {
798 + update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, $_POST );
847 799
848 800 // we will keep a parameter called 'internal_title' that will be set to the given title or, if empty, the chart ID
849 801 // this will help in searching with the chart id.
850 802 $settings = get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
@@ -891,9 +843,8 @@
891 843 wp_enqueue_style( 'visualizer-frame' );
892 844 wp_enqueue_script( 'visualizer-preview' );
893 845 wp_enqueue_script( 'visualizer-chosen' );
894 846 wp_enqueue_script( 'visualizer-render' );
895 - wp_enqueue_code_editor( array( 'type' => 'application/json' ) );
896 847
897 848 if ( Visualizer_Module::can_show_feature( 'simple-editor' ) ) {
898 849 wp_enqueue_script( 'visualizer-editor-simple' );
899 850 wp_localize_script(
@@ -901,10 +852,12 @@
901 852 'visualizer1',
902 853 array(
903 854 'ajax' => array(
904 855 'url' => admin_url( 'admin-ajax.php' ),
905 - 'nonces' => array(),
906 - 'actions' => array(),
856 + 'nonces' => array(
857 + ),
858 + 'actions' => array(
859 + ),
907 860 ),
908 861 )
909 862 );
910 863 }
@@ -915,15 +868,13 @@
915 868 'visualizer-render',
916 869 'visualizer',
917 870 array(
918 871 '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' ),
872 + 'invalid_source' => esc_html__( 'You have entered an invalid URL. Please provide a valid URL.', 'visualizer' ),
873 + 'loading' => esc_html__( 'Loading...', 'visualizer' ),
874 + 'json_error' => esc_html__( 'An error occured in fetching data.', 'visualizer' ),
875 + 'select_columns' => esc_html__( 'Please select a few columns to include in the chart.', 'visualizer' ),
876 + '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 877 ),
927 878 'charts' => array(
928 879 'canvas' => $data,
929 880 'id' => $this->_chart->ID,
@@ -967,10 +918,11 @@
967 918 if ( filter_input( INPUT_GET, 'library', FILTER_VALIDATE_BOOLEAN ) ) {
968 919 $render->button = filter_input( INPUT_GET, 'action' ) === Visualizer_Plugin::ACTION_EDIT_CHART
969 920 ? esc_html__( 'Save Chart', 'visualizer' )
970 921 : esc_html__( 'Create Chart', 'visualizer' );
971 -
972 - $render->cancel_button = esc_html__( 'Cancel', 'visualizer' );
922 + if ( filter_input( INPUT_GET, 'action' ) === Visualizer_Plugin::ACTION_EDIT_CHART ) {
923 + $render->cancel_button = esc_html__( 'Cancel', 'visualizer' );
924 + }
973 925 } else {
974 926 $render->button = esc_attr__( 'Insert Chart', 'visualizer' );
975 927 }
976 928
@@ -993,12 +945,12 @@
993 945 * @access private
994 946 */
995 947 private function _handleTypesPage() {
996 948 // process post request
997 - if ( $_SERVER['REQUEST_METHOD'] === 'POST' && wp_verify_nonce( filter_input( INPUT_POST, 'nonce' ), 'visualizer-upload-data' ) ) {
949 + if ( $_SERVER['REQUEST_METHOD'] === 'POST' && wp_verify_nonce( filter_input( INPUT_POST, 'nonce' ) ) ) {
998 950 $type = filter_input( INPUT_POST, 'type' );
999 951 $library = filter_input( INPUT_POST, 'chart-library' );
1000 - if ( Visualizer_Module_Admin::checkChartStatus( $type ) ) {
952 + if ( in_array( $type, Visualizer_Plugin::getChartTypes(), true ) ) {
1001 953 if ( empty( $library ) ) {
1002 954 // library cannot be empty.
1003 955 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, 'Chart library empty while creating the chart! Aborting...', 'error', __FILE__, __LINE__ );
1004 956 return;
@@ -1034,35 +986,8 @@
1034 986 wp_iframe( array( $render, 'render' ) );
1035 987 }
1036 988
1037 989 /**
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 990 * Renders flattr script in the iframe <head>
1066 991 *
1067 992 * @since 1.4.2
1068 993 * @action admin_head
@@ -1077,81 +1002,8 @@
1077 1002 * Processes the CSV that is sent in the request as a string.
1078 1003 *
1079 1004 * @since 3.2.0
1080 1005 */
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 1006 private function handleCSVasString( $data, $editor_type ) {
1155 1007 $source = null;
1156 1008
1157 1009 switch ( $editor_type ) {
@@ -1166,9 +1018,9 @@
1166 1018 continue;
1167 1019 }
1168 1020 $row = explode( ',', $row );
1169 1021 $row = array_map(
1170 - function ( $r ) {
1022 + function( $r ) {
1171 1023 return '' === $r ? ' ' : $r;
1172 1024 },
1173 1025 $row
1174 1026 );
@@ -1217,9 +1069,9 @@
1217 1069 foreach ( $types as $type ) {
1218 1070 if ( empty( $type ) ) {
1219 1071 $exclude[] = $index;
1220 1072 }
1221 - ++$index;
1073 + $index++;
1222 1074 }
1223 1075
1224 1076 // when N headers are being renamed, the number of headers increases by N
1225 1077 // because of the way datatable duplicates header information
@@ -1277,16 +1129,11 @@
1277 1129 public function uploadData() {
1278 1130 // if this is being called internally from pro and VISUALIZER_DO_NOT_DIE is set.
1279 1131 // otherwise, assume this is a normal web request.
1280 1132 $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 1133
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 - ) {
1134 + // validate nonce
1135 + if ( ! isset( $_GET['nonce'] ) || ! wp_verify_nonce( $_GET['nonce'] ) ) {
1289 1136 if ( ! $can_die ) {
1290 1137 return;
1291 1138 }
1292 1139 status_header( 403 );
@@ -1295,15 +1142,9 @@
1295 1142
1296 1143 // check chart, if chart exists
1297 1144 // do not use filter_input as it does not work for phpunit test cases, use filter_var instead
1298 1145 $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 - ) {
1146 + if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ) {
1306 1147 if ( ! $can_die ) {
1307 1148 return;
1308 1149 }
1309 1150 status_header( 400 );
@@ -1350,24 +1191,15 @@
1350 1191 if ( isset( $_POST['remote_data'] ) && function_exists( 'wp_http_validate_url' ) ) {
1351 1192 $remote_data = wp_http_validate_url( $_POST['remote_data'] );
1352 1193 }
1353 1194 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 - }
1195 + $source = new Visualizer_Source_Csv_Remote( $remote_data );
1360 1196 if ( isset( $_POST['vz-import-time'] ) ) {
1361 1197 apply_filters( 'visualizer_pro_chart_schedule', $chart_id, $remote_data, $_POST['vz-import-time'] );
1362 1198 }
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 - }
1199 + // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
1200 + } elseif ( isset( $_FILES['local_data'] ) && $_FILES['local_data']['error'] == 0 ) {
1201 + $source = new Visualizer_Source_Csv( $_FILES['local_data']['tmp_name'] );
1370 1202 } elseif ( isset( $_POST['chart_data'] ) && strlen( $_POST['chart_data'] ) > 0 ) {
1371 1203 $source = $this->handleCSVasString( $_POST['chart_data'], $_POST['editor-type'] );
1372 1204 update_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR, $_POST['editor-type'] );
1373 1205 } elseif ( isset( $_POST['table_data'] ) && 'yes' === $_POST['table_data'] ) {
@@ -1374,10 +1206,10 @@
1374 1206 $source = $this->handleTabularData();
1375 1207 update_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR, $_POST['editor-type'] );
1376 1208 } else {
1377 1209 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' ) );
1210 + $render->message = esc_html__( 'CSV file with chart data was not uploaded. Please try again.', 'visualizer' );
1211 + update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, esc_html__( 'CSV file with chart data was not uploaded. Please try again.', 'visualizer' ) );
1380 1212 }
1381 1213
1382 1214 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 1215
@@ -1384,10 +1216,10 @@
1384 1216 if ( $source ) {
1385 1217 if ( $source->fetch() ) {
1386 1218 $content = $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) );
1387 1219 $populate = true;
1388 - $json = self::decode_content( $content );
1389 - if ( is_array( $json ) ) {
1220 + if ( is_string( $content ) && is_array( unserialize( $content ) ) ) {
1221 + $json = unserialize( $content );
1390 1222 // if source exists, so should data. if source exists but data is blank, do not populate the chart.
1391 1223 // if we populate the data even if it is empty, the chart will show "Table has no columns".
1392 1224 if ( array_key_exists( 'source', $json ) && ! empty( $json['source'] ) && ( ! array_key_exists( 'data', $json ) || empty( $json['data'] ) ) ) {
1393 1225 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 +1257,9 @@
1425 1257 $render->settings = json_encode( $settings );
1426 1258 } else {
1427 1259 $error = $source->get_error();
1428 1260 if ( empty( $error ) ) {
1429 - $error = esc_html__( 'The CSV file couldn\'t be read. Check that it\'s properly formatted and try again.', 'visualizer' );
1261 + $error = esc_html__( 'CSV file is broken or invalid. Please try again.', 'visualizer' );
1430 1262 }
1431 1263 $render->message = $error;
1432 1264 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( '%s for chart %d.', $error, $chart_id ), 'error', __FILE__, __LINE__ );
1433 1265 update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, $error );
@@ -1438,9 +1270,9 @@
1438 1270 $render->render();
1439 1271 if ( ! $can_die ) {
1440 1272 return;
1441 1273 }
1442 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
1274 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1443 1275 }
1444 1276
1445 1277 /**
1446 1278 * Clones the chart.
@@ -1451,11 +1283,12 @@
1451 1283 */
1452 1284 public function cloneChart() {
1453 1285 $chart_id = $success = false;
1454 1286 $nonce = isset( $_GET['nonce'] ) && wp_verify_nonce( $_GET['nonce'], Visualizer_Plugin::ACTION_CLONE_CHART );
1455 - if ( $nonce ) {
1287 + $capable = current_user_can( 'edit_posts' );
1288 + if ( $nonce && $capable ) {
1456 1289 $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
1457 - if ( $chart_id && self::can_edit_chart( $chart_id ) ) {
1290 + if ( $chart_id ) {
1458 1291 $chart = get_post( $chart_id );
1459 1292 $success = $chart && $chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
1460 1293 }
1461 1294 }
@@ -1475,9 +1308,9 @@
1475 1308 } else {
1476 1309 $post_meta = get_post_meta( $chart_id );
1477 1310 foreach ( $post_meta as $key => $value ) {
1478 1311 if ( strpos( $key, 'visualizer-' ) !== false ) {
1479 - add_post_meta( $new_chart_id, $key, self::maybe_decode_content( $value[0] ) );
1312 + add_post_meta( $new_chart_id, $key, maybe_unserialize( $value[0] ) );
1480 1313 }
1481 1314 }
1482 1315 $redirect = esc_url(
1483 1316 add_query_arg(
@@ -1493,9 +1326,9 @@
1493 1326 );
1494 1327 }
1495 1328 }
1496 1329
1497 - if ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) {
1330 + if ( defined( 'WP_TESTS_DOMAIN' ) ) {
1498 1331 wp_die();
1499 1332 }
1500 1333 wp_redirect( $redirect );
1501 1334 exit;
@@ -1509,25 +1342,28 @@
1509 1342 * @access public
1510 1343 */
1511 1344 public function exportData() {
1512 1345 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 );
1346 + $capable = current_user_can( 'edit_posts' );
1347 + if ( $capable ) {
1348 + $chart_id = isset( $_GET['chart'] ) ? filter_var(
1349 + $_GET['chart'],
1350 + FILTER_VALIDATE_INT,
1351 + array(
1352 + 'options' => array(
1353 + 'min_range' => 1,
1354 + ),
1355 + )
1356 + ) : '';
1357 + if ( $chart_id ) {
1358 + $data = $this->_getDataAs( $chart_id, 'csv' );
1359 + if ( $data ) {
1360 + echo wp_send_json_success( $data );
1361 + }
1526 1362 }
1527 1363 }
1528 1364
1529 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
1365 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1530 1366 }
1531 1367
1532 1368 /**
1533 1369 * Handles chart data page.
@@ -1551,11 +1387,10 @@
1551 1387 'visualizer-render',
1552 1388 'visualizer',
1553 1389 array(
1554 1390 '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' ),
1391 + 'invalid_source' => esc_html__( 'You have entered an invalid URL. Please provide a valid URL.', 'visualizer' ),
1392 + 'loading' => esc_html__( 'Loading...', 'visualizer' ),
1558 1393 ),
1559 1394 'charts' => array(
1560 1395 'canvas' => $data,
1561 1396 ),
@@ -1581,24 +1416,12 @@
1581 1416 */
1582 1417 public function getQueryData() {
1583 1418 check_ajax_referer( Visualizer_Plugin::ACTION_FETCH_DB_DATA . Visualizer_Plugin::VERSION, 'security' );
1584 1419
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 1420 $params = wp_parse_args( $_POST['params'] );
1597 1421 $chart_id = filter_var( $params['chart_id'], FILTER_VALIDATE_INT );
1598 - $query = trim( $params['query'], ';' );
1599 1422
1600 - $source = new Visualizer_Source_Query( stripslashes( $query ), $chart_id, $params );
1423 + $source = new Visualizer_Source_Query( stripslashes( $params['query'] ), $chart_id, $params );
1601 1424 $html = $source->fetch( true );
1602 1425 $error = $source->get_error();
1603 1426 if ( ! empty( $error ) ) {
1604 1427 wp_send_json_error( array( 'msg' => $error ) );
@@ -1613,19 +1436,8 @@
1613 1436 */
1614 1437 public function saveQuery() {
1615 1438 check_ajax_referer( Visualizer_Plugin::ACTION_SAVE_DB_QUERY . Visualizer_Plugin::VERSION, 'security' );
1616 1439
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 1440 $chart_id = filter_input(
1629 1441 INPUT_GET,
1630 1442 'chart',
1631 1443 FILTER_VALIDATE_INT,
@@ -1654,14 +1466,13 @@
1654 1466
1655 1467 $render = new Visualizer_Render_Page_Update();
1656 1468 if ( $chart_id ) {
1657 1469 $params = wp_parse_args( $_POST['params'] );
1658 - $query = trim( $params['query'], ';' );
1659 - $source = new Visualizer_Source_Query( stripslashes( $query ), $chart_id, $params );
1470 + $source = new Visualizer_Source_Query( stripslashes( $params['query'] ), $chart_id, $params );
1660 1471 $source->fetch( false );
1661 1472 $error = $source->get_error();
1662 1473 if ( empty( $error ) ) {
1663 - update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY, stripslashes( $query ) );
1474 + update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY, stripslashes( $params['query'] ) );
1664 1475 update_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
1665 1476 update_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
1666 1477 update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_SCHEDULE, $hours );
1667 1478 update_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, 0 );
@@ -1690,9 +1501,9 @@
1690 1501 }
1691 1502 }
1692 1503 $render->render();
1693 1504 if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
1694 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
1505 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1695 1506 }
1696 1507 }
1697 1508
1698 1509
@@ -1703,10 +1514,11 @@
1703 1514 */
1704 1515 public function saveFilter() {
1705 1516 check_ajax_referer( Visualizer_Plugin::ACTION_SAVE_FILTER_QUERY . Visualizer_Plugin::VERSION, 'security' );
1706 1517
1707 - $chart_id = isset( $_GET['chart'] ) ? filter_var(
1708 - $_GET['chart'],
1518 + $chart_id = filter_input(
1519 + INPUT_GET,
1520 + 'chart',
1709 1521 FILTER_VALIDATE_INT,
1710 1522 array(
1711 1523 'options' => array(
1712 1524 'min_range' => 1,
@@ -1711,18 +1523,14 @@
1711 1523 'options' => array(
1712 1524 'min_range' => 1,
1713 1525 ),
1714 1526 )
1715 - ) : false;
1527 + );
1716 1528
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 1529 $hours = filter_input(
1722 1530 INPUT_POST,
1723 1531 'refresh',
1724 - FILTER_VALIDATE_FLOAT,
1532 + FILTER_VALIDATE_INT,
1725 1533 array(
1726 1534 'options' => array(
1727 1535 'min_range' => -1,
1728 1536 'max_range' => apply_filters( 'visualizer_is_business', false ) ? PHP_INT_MAX : -1,
@@ -1729,9 +1537,9 @@
1729 1537 ),
1730 1538 )
1731 1539 );
1732 1540
1733 - if ( ! is_numeric( $hours ) ) {
1541 + if ( 0 !== $hours && empty( $hours ) ) {
1734 1542 $hours = -1;
1735 1543 }
1736 1544
1737 1545 do_action( 'visualizer_save_filter', $chart_id, $hours );
@@ -1736,9 +1544,9 @@
1736 1544
1737 1545 do_action( 'visualizer_save_filter', $chart_id, $hours );
1738 1546
1739 1547 if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
1740 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
1548 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1741 1549 }
1742 1550 }
1743 1551
1744 1552 /**
@@ -1746,9 +1554,9 @@
1746 1554 *
1747 1555 * @param string $base64_img Chart image.
1748 1556 * @param int $chart_id Chart ID.
1749 1557 * @param bool $save_attachment Save attachment.
1750 - * @return int Attachment ID, or 0 when no attachment was saved.
1558 + * @return attachment ID
1751 1559 */
1752 1560 public function save_chart_image( $base64_img, $chart_id, $save_attachment = true ) {
1753 1561 // Delete old chart image.
1754 1562 $old_attachment_id = get_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_IMAGE, true );
@@ -1763,15 +1571,11 @@
1763 1571 // Upload dir.
1764 1572 $upload_dir = wp_upload_dir();
1765 1573 $upload_path = str_replace( '/', DIRECTORY_SEPARATOR, $upload_dir['path'] ) . DIRECTORY_SEPARATOR;
1766 1574
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 - }
1575 + $img = str_replace( 'data:image/png;base64,', '', $base64_img );
1576 + $img = str_replace( ' ', '+', $img );
1577 + $decoded = base64_decode( $img );
1774 1578 $filename = 'visualization-' . $chart_id . '.png';
1775 1579 $file_type = 'image/png';
1776 1580 $hashed_filename = $filename;
1777 1581
@@ -1778,12 +1582,8 @@
1778 1582 // Save the image in the uploads directory.
1779 1583 require_once ABSPATH . '/wp-admin/includes/file.php';
1780 1584 \WP_Filesystem();
1781 1585 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 1586 $upload_file = $wp_filesystem->put_contents( $upload_path . $hashed_filename, $decoded );
1787 1587
1788 1588 // Insert new chart image.
1789 1589 $attachment = array(