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