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 +78 -229 4.0.53.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.
@@ -169,12 +170,8 @@
169 170 */
170 171 public function getJsonRoots() {
171 172 check_ajax_referer( Visualizer_Plugin::ACTION_JSON_GET_ROOTS . Visualizer_Plugin::VERSION, 'security' );
172 173
173 - if ( ! current_user_can( 'edit_posts' ) ) {
174 - wp_send_json_error( array( 'msg' => esc_html__( 'You do not have permission to perform this action.', 'visualizer' ) ) );
175 - }
176 -
177 174 $params = wp_parse_args( $_POST['params'] );
178 175
179 176 $source = new Visualizer_Source_Json( $params );
180 177
@@ -195,12 +192,8 @@
195 192 */
196 193 public function getJsonData() {
197 194 check_ajax_referer( Visualizer_Plugin::ACTION_JSON_GET_DATA . Visualizer_Plugin::VERSION, 'security' );
198 195
199 - if ( ! current_user_can( 'edit_posts' ) ) {
200 - wp_send_json_error( array( 'msg' => esc_html__( 'You do not have permission to perform this action.', 'visualizer' ) ) );
201 - }
202 -
203 196 $params = wp_parse_args( $_POST['params'] );
204 197
205 198 $chart_id = $params['chart'];
206 199
@@ -303,9 +296,9 @@
303 296 $render->data = json_encode( $source->getRawData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ) );
304 297 $render->series = json_encode( $source->getSeries() );
305 298 $render->render();
306 299
307 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
300 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
308 301 }
309 302
310 303
311 304 /**
@@ -381,13 +374,13 @@
381 374 * @since 1.0.0
382 375 *
383 376 * @access private
384 377 *
385 - * @param WP_Post|null $chart The chart object.
378 + * @param WP_Post $chart The chart object.
386 379 *
387 380 * @return array The array of chart data.
388 381 */
389 - private function _getChartArray( $chart = null ) {
382 + private function _getChartArray( WP_Post $chart = null ) {
390 383 if ( is_null( $chart ) ) {
391 384 $chart = $this->_chart;
392 385 }
393 386 $type = get_post_meta( $chart->ID, Visualizer_Plugin::CF_CHART_TYPE, true );
@@ -409,13 +402,8 @@
409 402 }
410 403
411 404 $date_formats = Visualizer_Source::get_date_formats_if_exists( $series, $data );
412 405
413 - $code = '';
414 - if ( 'd3' === $library ) {
415 - $code = get_post_meta( $chart->ID, Visualizer_Module_AIBuilder::CF_D3_CODE, true );
416 - }
417 -
418 406 return array(
419 407 'type' => $type,
420 408 'series' => $series,
421 409 'settings' => $settings,
@@ -420,9 +408,8 @@
420 408 'series' => $series,
421 409 'settings' => $settings,
422 410 'data' => $data,
423 411 'library' => $library,
424 - 'code' => $code,
425 412 'css' => $css,
426 413 'date_formats' => $date_formats,
427 414 );
428 415 }
@@ -439,9 +426,9 @@
439 426 public static function _sendResponse( $results ) {
440 427 header( 'Content-type: application/json' );
441 428 nocache_headers();
442 429 echo json_encode( $results );
443 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
430 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
444 431 }
445 432
446 433 /**
447 434 * Deletes a chart from database.
@@ -473,10 +460,10 @@
473 460 $success = $chart && $chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
474 461 }
475 462 }
476 463 if ( $success ) {
477 - global $sitepress;
478 - 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;
479 466 $trid = $sitepress->get_element_trid( $chart_id, 'post_' . Visualizer_Plugin::CPT_VISUALIZER );
480 467 $translations = $sitepress->get_element_translations( $trid );
481 468 if ( ! empty( $translations ) ) {
482 469 foreach ( $translations as $translated_post ) {
@@ -536,12 +523,8 @@
536 523 *
537 524 * @access public
538 525 */
539 526 public function renderChartPages() {
540 - if ( ! current_user_can( 'edit_posts' ) ) {
541 - wp_die( __( 'You do not have permission to access this page.', 'visualizer' ) );
542 - }
543 -
544 527 defined( 'IFRAME_REQUEST' ) || define( 'IFRAME_REQUEST', 1 );
545 528 if ( ! defined( 'ET_BUILDER_PRODUCT_VERSION' ) && function_exists( 'et_get_theme_version' ) ) {
546 529 define( 'ET_BUILDER_PRODUCT_VERSION', et_get_theme_version() );
547 530 }
@@ -548,20 +531,12 @@
548 531 // Set current screen for the render chart.
549 532 set_current_screen( 'visualizer_render_chart' );
550 533 // check chart, if chart not exists, will create new one and redirects to the same page with proper chart id
551 534 $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
552 - if ( ! empty( $_POST ) ) {
553 - $_POST = map_deep( $_POST, 'wp_strip_all_tags' );
554 - }
555 - $chart = $chart_id ? get_post( $chart_id ) : null;
556 - 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 ) {
557 536 if ( empty( $_GET['lang'] ) || empty( $_GET['parent_chart_id'] ) ) {
558 537 $this->deleteOldCharts();
559 538 $default_type = isset( $_GET['type'] ) && ! empty( $_GET['type'] ) ? $_GET['type'] : 'line';
560 - $chart_status = Visualizer_Module_Admin::checkChartStatus( $default_type );
561 - if ( ! $chart_status ) {
562 - $default_type = 'line';
563 - }
564 539 $source = new Visualizer_Source_Csv( VISUALIZER_ABSPATH . DIRECTORY_SEPARATOR . 'samples' . DIRECTORY_SEPARATOR . $default_type . '.csv' );
565 540 $source->fetch();
566 541 $chart_id = wp_insert_post(
567 542 array(
@@ -588,33 +563,35 @@
588 563
589 564 do_action( 'visualizer_pro_new_chart_defaults', $chart_id );
590 565 }
591 566 } else {
592 - $parent_chart_id = filter_var( $_GET['parent_chart_id'], FILTER_VALIDATE_INT );
593 - $success = false;
594 - if ( $parent_chart_id ) {
595 - $parent_chart = get_post( $parent_chart_id );
596 - $success = $parent_chart && $parent_chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
597 - }
598 - if ( $success ) {
599 - $new_chart_id = wp_insert_post(
600 - array(
601 - 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
602 - 'post_title' => 'Visualization',
603 - 'post_author' => get_current_user_id(),
604 - 'post_status' => $parent_chart->post_status,
605 - 'post_content' => $parent_chart->post_content,
606 - )
607 - );
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 + );
608 584
609 - if ( is_wp_error( $new_chart_id ) ) {
610 - 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__ );
611 - } else {
612 - $post_meta = get_post_meta( $parent_chart_id );
613 - $chart_id = $new_chart_id;
614 - foreach ( $post_meta as $key => $value ) {
615 - if ( strpos( $key, 'visualizer-' ) !== false ) {
616 - add_post_meta( $new_chart_id, $key, maybe_unserialize( $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 + }
617 594 }
618 595 }
619 596 }
620 597 }
@@ -621,9 +598,9 @@
621 598 do_action( 'visualizer_pro_new_chart_defaults', $chart_id );
622 599 }
623 600 wp_redirect( esc_url_raw( add_query_arg( 'chart', (int) $chart_id ) ) );
624 601
625 - if ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) {
602 + if ( defined( 'WP_TESTS_DOMAIN' ) ) {
626 603 wp_die();
627 604 }
628 605 exit();
629 606 }
@@ -719,9 +696,9 @@
719 696 default:
720 697 // this should never happen.
721 698 break;
722 699 }
723 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
700 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
724 701 }
725 702
726 703 /**
727 704 * Load code editor assets.
@@ -751,9 +728,9 @@
751 728 wp_register_script( 'visualizer-codemirror-matchbrackets', '//codemirror.net/addon/edit/matchbrackets.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
752 729 wp_register_script( 'visualizer-codemirror-closebrackets', '//codemirror.net/addon/edit/closebrackets.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
753 730 wp_register_script( 'visualizer-codemirror-sql', '//codemirror.net/mode/sql/sql.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
754 731 wp_register_script( 'visualizer-codemirror-sql-hint', '//codemirror.net/addon/hint/sql-hint.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
755 - 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 );
756 733 wp_register_style( 'visualizer-codemirror-core', '//codemirror.net/lib/codemirror.css', array(), Visualizer_Plugin::VERSION );
757 734 wp_register_style( 'visualizer-codemirror-hint', '//codemirror.net/addon/hint/show-hint.css', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
758 735
759 736 wp_enqueue_script( 'visualizer-codemirror-hint' );
@@ -767,9 +744,9 @@
767 744 'lineWrapping' => true,
768 745 'dragDrop' => false,
769 746 'matchBrackets' => true,
770 747 'autoCloseBrackets' => true,
771 - 'extraKeys' => array( 'Shift-Space' => 'autocomplete' ),
748 + 'extraKeys' => array( 'Ctrl-Space' => 'autocomplete' ),
772 749 'hintOptions' => array( 'tables' => $table_col_mapping ),
773 750 ),
774 751 )
775 752 );
@@ -803,12 +780,9 @@
803 780 update_option( 'visualizer-map-api-key', $_POST['map_api_key'] );
804 781 }
805 782
806 783 if ( $_SERVER['REQUEST_METHOD'] === 'POST' && isset( $_GET['nonce'] ) && wp_verify_nonce( $_GET['nonce'] ) ) {
807 - $is_canceled = isset( $_POST['cancel'] ) && 1 === intval( $_POST['cancel'] );
808 - $is_newly_created = $this->_chart->post_status === 'auto-draft';
809 -
810 - if ( $is_newly_created && ! $is_canceled ) {
784 + if ( $this->_chart->post_status === 'auto-draft' ) {
811 785 $this->_chart->post_status = 'publish';
812 786
813 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.
814 788 // we do not want any difference in data so disable revisions temporarily.
@@ -816,15 +790,10 @@
816 790
817 791 wp_update_post( $this->_chart->to_array() );
818 792 }
819 793 // save meta data only when it is NOT being canceled.
820 - if ( ! $is_canceled ) {
821 - $post_settings = $_POST;
822 - $existing = get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
823 - if ( isset( $existing['colors'] ) && is_array( $existing['colors'] ) && ! isset( $post_settings['colors'] ) ) {
824 - $post_settings['colors'] = $existing['colors'];
825 - }
826 - 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 );
827 796
828 797 // we will keep a parameter called 'internal_title' that will be set to the given title or, if empty, the chart ID
829 798 // this will help in searching with the chart id.
830 799 $settings = get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
@@ -871,9 +840,8 @@
871 840 wp_enqueue_style( 'visualizer-frame' );
872 841 wp_enqueue_script( 'visualizer-preview' );
873 842 wp_enqueue_script( 'visualizer-chosen' );
874 843 wp_enqueue_script( 'visualizer-render' );
875 - wp_enqueue_code_editor( array( 'type' => 'application/json' ) );
876 844
877 845 if ( Visualizer_Module::can_show_feature( 'simple-editor' ) ) {
878 846 wp_enqueue_script( 'visualizer-editor-simple' );
879 847 wp_localize_script(
@@ -881,10 +849,12 @@
881 849 'visualizer1',
882 850 array(
883 851 'ajax' => array(
884 852 'url' => admin_url( 'admin-ajax.php' ),
885 - 'nonces' => array(),
886 - 'actions' => array(),
853 + 'nonces' => array(
854 + ),
855 + 'actions' => array(
856 + ),
887 857 ),
888 858 )
889 859 );
890 860 }
@@ -895,15 +865,13 @@
895 865 'visualizer-render',
896 866 'visualizer',
897 867 array(
898 868 'l10n' => array(
899 - 'invalid_source' => esc_html__( 'The URL you entered is invalid. Please enter a valid URL.', 'visualizer' ),
900 - 'loading' => esc_html__( 'Loading...', 'visualizer' ),
901 - 'json_error' => esc_html__( 'An error occured in fetching data.', 'visualizer' ),
902 - 'select_columns' => esc_html__( 'Please select a few columns to include in the chart.', 'visualizer' ),
903 - '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' ),
904 - 'copied' => __( 'The data has been copied to your clipboard. Hit Ctrl-V/Cmd-V in your spreadsheet editor to paste the data.', 'visualizer' ),
905 - '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' ),
906 874 ),
907 875 'charts' => array(
908 876 'canvas' => $data,
909 877 'id' => $this->_chart->ID,
@@ -947,10 +915,11 @@
947 915 if ( filter_input( INPUT_GET, 'library', FILTER_VALIDATE_BOOLEAN ) ) {
948 916 $render->button = filter_input( INPUT_GET, 'action' ) === Visualizer_Plugin::ACTION_EDIT_CHART
949 917 ? esc_html__( 'Save Chart', 'visualizer' )
950 918 : esc_html__( 'Create Chart', 'visualizer' );
951 -
952 - $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 + }
953 922 } else {
954 923 $render->button = esc_attr__( 'Insert Chart', 'visualizer' );
955 924 }
956 925
@@ -973,12 +942,12 @@
973 942 * @access private
974 943 */
975 944 private function _handleTypesPage() {
976 945 // process post request
977 - 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' ) ) ) {
978 947 $type = filter_input( INPUT_POST, 'type' );
979 948 $library = filter_input( INPUT_POST, 'chart-library' );
980 - if ( Visualizer_Module_Admin::checkChartStatus( $type ) ) {
949 + if ( in_array( $type, Visualizer_Plugin::getChartTypes(), true ) ) {
981 950 if ( empty( $library ) ) {
982 951 // library cannot be empty.
983 952 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, 'Chart library empty while creating the chart! Aborting...', 'error', __FILE__, __LINE__ );
984 953 return;
@@ -1030,80 +999,8 @@
1030 999 * Processes the CSV that is sent in the request as a string.
1031 1000 *
1032 1001 * @since 3.2.0
1033 1002 */
1034 - /**
1035 - * Determines whether a remote URL serves an XLSX file.
1036 - *
1037 - * Used as a fallback when the URL path has no recognisable file extension
1038 - * (e.g. SharePoint, signed S3 URLs, or "download?id=…" endpoints).
1039 - *
1040 - * Uses wp_safe_remote_get() to block requests to private/loopback addresses,
1041 - * and streams the response to a temp file so no body data is held in memory
1042 - * regardless of whether the server honours the Range header.
1043 - *
1044 - * The check relies on the ZIP magic number (PK\x03\x04) that every XLSX
1045 - * file begins with, making it immune to misleading Content-Type headers
1046 - * such as application/octet-stream. Content-Type is used as a last-resort
1047 - * fallback only when the temp file is empty (e.g. a HEAD-only server).
1048 - *
1049 - * @access private
1050 - * @param string $url The remote URL to probe.
1051 - * @return bool TRUE if the file appears to be XLSX, FALSE otherwise.
1052 - */
1053 - private static function _url_is_xlsx( $url ) {
1054 - $tmpfile = wp_tempnam( 'visualizer_xlsx_probe' );
1055 - if ( ! $tmpfile ) {
1056 - return false;
1057 - }
1058 -
1059 - $response = wp_safe_remote_get(
1060 - $url,
1061 - array(
1062 - 'timeout' => 10,
1063 - 'user-agent' => 'WordPress/' . get_bloginfo( 'version' ),
1064 - 'headers' => array( 'Range' => 'bytes=0-3' ),
1065 - 'stream' => true,
1066 - 'filename' => $tmpfile,
1067 - )
1068 - );
1069 -
1070 - if ( is_wp_error( $response ) ) {
1071 - @unlink( $tmpfile ); // phpcs:ignore WordPress.PHP.NoSilencedErrors
1072 - return false;
1073 - }
1074 -
1075 - $magic = '';
1076 - // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen
1077 - $fh = @fopen( $tmpfile, 'rb' ); // phpcs:ignore WordPress.PHP.NoSilencedErrors
1078 - if ( $fh ) {
1079 - $magic = fread( $fh, 4 ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fread
1080 - fclose( $fh ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose
1081 - }
1082 - @unlink( $tmpfile ); // phpcs:ignore WordPress.PHP.NoSilencedErrors
1083 -
1084 - if ( strlen( $magic ) >= 4 ) {
1085 - // XLSX (and all ZIP-based Office formats) start with PK\x03\x04.
1086 - return $magic === "PK\x03\x04";
1087 - }
1088 -
1089 - // Last resort: server returned an empty body (e.g. ignored Range and
1090 - // returned only headers). Check Content-Type from the same response.
1091 - // application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
1092 - return false !== strpos(
1093 - wp_remote_retrieve_header( $response, 'content-type' ),
1094 - 'spreadsheetml'
1095 - );
1096 - }
1097 -
1098 - /**
1099 - * Parses a raw CSV string or editor payload and returns a source object.
1100 - *
1101 - * @access private
1102 - * @param string $data The raw CSV data string.
1103 - * @param string $editor_type The editor type ('text' or 'tabular').
1104 - * @return Visualizer_Source|null The populated source object, or null on failure.
1105 - */
1106 1003 private function handleCSVasString( $data, $editor_type ) {
1107 1004 $source = null;
1108 1005
1109 1006 switch ( $editor_type ) {
@@ -1118,9 +1015,9 @@
1118 1015 continue;
1119 1016 }
1120 1017 $row = explode( ',', $row );
1121 1018 $row = array_map(
1122 - function ( $r ) {
1019 + function( $r ) {
1123 1020 return '' === $r ? ' ' : $r;
1124 1021 },
1125 1022 $row
1126 1023 );
@@ -1169,9 +1066,9 @@
1169 1066 foreach ( $types as $type ) {
1170 1067 if ( empty( $type ) ) {
1171 1068 $exclude[] = $index;
1172 1069 }
1173 - ++$index;
1070 + $index++;
1174 1071 }
1175 1072
1176 1073 // when N headers are being renamed, the number of headers increases by N
1177 1074 // because of the way datatable duplicates header information
@@ -1231,13 +1128,9 @@
1231 1128 // otherwise, assume this is a normal web request.
1232 1129 $can_die = ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE );
1233 1130
1234 1131 // validate nonce
1235 - if (
1236 - ! isset( $_GET['nonce'] ) ||
1237 - ! wp_verify_nonce( $_GET['nonce'], 'visualizer-upload-data' ) ||
1238 - ! current_user_can( 'edit_posts' )
1239 - ) {
1132 + if ( ! isset( $_GET['nonce'] ) || ! wp_verify_nonce( $_GET['nonce'] ) ) {
1240 1133 if ( ! $can_die ) {
1241 1134 return;
1242 1135 }
1243 1136 status_header( 403 );
@@ -1246,15 +1139,9 @@
1246 1139
1247 1140 // check chart, if chart exists
1248 1141 // do not use filter_input as it does not work for phpunit test cases, use filter_var instead
1249 1142 $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
1250 - $chart = $chart_id ? get_post( $chart_id ) : null;
1251 - if (
1252 - ! $chart_id ||
1253 - ! $chart ||
1254 - $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ||
1255 - ! current_user_can( 'edit_post', $chart_id )
1256 - ) {
1143 + if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ) {
1257 1144 if ( ! $can_die ) {
1258 1145 return;
1259 1146 }
1260 1147 status_header( 400 );
@@ -1301,24 +1188,15 @@
1301 1188 if ( isset( $_POST['remote_data'] ) && function_exists( 'wp_http_validate_url' ) ) {
1302 1189 $remote_data = wp_http_validate_url( $_POST['remote_data'] );
1303 1190 }
1304 1191 if ( false !== $remote_data ) {
1305 - $remote_ext = strtolower( pathinfo( parse_url( $remote_data, PHP_URL_PATH ), PATHINFO_EXTENSION ) );
1306 - if ( 'xlsx' === $remote_ext || ( 'csv' !== $remote_ext && self::_url_is_xlsx( $remote_data ) ) ) {
1307 - $source = new Visualizer_Source_Xlsx_Remote( $remote_data );
1308 - } else {
1309 - $source = new Visualizer_Source_Csv_Remote( $remote_data );
1310 - }
1192 + $source = new Visualizer_Source_Csv_Remote( $remote_data );
1311 1193 if ( isset( $_POST['vz-import-time'] ) ) {
1312 1194 apply_filters( 'visualizer_pro_chart_schedule', $chart_id, $remote_data, $_POST['vz-import-time'] );
1313 1195 }
1314 - } elseif ( isset( $_FILES['local_data'] ) && $_FILES['local_data']['error'] === 0 ) {
1315 - $local_ext = strtolower( pathinfo( isset( $_FILES['local_data']['name'] ) ? $_FILES['local_data']['name'] : '', PATHINFO_EXTENSION ) );
1316 - if ( 'xlsx' === $local_ext ) {
1317 - $source = new Visualizer_Source_Xlsx( $_FILES['local_data']['tmp_name'] );
1318 - } else {
1319 - $source = new Visualizer_Source_Csv( $_FILES['local_data']['tmp_name'] );
1320 - }
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'] );
1321 1199 } elseif ( isset( $_POST['chart_data'] ) && strlen( $_POST['chart_data'] ) > 0 ) {
1322 1200 $source = $this->handleCSVasString( $_POST['chart_data'], $_POST['editor-type'] );
1323 1201 update_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR, $_POST['editor-type'] );
1324 1202 } elseif ( isset( $_POST['table_data'] ) && 'yes' === $_POST['table_data'] ) {
@@ -1325,10 +1203,10 @@
1325 1203 $source = $this->handleTabularData();
1326 1204 update_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR, $_POST['editor-type'] );
1327 1205 } else {
1328 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__ );
1329 - $render->message = esc_html__( 'No CSV file was received. Select a file and try uploading again.', 'visualizer' );
1330 - 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' ) );
1331 1209 }
1332 1210
1333 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__ );
1334 1212
@@ -1376,9 +1254,9 @@
1376 1254 $render->settings = json_encode( $settings );
1377 1255 } else {
1378 1256 $error = $source->get_error();
1379 1257 if ( empty( $error ) ) {
1380 - $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' );
1381 1259 }
1382 1260 $render->message = $error;
1383 1261 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( '%s for chart %d.', $error, $chart_id ), 'error', __FILE__, __LINE__ );
1384 1262 update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, $error );
@@ -1389,9 +1267,9 @@
1389 1267 $render->render();
1390 1268 if ( ! $can_die ) {
1391 1269 return;
1392 1270 }
1393 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
1271 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1394 1272 }
1395 1273
1396 1274 /**
1397 1275 * Clones the chart.
@@ -1445,9 +1323,9 @@
1445 1323 );
1446 1324 }
1447 1325 }
1448 1326
1449 - if ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) {
1327 + if ( defined( 'WP_TESTS_DOMAIN' ) ) {
1450 1328 wp_die();
1451 1329 }
1452 1330 wp_redirect( $redirect );
1453 1331 exit;
@@ -1480,9 +1358,9 @@
1480 1358 }
1481 1359 }
1482 1360 }
1483 1361
1484 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
1362 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1485 1363 }
1486 1364
1487 1365 /**
1488 1366 * Handles chart data page.
@@ -1506,11 +1384,10 @@
1506 1384 'visualizer-render',
1507 1385 'visualizer',
1508 1386 array(
1509 1387 'l10n' => array(
1510 - 'invalid_source' => esc_html__( 'The URL you entered is invalid. Please enter a valid URL.', 'visualizer' ),
1511 - 'loading' => esc_html__( 'Loading...', 'visualizer' ),
1512 - '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' ),
1513 1390 ),
1514 1391 'charts' => array(
1515 1392 'canvas' => $data,
1516 1393 ),
@@ -1536,24 +1413,12 @@
1536 1413 */
1537 1414 public function getQueryData() {
1538 1415 check_ajax_referer( Visualizer_Plugin::ACTION_FETCH_DB_DATA . Visualizer_Plugin::VERSION, 'security' );
1539 1416
1540 - if ( ! current_user_can( 'administrator' ) ) {
1541 - wp_send_json_error( array( 'msg' => __( 'Action not allowed for this user.', 'visualizer' ) ) );
1542 - }
1543 - if ( ! is_super_admin() ) {
1544 - wp_send_json_error( array( 'msg' => __( 'Action not allowed for this user.', 'visualizer' ) ) );
1545 - }
1546 -
1547 - if ( ! Visualizer_Module::is_pro() ) {
1548 - wp_send_json_error( array( 'msg' => __( 'Feature is not available.', 'visualizer' ) ) );
1549 - }
1550 -
1551 1417 $params = wp_parse_args( $_POST['params'] );
1552 1418 $chart_id = filter_var( $params['chart_id'], FILTER_VALIDATE_INT );
1553 - $query = trim( $params['query'], ';' );
1554 1419
1555 - $source = new Visualizer_Source_Query( stripslashes( $query ), $chart_id, $params );
1420 + $source = new Visualizer_Source_Query( stripslashes( $params['query'] ), $chart_id, $params );
1556 1421 $html = $source->fetch( true );
1557 1422 $error = $source->get_error();
1558 1423 if ( ! empty( $error ) ) {
1559 1424 wp_send_json_error( array( 'msg' => $error ) );
@@ -1568,19 +1433,8 @@
1568 1433 */
1569 1434 public function saveQuery() {
1570 1435 check_ajax_referer( Visualizer_Plugin::ACTION_SAVE_DB_QUERY . Visualizer_Plugin::VERSION, 'security' );
1571 1436
1572 - if ( ! current_user_can( 'administrator' ) ) {
1573 - wp_send_json_error( array( 'msg' => __( 'Action not allowed for this user.', 'visualizer' ) ) );
1574 - }
1575 - if ( ! is_super_admin() ) {
1576 - wp_send_json_error( array( 'msg' => __( 'Action not allowed for this user.', 'visualizer' ) ) );
1577 - }
1578 -
1579 - if ( ! Visualizer_Module::is_pro() ) {
1580 - wp_send_json_error( array( 'msg' => __( 'Feature is not available.', 'visualizer' ) ) );
1581 - }
1582 -
1583 1437 $chart_id = filter_input(
1584 1438 INPUT_GET,
1585 1439 'chart',
1586 1440 FILTER_VALIDATE_INT,
@@ -1609,14 +1463,13 @@
1609 1463
1610 1464 $render = new Visualizer_Render_Page_Update();
1611 1465 if ( $chart_id ) {
1612 1466 $params = wp_parse_args( $_POST['params'] );
1613 - $query = trim( $params['query'], ';' );
1614 - $source = new Visualizer_Source_Query( stripslashes( $query ), $chart_id, $params );
1467 + $source = new Visualizer_Source_Query( stripslashes( $params['query'] ), $chart_id, $params );
1615 1468 $source->fetch( false );
1616 1469 $error = $source->get_error();
1617 1470 if ( empty( $error ) ) {
1618 - 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'] ) );
1619 1472 update_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
1620 1473 update_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
1621 1474 update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_SCHEDULE, $hours );
1622 1475 update_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, 0 );
@@ -1645,9 +1498,9 @@
1645 1498 }
1646 1499 }
1647 1500 $render->render();
1648 1501 if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
1649 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
1502 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1650 1503 }
1651 1504 }
1652 1505
1653 1506
@@ -1672,9 +1525,9 @@
1672 1525
1673 1526 $hours = filter_input(
1674 1527 INPUT_POST,
1675 1528 'refresh',
1676 - FILTER_VALIDATE_FLOAT,
1529 + FILTER_VALIDATE_INT,
1677 1530 array(
1678 1531 'options' => array(
1679 1532 'min_range' => -1,
1680 1533 'max_range' => apply_filters( 'visualizer_is_business', false ) ? PHP_INT_MAX : -1,
@@ -1681,9 +1534,9 @@
1681 1534 ),
1682 1535 )
1683 1536 );
1684 1537
1685 - if ( ! is_numeric( $hours ) ) {
1538 + if ( 0 !== $hours && empty( $hours ) ) {
1686 1539 $hours = -1;
1687 1540 }
1688 1541
1689 1542 do_action( 'visualizer_save_filter', $chart_id, $hours );
@@ -1688,9 +1541,9 @@
1688 1541
1689 1542 do_action( 'visualizer_save_filter', $chart_id, $hours );
1690 1543
1691 1544 if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
1692 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
1545 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1693 1546 }
1694 1547 }
1695 1548
1696 1549 /**
@@ -1726,12 +1579,8 @@
1726 1579 // Save the image in the uploads directory.
1727 1580 require_once ABSPATH . '/wp-admin/includes/file.php';
1728 1581 \WP_Filesystem();
1729 1582 global $wp_filesystem;
1730 - if ( ! is_a( $wp_filesystem, 'WP_Filesystem_Base' ) ) {
1731 - $creds = request_filesystem_credentials( site_url() );
1732 - wp_filesystem( $creds );
1733 - }
1734 1583 $upload_file = $wp_filesystem->put_contents( $upload_path . $hashed_filename, $decoded );
1735 1584
1736 1585 // Insert new chart image.
1737 1586 $attachment = array(