PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.7.9
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.7.9
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 +79 -300 4.0.43.7.9 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.
@@ -133,22 +134,8 @@
133 134 ),
134 135 )
135 136 );
136 137
137 - if ( Visualizer_Module::is_pro() ) {
138 - $is_woocommerce_report = filter_input(
139 - INPUT_POST,
140 - 'is_woocommerce_report',
141 - FILTER_VALIDATE_BOOLEAN
142 - );
143 -
144 - if ( $is_woocommerce_report ) {
145 - update_post_meta( $chart_id, Visualizer_Plugin::CF_IS_WOOCOMMERCE_SOURCE, true );
146 - } else {
147 - delete_post_meta( $chart_id, Visualizer_Plugin::CF_IS_WOOCOMMERCE_SOURCE );
148 - }
149 - }
150 -
151 138 delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_SCHEDULE );
152 139
153 140 if ( -1 < $time ) {
154 141 add_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_SCHEDULE, $time );
@@ -258,16 +245,8 @@
258 245 if ( ! empty( $params['paging'] ) ) {
259 246 add_post_meta( $chart->ID, Visualizer_Plugin::CF_JSON_PAGING, $params['paging'] );
260 247 }
261 248
262 - if ( Visualizer_Module::is_pro() ) {
263 - if ( ! empty( $params['vz_woo_source'] ) ) {
264 - update_post_meta( $chart->ID, Visualizer_Plugin::CF_JSON_WOOCOMMERCE_SOURCE, $params['vz_woo_source'] );
265 - } else {
266 - delete_post_meta( $chart->ID, Visualizer_Plugin::CF_JSON_WOOCOMMERCE_SOURCE );
267 - }
268 - }
269 -
270 249 $time = filter_input(
271 250 INPUT_POST,
272 251 'time',
273 252 FILTER_VALIDATE_INT,
@@ -295,9 +274,9 @@
295 274 $render->data = json_encode( $source->getRawData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ) );
296 275 $render->series = json_encode( $source->getSeries() );
297 276 $render->render();
298 277
299 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
278 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
300 279 }
301 280
302 281
303 282 /**
@@ -373,13 +352,13 @@
373 352 * @since 1.0.0
374 353 *
375 354 * @access private
376 355 *
377 - * @param WP_Post|null $chart The chart object.
356 + * @param WP_Post $chart The chart object.
378 357 *
379 358 * @return array The array of chart data.
380 359 */
381 - private function _getChartArray( $chart = null ) {
360 + private function _getChartArray( WP_Post $chart = null ) {
382 361 if ( is_null( $chart ) ) {
383 362 $chart = $this->_chart;
384 363 }
385 364 $type = get_post_meta( $chart->ID, Visualizer_Plugin::CF_CHART_TYPE, true );
@@ -401,13 +380,8 @@
401 380 }
402 381
403 382 $date_formats = Visualizer_Source::get_date_formats_if_exists( $series, $data );
404 383
405 - $code = '';
406 - if ( 'd3' === $library ) {
407 - $code = get_post_meta( $chart->ID, Visualizer_Module_AIBuilder::CF_D3_CODE, true );
408 - }
409 -
410 384 return array(
411 385 'type' => $type,
412 386 'series' => $series,
413 387 'settings' => $settings,
@@ -412,9 +386,8 @@
412 386 'series' => $series,
413 387 'settings' => $settings,
414 388 'data' => $data,
415 389 'library' => $library,
416 - 'code' => $code,
417 390 'css' => $css,
418 391 'date_formats' => $date_formats,
419 392 );
420 393 }
@@ -431,9 +404,9 @@
431 404 public static function _sendResponse( $results ) {
432 405 header( 'Content-type: application/json' );
433 406 nocache_headers();
434 407 echo json_encode( $results );
435 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
408 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
436 409 }
437 410
438 411 /**
439 412 * Deletes a chart from database.
@@ -465,22 +438,9 @@
465 438 $success = $chart && $chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
466 439 }
467 440 }
468 441 if ( $success ) {
469 - global $sitepress;
470 - if ( Visualizer_Module::is_pro() && ( function_exists( 'icl_get_languages' ) && $sitepress instanceof \SitePress ) ) {
471 - $trid = $sitepress->get_element_trid( $chart_id, 'post_' . Visualizer_Plugin::CPT_VISUALIZER );
472 - $translations = $sitepress->get_element_translations( $trid );
473 - if ( ! empty( $translations ) ) {
474 - foreach ( $translations as $translated_post ) {
475 - wp_delete_post( $translated_post->element_id, true );
476 - }
477 - } else {
478 - wp_delete_post( $chart_id, true );
479 - }
480 - } else {
481 - wp_delete_post( $chart_id, true );
482 - }
442 + wp_delete_post( $chart_id, true );
483 443 }
484 444 if ( $is_post ) {
485 445 self::_sendResponse(
486 446 array(
@@ -528,12 +488,8 @@
528 488 *
529 489 * @access public
530 490 */
531 491 public function renderChartPages() {
532 - if ( ! current_user_can( 'edit_posts' ) ) {
533 - wp_die( __( 'You do not have permission to access this page.', 'visualizer' ) );
534 - }
535 -
536 492 defined( 'IFRAME_REQUEST' ) || define( 'IFRAME_REQUEST', 1 );
537 493 if ( ! defined( 'ET_BUILDER_PRODUCT_VERSION' ) && function_exists( 'et_get_theme_version' ) ) {
538 494 define( 'ET_BUILDER_PRODUCT_VERSION', et_get_theme_version() );
539 495 }
@@ -540,82 +496,40 @@
540 496 // Set current screen for the render chart.
541 497 set_current_screen( 'visualizer_render_chart' );
542 498 // check chart, if chart not exists, will create new one and redirects to the same page with proper chart id
543 499 $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
544 - if ( ! empty( $_POST ) ) {
545 - $_POST = map_deep( $_POST, 'wp_strip_all_tags' );
546 - }
547 - $chart = $chart_id ? get_post( $chart_id ) : null;
548 - if ( ! $chart_id || ! $chart || $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ) {
549 - if ( empty( $_GET['lang'] ) || empty( $_GET['parent_chart_id'] ) ) {
550 - $this->deleteOldCharts();
551 - $default_type = isset( $_GET['type'] ) && ! empty( $_GET['type'] ) ? $_GET['type'] : 'line';
552 - $chart_status = Visualizer_Module_Admin::checkChartStatus( $default_type );
553 - if ( ! $chart_status ) {
554 - $default_type = 'line';
555 - }
556 - $source = new Visualizer_Source_Csv( VISUALIZER_ABSPATH . DIRECTORY_SEPARATOR . 'samples' . DIRECTORY_SEPARATOR . $default_type . '.csv' );
557 - $source->fetch();
558 - $chart_id = wp_insert_post(
500 + if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ) {
501 + $this->deleteOldCharts();
502 + $default_type = isset( $_GET['type'] ) && ! empty( $_GET['type'] ) ? $_GET['type'] : 'line';
503 + $source = new Visualizer_Source_Csv( VISUALIZER_ABSPATH . DIRECTORY_SEPARATOR . 'samples' . DIRECTORY_SEPARATOR . $default_type . '.csv' );
504 + $source->fetch();
505 + $chart_id = wp_insert_post(
506 + array(
507 + 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
508 + 'post_title' => 'Visualization',
509 + 'post_author' => get_current_user_id(),
510 + 'post_status' => 'auto-draft',
511 + 'post_content' => $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ),
512 + )
513 + );
514 + if ( $chart_id && ! is_wp_error( $chart_id ) ) {
515 + add_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_TYPE, $default_type );
516 + add_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, 1 );
517 + add_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
518 + add_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
519 + add_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_LIBRARY, '' );
520 + add_post_meta(
521 + $chart_id,
522 + Visualizer_Plugin::CF_SETTINGS,
559 523 array(
560 - 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
561 - 'post_title' => 'Visualization',
562 - 'post_author' => get_current_user_id(),
563 - 'post_status' => 'auto-draft',
564 - 'post_content' => $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ),
524 + 'focusTarget' => 'datum',
565 525 )
566 526 );
567 - if ( $chart_id && ! is_wp_error( $chart_id ) ) {
568 - add_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_TYPE, $default_type );
569 - add_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, 1 );
570 - add_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
571 - add_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
572 - add_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_LIBRARY, '' );
573 - add_post_meta(
574 - $chart_id,
575 - Visualizer_Plugin::CF_SETTINGS,
576 - array(
577 - 'focusTarget' => 'datum',
578 - )
579 - );
580 -
581 - do_action( 'visualizer_pro_new_chart_defaults', $chart_id );
582 - }
583 - } else {
584 - $parent_chart_id = filter_var( $_GET['parent_chart_id'], FILTER_VALIDATE_INT );
585 - $success = false;
586 - if ( $parent_chart_id ) {
587 - $parent_chart = get_post( $parent_chart_id );
588 - $success = $parent_chart && $parent_chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
589 - }
590 - if ( $success ) {
591 - $new_chart_id = wp_insert_post(
592 - array(
593 - 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
594 - 'post_title' => 'Visualization',
595 - 'post_author' => get_current_user_id(),
596 - 'post_status' => $parent_chart->post_status,
597 - 'post_content' => $parent_chart->post_content,
598 - )
599 - );
600 -
601 - if ( is_wp_error( $new_chart_id ) ) {
602 - 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__ );
603 - } else {
604 - $post_meta = get_post_meta( $parent_chart_id );
605 - $chart_id = $new_chart_id;
606 - foreach ( $post_meta as $key => $value ) {
607 - if ( strpos( $key, 'visualizer-' ) !== false ) {
608 - add_post_meta( $new_chart_id, $key, maybe_unserialize( $value[0] ) );
609 - }
610 - }
611 - }
612 - }
613 527 do_action( 'visualizer_pro_new_chart_defaults', $chart_id );
614 528 }
615 529 wp_redirect( esc_url_raw( add_query_arg( 'chart', (int) $chart_id ) ) );
616 530
617 - if ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) {
531 + if ( defined( 'WP_TESTS_DOMAIN' ) ) {
618 532 wp_die();
619 533 }
620 534 exit();
621 535 }
@@ -711,9 +625,9 @@
711 625 default:
712 626 // this should never happen.
713 627 break;
714 628 }
715 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
629 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
716 630 }
717 631
718 632 /**
719 633 * Load code editor assets.
@@ -743,9 +657,9 @@
743 657 wp_register_script( 'visualizer-codemirror-matchbrackets', '//codemirror.net/addon/edit/matchbrackets.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
744 658 wp_register_script( 'visualizer-codemirror-closebrackets', '//codemirror.net/addon/edit/closebrackets.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
745 659 wp_register_script( 'visualizer-codemirror-sql', '//codemirror.net/mode/sql/sql.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
746 660 wp_register_script( 'visualizer-codemirror-sql-hint', '//codemirror.net/addon/hint/sql-hint.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
747 - wp_register_script( 'visualizer-codemirror-hint', '//codemirror.net/addon/hint/show-hint.js', array( 'visualizer-codemirror-sql', 'visualizer-codemirror-sql-hint', 'visualizer-codemirror-placeholder', 'visualizer-codemirror-matchbrackets', 'visualizer-codemirror-closebrackets' ), Visualizer_Plugin::VERSION );
661 + wp_register_script( 'visualizer-codemirror-hint', '//codemirror.net/addon/hint/show-hint.js', array( 'visualizer-codemirror-sql', 'visualizer-codemirror-sql-hint', 'visualizer-codemirror-placeholder', 'visualizer-codemirror-matchbrackets', 'visualizer-codemirror-closebrackets' ), Visualizer_Plugin::VERSION );
748 662 wp_register_style( 'visualizer-codemirror-core', '//codemirror.net/lib/codemirror.css', array(), Visualizer_Plugin::VERSION );
749 663 wp_register_style( 'visualizer-codemirror-hint', '//codemirror.net/addon/hint/show-hint.css', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
750 664
751 665 wp_enqueue_script( 'visualizer-codemirror-hint' );
@@ -759,9 +673,9 @@
759 673 'lineWrapping' => true,
760 674 'dragDrop' => false,
761 675 'matchBrackets' => true,
762 676 'autoCloseBrackets' => true,
763 - 'extraKeys' => array( 'Shift-Space' => 'autocomplete' ),
677 + 'extraKeys' => array( 'Ctrl-Space' => 'autocomplete' ),
764 678 'hintOptions' => array( 'tables' => $table_col_mapping ),
765 679 ),
766 680 )
767 681 );
@@ -795,12 +709,9 @@
795 709 update_option( 'visualizer-map-api-key', $_POST['map_api_key'] );
796 710 }
797 711
798 712 if ( $_SERVER['REQUEST_METHOD'] === 'POST' && isset( $_GET['nonce'] ) && wp_verify_nonce( $_GET['nonce'] ) ) {
799 - $is_canceled = isset( $_POST['cancel'] ) && 1 === intval( $_POST['cancel'] );
800 - $is_newly_created = $this->_chart->post_status === 'auto-draft';
801 -
802 - if ( $is_newly_created && ! $is_canceled ) {
713 + if ( $this->_chart->post_status === 'auto-draft' ) {
803 714 $this->_chart->post_status = 'publish';
804 715
805 716 // ensure that a revision is not created. If a revision is created it will have the proper data and the parent of the revision will have default data.
806 717 // we do not want any difference in data so disable revisions temporarily.
@@ -808,15 +719,10 @@
808 719
809 720 wp_update_post( $this->_chart->to_array() );
810 721 }
811 722 // save meta data only when it is NOT being canceled.
812 - if ( ! $is_canceled ) {
813 - $post_settings = $_POST;
814 - $existing = get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
815 - if ( isset( $existing['colors'] ) && is_array( $existing['colors'] ) && ! isset( $post_settings['colors'] ) ) {
816 - $post_settings['colors'] = $existing['colors'];
817 - }
818 - update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, $post_settings );
723 + if ( ! ( isset( $_POST['cancel'] ) && 1 === intval( $_POST['cancel'] ) ) ) {
724 + update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, $_POST );
819 725
820 726 // we will keep a parameter called 'internal_title' that will be set to the given title or, if empty, the chart ID
821 727 // this will help in searching with the chart id.
822 728 $settings = get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
@@ -830,9 +736,9 @@
830 736 if ( empty( $title ) ) {
831 737 $title = $this->_chart->ID;
832 738 }
833 739 $settings['internal_title'] = $title;
834 - $settings_label = isset( $settings['pieResidueSliceLabel'] ) ? $settings['pieResidueSliceLabel'] : '';
740 + $settings_label = $settings['pieResidueSliceLabel'];
835 741 if ( empty( $settings_label ) ) {
836 742 $settings['pieResidueSliceLabel'] = esc_html__( 'Other', 'visualizer' );
837 743 } else {
838 744 $settings['pieResidueSliceLabel'] = $settings_label;
@@ -863,9 +769,8 @@
863 769 wp_enqueue_style( 'visualizer-frame' );
864 770 wp_enqueue_script( 'visualizer-preview' );
865 771 wp_enqueue_script( 'visualizer-chosen' );
866 772 wp_enqueue_script( 'visualizer-render' );
867 - wp_enqueue_code_editor( array( 'type' => 'application/json' ) );
868 773
869 774 if ( Visualizer_Module::can_show_feature( 'simple-editor' ) ) {
870 775 wp_enqueue_script( 'visualizer-editor-simple' );
871 776 wp_localize_script(
@@ -873,10 +778,12 @@
873 778 'visualizer1',
874 779 array(
875 780 'ajax' => array(
876 781 'url' => admin_url( 'admin-ajax.php' ),
877 - 'nonces' => array(),
878 - 'actions' => array(),
782 + 'nonces' => array(
783 + ),
784 + 'actions' => array(
785 + ),
879 786 ),
880 787 )
881 788 );
882 789 }
@@ -887,15 +794,13 @@
887 794 'visualizer-render',
888 795 'visualizer',
889 796 array(
890 797 'l10n' => array(
891 - 'invalid_source' => esc_html__( 'The URL you entered is invalid. Please enter a valid URL.', 'visualizer' ),
892 - 'loading' => esc_html__( 'Loading...', 'visualizer' ),
893 - 'json_error' => esc_html__( 'An error occured in fetching data.', 'visualizer' ),
894 - 'select_columns' => esc_html__( 'Please select a few columns to include in the chart.', 'visualizer' ),
895 - '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' ),
896 - 'copied' => __( 'The data has been copied to your clipboard. Hit Ctrl-V/Cmd-V in your spreadsheet editor to paste the data.', 'visualizer' ),
897 - 'invalid_format' => esc_html__( 'This format pattern is not supported in the series settings field. Use the Manual Configuration option instead.', 'visualizer' ),
798 + 'invalid_source' => esc_html__( 'You have entered an invalid URL. Please provide a valid URL.', 'visualizer' ),
799 + 'loading' => esc_html__( 'Loading...', 'visualizer' ),
800 + 'json_error' => esc_html__( 'An error occured in fetching data.', 'visualizer' ),
801 + 'select_columns' => esc_html__( 'Please select a few columns to include in the chart.', 'visualizer' ),
802 + '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' ),
898 803 ),
899 804 'charts' => array(
900 805 'canvas' => $data,
901 806 'id' => $this->_chart->ID,
@@ -926,9 +831,8 @@
926 831 'page_type' => 'chart',
927 832 'json_tag_separator' => Visualizer_Source_Json::TAG_SEPARATOR,
928 833 'json_tag_separator_view' => Visualizer_Source_Json::TAG_SEPARATOR_VIEW,
929 834 'is_front' => false,
930 - 'rest_base' => get_rest_url( null, 'wc/v3/reports/' ),
931 835 )
932 836 );
933 837
934 838 $render = new Visualizer_Render_Page_Data();
@@ -939,10 +843,11 @@
939 843 if ( filter_input( INPUT_GET, 'library', FILTER_VALIDATE_BOOLEAN ) ) {
940 844 $render->button = filter_input( INPUT_GET, 'action' ) === Visualizer_Plugin::ACTION_EDIT_CHART
941 845 ? esc_html__( 'Save Chart', 'visualizer' )
942 846 : esc_html__( 'Create Chart', 'visualizer' );
943 -
944 - $render->cancel_button = esc_html__( 'Cancel', 'visualizer' );
847 + if ( filter_input( INPUT_GET, 'action' ) === Visualizer_Plugin::ACTION_EDIT_CHART ) {
848 + $render->cancel_button = esc_html__( 'Cancel', 'visualizer' );
849 + }
945 850 } else {
946 851 $render->button = esc_attr__( 'Insert Chart', 'visualizer' );
947 852 }
948 853
@@ -965,12 +870,12 @@
965 870 * @access private
966 871 */
967 872 private function _handleTypesPage() {
968 873 // process post request
969 - if ( $_SERVER['REQUEST_METHOD'] === 'POST' && wp_verify_nonce( filter_input( INPUT_POST, 'nonce' ), 'visualizer-upload-data' ) ) {
874 + if ( $_SERVER['REQUEST_METHOD'] === 'POST' && wp_verify_nonce( filter_input( INPUT_POST, 'nonce' ) ) ) {
970 875 $type = filter_input( INPUT_POST, 'type' );
971 876 $library = filter_input( INPUT_POST, 'chart-library' );
972 - if ( Visualizer_Module_Admin::checkChartStatus( $type ) ) {
877 + if ( in_array( $type, Visualizer_Plugin::getChartTypes(), true ) ) {
973 878 if ( empty( $library ) ) {
974 879 // library cannot be empty.
975 880 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, 'Chart library empty while creating the chart! Aborting...', 'error', __FILE__, __LINE__ );
976 881 return;
@@ -1022,80 +927,8 @@
1022 927 * Processes the CSV that is sent in the request as a string.
1023 928 *
1024 929 * @since 3.2.0
1025 930 */
1026 - /**
1027 - * Determines whether a remote URL serves an XLSX file.
1028 - *
1029 - * Used as a fallback when the URL path has no recognisable file extension
1030 - * (e.g. SharePoint, signed S3 URLs, or "download?id=…" endpoints).
1031 - *
1032 - * Uses wp_safe_remote_get() to block requests to private/loopback addresses,
1033 - * and streams the response to a temp file so no body data is held in memory
1034 - * regardless of whether the server honours the Range header.
1035 - *
1036 - * The check relies on the ZIP magic number (PK\x03\x04) that every XLSX
1037 - * file begins with, making it immune to misleading Content-Type headers
1038 - * such as application/octet-stream. Content-Type is used as a last-resort
1039 - * fallback only when the temp file is empty (e.g. a HEAD-only server).
1040 - *
1041 - * @access private
1042 - * @param string $url The remote URL to probe.
1043 - * @return bool TRUE if the file appears to be XLSX, FALSE otherwise.
1044 - */
1045 - private static function _url_is_xlsx( $url ) {
1046 - $tmpfile = wp_tempnam( 'visualizer_xlsx_probe' );
1047 - if ( ! $tmpfile ) {
1048 - return false;
1049 - }
1050 -
1051 - $response = wp_safe_remote_get(
1052 - $url,
1053 - array(
1054 - 'timeout' => 10,
1055 - 'user-agent' => 'WordPress/' . get_bloginfo( 'version' ),
1056 - 'headers' => array( 'Range' => 'bytes=0-3' ),
1057 - 'stream' => true,
1058 - 'filename' => $tmpfile,
1059 - )
1060 - );
1061 -
1062 - if ( is_wp_error( $response ) ) {
1063 - @unlink( $tmpfile ); // phpcs:ignore WordPress.PHP.NoSilencedErrors
1064 - return false;
1065 - }
1066 -
1067 - $magic = '';
1068 - // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen
1069 - $fh = @fopen( $tmpfile, 'rb' ); // phpcs:ignore WordPress.PHP.NoSilencedErrors
1070 - if ( $fh ) {
1071 - $magic = fread( $fh, 4 ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fread
1072 - fclose( $fh ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose
1073 - }
1074 - @unlink( $tmpfile ); // phpcs:ignore WordPress.PHP.NoSilencedErrors
1075 -
1076 - if ( strlen( $magic ) >= 4 ) {
1077 - // XLSX (and all ZIP-based Office formats) start with PK\x03\x04.
1078 - return $magic === "PK\x03\x04";
1079 - }
1080 -
1081 - // Last resort: server returned an empty body (e.g. ignored Range and
1082 - // returned only headers). Check Content-Type from the same response.
1083 - // application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
1084 - return false !== strpos(
1085 - wp_remote_retrieve_header( $response, 'content-type' ),
1086 - 'spreadsheetml'
1087 - );
1088 - }
1089 -
1090 - /**
1091 - * Parses a raw CSV string or editor payload and returns a source object.
1092 - *
1093 - * @access private
1094 - * @param string $data The raw CSV data string.
1095 - * @param string $editor_type The editor type ('text' or 'tabular').
1096 - * @return Visualizer_Source|null The populated source object, or null on failure.
1097 - */
1098 931 private function handleCSVasString( $data, $editor_type ) {
1099 932 $source = null;
1100 933
1101 934 switch ( $editor_type ) {
@@ -1110,9 +943,9 @@
1110 943 continue;
1111 944 }
1112 945 $row = explode( ',', $row );
1113 946 $row = array_map(
1114 - function ( $r ) {
947 + function( $r ) {
1115 948 return '' === $r ? ' ' : $r;
1116 949 },
1117 950 $row
1118 951 );
@@ -1161,9 +994,9 @@
1161 994 foreach ( $types as $type ) {
1162 995 if ( empty( $type ) ) {
1163 996 $exclude[] = $index;
1164 997 }
1165 - ++$index;
998 + $index++;
1166 999 }
1167 1000
1168 1001 // when N headers are being renamed, the number of headers increases by N
1169 1002 // because of the way datatable duplicates header information
@@ -1223,13 +1056,9 @@
1223 1056 // otherwise, assume this is a normal web request.
1224 1057 $can_die = ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE );
1225 1058
1226 1059 // validate nonce
1227 - if (
1228 - ! isset( $_GET['nonce'] ) ||
1229 - ! wp_verify_nonce( $_GET['nonce'], 'visualizer-upload-data' ) ||
1230 - ! current_user_can( 'edit_posts' )
1231 - ) {
1060 + if ( ! isset( $_GET['nonce'] ) || ! wp_verify_nonce( $_GET['nonce'] ) ) {
1232 1061 if ( ! $can_die ) {
1233 1062 return;
1234 1063 }
1235 1064 status_header( 403 );
@@ -1238,15 +1067,9 @@
1238 1067
1239 1068 // check chart, if chart exists
1240 1069 // do not use filter_input as it does not work for phpunit test cases, use filter_var instead
1241 1070 $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
1242 - $chart = $chart_id ? get_post( $chart_id ) : null;
1243 - if (
1244 - ! $chart_id ||
1245 - ! $chart ||
1246 - $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ||
1247 - ! current_user_can( 'edit_post', $chart_id )
1248 - ) {
1071 + if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ) {
1249 1072 if ( ! $can_die ) {
1250 1073 return;
1251 1074 }
1252 1075 status_header( 400 );
@@ -1287,30 +1110,16 @@
1287 1110 delete_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE );
1288 1111
1289 1112 $source = null;
1290 1113 $render = new Visualizer_Render_Page_Update();
1291 -
1292 - $remote_data = false;
1293 - if ( isset( $_POST['remote_data'] ) && function_exists( 'wp_http_validate_url' ) ) {
1294 - $remote_data = wp_http_validate_url( $_POST['remote_data'] );
1295 - }
1296 - if ( false !== $remote_data ) {
1297 - $remote_ext = strtolower( pathinfo( parse_url( $remote_data, PHP_URL_PATH ), PATHINFO_EXTENSION ) );
1298 - if ( 'xlsx' === $remote_ext || ( 'csv' !== $remote_ext && self::_url_is_xlsx( $remote_data ) ) ) {
1299 - $source = new Visualizer_Source_Xlsx_Remote( $remote_data );
1300 - } else {
1301 - $source = new Visualizer_Source_Csv_Remote( $remote_data );
1302 - }
1114 + if ( isset( $_POST['remote_data'] ) && filter_var( $_POST['remote_data'], FILTER_VALIDATE_URL ) ) {
1115 + $source = new Visualizer_Source_Csv_Remote( $_POST['remote_data'] );
1303 1116 if ( isset( $_POST['vz-import-time'] ) ) {
1304 - apply_filters( 'visualizer_pro_chart_schedule', $chart_id, $remote_data, $_POST['vz-import-time'] );
1117 + apply_filters( 'visualizer_pro_chart_schedule', $chart_id, $_POST['remote_data'], $_POST['vz-import-time'] );
1305 1118 }
1306 - } elseif ( isset( $_FILES['local_data'] ) && $_FILES['local_data']['error'] === 0 ) {
1307 - $local_ext = strtolower( pathinfo( isset( $_FILES['local_data']['name'] ) ? $_FILES['local_data']['name'] : '', PATHINFO_EXTENSION ) );
1308 - if ( 'xlsx' === $local_ext ) {
1309 - $source = new Visualizer_Source_Xlsx( $_FILES['local_data']['tmp_name'] );
1310 - } else {
1311 - $source = new Visualizer_Source_Csv( $_FILES['local_data']['tmp_name'] );
1312 - }
1119 + // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
1120 + } elseif ( isset( $_FILES['local_data'] ) && $_FILES['local_data']['error'] == 0 ) {
1121 + $source = new Visualizer_Source_Csv( $_FILES['local_data']['tmp_name'] );
1313 1122 } elseif ( isset( $_POST['chart_data'] ) && strlen( $_POST['chart_data'] ) > 0 ) {
1314 1123 $source = $this->handleCSVasString( $_POST['chart_data'], $_POST['editor-type'] );
1315 1124 update_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR, $_POST['editor-type'] );
1316 1125 } elseif ( isset( $_POST['table_data'] ) && 'yes' === $_POST['table_data'] ) {
@@ -1317,10 +1126,10 @@
1317 1126 $source = $this->handleTabularData();
1318 1127 update_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR, $_POST['editor-type'] );
1319 1128 } else {
1320 1129 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__ );
1321 - $render->message = esc_html__( 'No CSV file was received. Select a file and try uploading again.', 'visualizer' );
1322 - update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, esc_html__( 'No CSV file was received. Select a file and try uploading again.', 'visualizer' ) );
1130 + $render->message = esc_html__( 'CSV file with chart data was not uploaded. Please try again.', 'visualizer' );
1131 + update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, esc_html__( 'CSV file with chart data was not uploaded. Please try again.', 'visualizer' ) );
1323 1132 }
1324 1133
1325 1134 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__ );
1326 1135
@@ -1368,9 +1177,9 @@
1368 1177 $render->settings = json_encode( $settings );
1369 1178 } else {
1370 1179 $error = $source->get_error();
1371 1180 if ( empty( $error ) ) {
1372 - $error = esc_html__( 'The CSV file couldn\'t be read. Check that it\'s properly formatted and try again.', 'visualizer' );
1181 + $error = esc_html__( 'CSV file is broken or invalid. Please try again.', 'visualizer' );
1373 1182 }
1374 1183 $render->message = $error;
1375 1184 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( '%s for chart %d.', $error, $chart_id ), 'error', __FILE__, __LINE__ );
1376 1185 update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, $error );
@@ -1381,9 +1190,9 @@
1381 1190 $render->render();
1382 1191 if ( ! $can_die ) {
1383 1192 return;
1384 1193 }
1385 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
1194 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1386 1195 }
1387 1196
1388 1197 /**
1389 1198 * Clones the chart.
@@ -1430,18 +1239,17 @@
1430 1239 'type' => filter_input( INPUT_GET, 'type' ),
1431 1240 'vaction' => false,
1432 1241 ),
1433 1242 admin_url( 'admin.php' )
1434 - ),
1435 - null,
1436 - 'db'
1243 + )
1437 1244 );
1438 1245 }
1439 1246 }
1440 1247
1441 - if ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) {
1248 + if ( defined( 'WP_TESTS_DOMAIN' ) ) {
1442 1249 wp_die();
1443 1250 }
1251 +
1444 1252 wp_redirect( $redirect );
1445 1253 exit;
1446 1254 }
1447 1255
@@ -1472,9 +1280,9 @@
1472 1280 }
1473 1281 }
1474 1282 }
1475 1283
1476 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
1284 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1477 1285 }
1478 1286
1479 1287 /**
1480 1288 * Handles chart data page.
@@ -1498,11 +1306,10 @@
1498 1306 'visualizer-render',
1499 1307 'visualizer',
1500 1308 array(
1501 1309 'l10n' => array(
1502 - 'invalid_source' => esc_html__( 'The URL you entered is invalid. Please enter a valid URL.', 'visualizer' ),
1503 - 'loading' => esc_html__( 'Loading...', 'visualizer' ),
1504 - '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' ),
1310 + 'invalid_source' => esc_html__( 'You have entered an invalid URL. Please provide a valid URL.', 'visualizer' ),
1311 + 'loading' => esc_html__( 'Loading...', 'visualizer' ),
1505 1312 ),
1506 1313 'charts' => array(
1507 1314 'canvas' => $data,
1508 1315 ),
@@ -1528,24 +1335,12 @@
1528 1335 */
1529 1336 public function getQueryData() {
1530 1337 check_ajax_referer( Visualizer_Plugin::ACTION_FETCH_DB_DATA . Visualizer_Plugin::VERSION, 'security' );
1531 1338
1532 - if ( ! current_user_can( 'administrator' ) ) {
1533 - wp_send_json_error( array( 'msg' => __( 'Action not allowed for this user.', 'visualizer' ) ) );
1534 - }
1535 - if ( ! is_super_admin() ) {
1536 - wp_send_json_error( array( 'msg' => __( 'Action not allowed for this user.', 'visualizer' ) ) );
1537 - }
1538 -
1539 - if ( ! Visualizer_Module::is_pro() ) {
1540 - wp_send_json_error( array( 'msg' => __( 'Feature is not available.', 'visualizer' ) ) );
1541 - }
1542 -
1543 1339 $params = wp_parse_args( $_POST['params'] );
1544 1340 $chart_id = filter_var( $params['chart_id'], FILTER_VALIDATE_INT );
1545 - $query = trim( $params['query'], ';' );
1546 1341
1547 - $source = new Visualizer_Source_Query( stripslashes( $query ), $chart_id, $params );
1342 + $source = new Visualizer_Source_Query( stripslashes( $params['query'] ), $chart_id, $params );
1548 1343 $html = $source->fetch( true );
1549 1344 $error = $source->get_error();
1550 1345 if ( ! empty( $error ) ) {
1551 1346 wp_send_json_error( array( 'msg' => $error ) );
@@ -1560,19 +1355,8 @@
1560 1355 */
1561 1356 public function saveQuery() {
1562 1357 check_ajax_referer( Visualizer_Plugin::ACTION_SAVE_DB_QUERY . Visualizer_Plugin::VERSION, 'security' );
1563 1358
1564 - if ( ! current_user_can( 'administrator' ) ) {
1565 - wp_send_json_error( array( 'msg' => __( 'Action not allowed for this user.', 'visualizer' ) ) );
1566 - }
1567 - if ( ! is_super_admin() ) {
1568 - wp_send_json_error( array( 'msg' => __( 'Action not allowed for this user.', 'visualizer' ) ) );
1569 - }
1570 -
1571 - if ( ! Visualizer_Module::is_pro() ) {
1572 - wp_send_json_error( array( 'msg' => __( 'Feature is not available.', 'visualizer' ) ) );
1573 - }
1574 -
1575 1359 $chart_id = filter_input(
1576 1360 INPUT_GET,
1577 1361 'chart',
1578 1362 FILTER_VALIDATE_INT,
@@ -1601,14 +1385,13 @@
1601 1385
1602 1386 $render = new Visualizer_Render_Page_Update();
1603 1387 if ( $chart_id ) {
1604 1388 $params = wp_parse_args( $_POST['params'] );
1605 - $query = trim( $params['query'], ';' );
1606 - $source = new Visualizer_Source_Query( stripslashes( $query ), $chart_id, $params );
1389 + $source = new Visualizer_Source_Query( stripslashes( $params['query'] ), $chart_id, $params );
1607 1390 $source->fetch( false );
1608 1391 $error = $source->get_error();
1609 1392 if ( empty( $error ) ) {
1610 - update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY, stripslashes( $query ) );
1393 + update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY, stripslashes( $params['query'] ) );
1611 1394 update_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
1612 1395 update_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
1613 1396 update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_SCHEDULE, $hours );
1614 1397 update_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, 0 );
@@ -1637,9 +1420,9 @@
1637 1420 }
1638 1421 }
1639 1422 $render->render();
1640 1423 if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
1641 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
1424 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1642 1425 }
1643 1426 }
1644 1427
1645 1428
@@ -1664,9 +1447,9 @@
1664 1447
1665 1448 $hours = filter_input(
1666 1449 INPUT_POST,
1667 1450 'refresh',
1668 - FILTER_VALIDATE_FLOAT,
1451 + FILTER_VALIDATE_INT,
1669 1452 array(
1670 1453 'options' => array(
1671 1454 'min_range' => -1,
1672 1455 'max_range' => apply_filters( 'visualizer_is_business', false ) ? PHP_INT_MAX : -1,
@@ -1673,9 +1456,9 @@
1673 1456 ),
1674 1457 )
1675 1458 );
1676 1459
1677 - if ( ! is_numeric( $hours ) ) {
1460 + if ( 0 !== $hours && empty( $hours ) ) {
1678 1461 $hours = -1;
1679 1462 }
1680 1463
1681 1464 do_action( 'visualizer_save_filter', $chart_id, $hours );
@@ -1680,9 +1463,9 @@
1680 1463
1681 1464 do_action( 'visualizer_save_filter', $chart_id, $hours );
1682 1465
1683 1466 if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
1684 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
1467 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1685 1468 }
1686 1469 }
1687 1470
1688 1471 /**
@@ -1718,12 +1501,8 @@
1718 1501 // Save the image in the uploads directory.
1719 1502 require_once ABSPATH . '/wp-admin/includes/file.php';
1720 1503 \WP_Filesystem();
1721 1504 global $wp_filesystem;
1722 - if ( ! is_a( $wp_filesystem, 'WP_Filesystem_Base' ) ) {
1723 - $creds = request_filesystem_credentials( site_url() );
1724 - wp_filesystem( $creds );
1725 - }
1726 1505 $upload_file = $wp_filesystem->put_contents( $upload_path . $hashed_filename, $decoded );
1727 1506
1728 1507 // Insert new chart image.
1729 1508 $attachment = array(