PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.7.7
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.7.7
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 -313 4.0.53.7.7 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 );
@@ -169,12 +156,8 @@
169 156 */
170 157 public function getJsonRoots() {
171 158 check_ajax_referer( Visualizer_Plugin::ACTION_JSON_GET_ROOTS . Visualizer_Plugin::VERSION, 'security' );
172 159
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 160 $params = wp_parse_args( $_POST['params'] );
178 161
179 162 $source = new Visualizer_Source_Json( $params );
180 163
@@ -195,12 +178,8 @@
195 178 */
196 179 public function getJsonData() {
197 180 check_ajax_referer( Visualizer_Plugin::ACTION_JSON_GET_DATA . Visualizer_Plugin::VERSION, 'security' );
198 181
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 182 $params = wp_parse_args( $_POST['params'] );
204 183
205 184 $chart_id = $params['chart'];
206 185
@@ -266,16 +245,8 @@
266 245 if ( ! empty( $params['paging'] ) ) {
267 246 add_post_meta( $chart->ID, Visualizer_Plugin::CF_JSON_PAGING, $params['paging'] );
268 247 }
269 248
270 - if ( Visualizer_Module::is_pro() ) {
271 - if ( ! empty( $params['vz_woo_source'] ) ) {
272 - update_post_meta( $chart->ID, Visualizer_Plugin::CF_JSON_WOOCOMMERCE_SOURCE, $params['vz_woo_source'] );
273 - } else {
274 - delete_post_meta( $chart->ID, Visualizer_Plugin::CF_JSON_WOOCOMMERCE_SOURCE );
275 - }
276 - }
277 -
278 249 $time = filter_input(
279 250 INPUT_POST,
280 251 'time',
281 252 FILTER_VALIDATE_INT,
@@ -303,9 +274,9 @@
303 274 $render->data = json_encode( $source->getRawData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ) );
304 275 $render->series = json_encode( $source->getSeries() );
305 276 $render->render();
306 277
307 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
278 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
308 279 }
309 280
310 281
311 282 /**
@@ -381,13 +352,13 @@
381 352 * @since 1.0.0
382 353 *
383 354 * @access private
384 355 *
385 - * @param WP_Post|null $chart The chart object.
356 + * @param WP_Post $chart The chart object.
386 357 *
387 358 * @return array The array of chart data.
388 359 */
389 - private function _getChartArray( $chart = null ) {
360 + private function _getChartArray( WP_Post $chart = null ) {
390 361 if ( is_null( $chart ) ) {
391 362 $chart = $this->_chart;
392 363 }
393 364 $type = get_post_meta( $chart->ID, Visualizer_Plugin::CF_CHART_TYPE, true );
@@ -409,13 +380,8 @@
409 380 }
410 381
411 382 $date_formats = Visualizer_Source::get_date_formats_if_exists( $series, $data );
412 383
413 - $code = '';
414 - if ( 'd3' === $library ) {
415 - $code = get_post_meta( $chart->ID, Visualizer_Module_AIBuilder::CF_D3_CODE, true );
416 - }
417 -
418 384 return array(
419 385 'type' => $type,
420 386 'series' => $series,
421 387 'settings' => $settings,
@@ -420,9 +386,8 @@
420 386 'series' => $series,
421 387 'settings' => $settings,
422 388 'data' => $data,
423 389 'library' => $library,
424 - 'code' => $code,
425 390 'css' => $css,
426 391 'date_formats' => $date_formats,
427 392 );
428 393 }
@@ -439,9 +404,9 @@
439 404 public static function _sendResponse( $results ) {
440 405 header( 'Content-type: application/json' );
441 406 nocache_headers();
442 407 echo json_encode( $results );
443 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
408 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
444 409 }
445 410
446 411 /**
447 412 * Deletes a chart from database.
@@ -473,22 +438,9 @@
473 438 $success = $chart && $chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
474 439 }
475 440 }
476 441 if ( $success ) {
477 - global $sitepress;
478 - if ( Visualizer_Module::is_pro() && ( function_exists( 'icl_get_languages' ) && $sitepress instanceof \SitePress ) ) {
479 - $trid = $sitepress->get_element_trid( $chart_id, 'post_' . Visualizer_Plugin::CPT_VISUALIZER );
480 - $translations = $sitepress->get_element_translations( $trid );
481 - if ( ! empty( $translations ) ) {
482 - foreach ( $translations as $translated_post ) {
483 - wp_delete_post( $translated_post->element_id, true );
484 - }
485 - } else {
486 - wp_delete_post( $chart_id, true );
487 - }
488 - } else {
489 - wp_delete_post( $chart_id, true );
490 - }
442 + wp_delete_post( $chart_id, true );
491 443 }
492 444 if ( $is_post ) {
493 445 self::_sendResponse(
494 446 array(
@@ -536,12 +488,8 @@
536 488 *
537 489 * @access public
538 490 */
539 491 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 492 defined( 'IFRAME_REQUEST' ) || define( 'IFRAME_REQUEST', 1 );
545 493 if ( ! defined( 'ET_BUILDER_PRODUCT_VERSION' ) && function_exists( 'et_get_theme_version' ) ) {
546 494 define( 'ET_BUILDER_PRODUCT_VERSION', et_get_theme_version() );
547 495 }
@@ -548,82 +496,40 @@
548 496 // Set current screen for the render chart.
549 497 set_current_screen( 'visualizer_render_chart' );
550 498 // check chart, if chart not exists, will create new one and redirects to the same page with proper chart id
551 499 $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 ) {
557 - if ( empty( $_GET['lang'] ) || empty( $_GET['parent_chart_id'] ) ) {
558 - $this->deleteOldCharts();
559 - $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 - $source = new Visualizer_Source_Csv( VISUALIZER_ABSPATH . DIRECTORY_SEPARATOR . 'samples' . DIRECTORY_SEPARATOR . $default_type . '.csv' );
565 - $source->fetch();
566 - $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,
567 523 array(
568 - 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
569 - 'post_title' => 'Visualization',
570 - 'post_author' => get_current_user_id(),
571 - 'post_status' => 'auto-draft',
572 - 'post_content' => $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ),
524 + 'focusTarget' => 'datum',
573 525 )
574 526 );
575 - if ( $chart_id && ! is_wp_error( $chart_id ) ) {
576 - add_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_TYPE, $default_type );
577 - add_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, 1 );
578 - add_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
579 - add_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
580 - add_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_LIBRARY, '' );
581 - add_post_meta(
582 - $chart_id,
583 - Visualizer_Plugin::CF_SETTINGS,
584 - array(
585 - 'focusTarget' => 'datum',
586 - )
587 - );
588 -
589 - do_action( 'visualizer_pro_new_chart_defaults', $chart_id );
590 - }
591 - } 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 - );
608 -
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] ) );
617 - }
618 - }
619 - }
620 - }
621 527 do_action( 'visualizer_pro_new_chart_defaults', $chart_id );
622 528 }
623 529 wp_redirect( esc_url_raw( add_query_arg( 'chart', (int) $chart_id ) ) );
624 530
625 - if ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) {
531 + if ( defined( 'WP_TESTS_DOMAIN' ) ) {
626 532 wp_die();
627 533 }
628 534 exit();
629 535 }
@@ -719,9 +625,9 @@
719 625 default:
720 626 // this should never happen.
721 627 break;
722 628 }
723 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
629 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
724 630 }
725 631
726 632 /**
727 633 * Load code editor assets.
@@ -751,9 +657,9 @@
751 657 wp_register_script( 'visualizer-codemirror-matchbrackets', '//codemirror.net/addon/edit/matchbrackets.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
752 658 wp_register_script( 'visualizer-codemirror-closebrackets', '//codemirror.net/addon/edit/closebrackets.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
753 659 wp_register_script( 'visualizer-codemirror-sql', '//codemirror.net/mode/sql/sql.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
754 660 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 );
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 );
756 662 wp_register_style( 'visualizer-codemirror-core', '//codemirror.net/lib/codemirror.css', array(), Visualizer_Plugin::VERSION );
757 663 wp_register_style( 'visualizer-codemirror-hint', '//codemirror.net/addon/hint/show-hint.css', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
758 664
759 665 wp_enqueue_script( 'visualizer-codemirror-hint' );
@@ -767,9 +673,9 @@
767 673 'lineWrapping' => true,
768 674 'dragDrop' => false,
769 675 'matchBrackets' => true,
770 676 'autoCloseBrackets' => true,
771 - 'extraKeys' => array( 'Shift-Space' => 'autocomplete' ),
677 + 'extraKeys' => array( 'Ctrl-Space' => 'autocomplete' ),
772 678 'hintOptions' => array( 'tables' => $table_col_mapping ),
773 679 ),
774 680 )
775 681 );
@@ -803,12 +709,9 @@
803 709 update_option( 'visualizer-map-api-key', $_POST['map_api_key'] );
804 710 }
805 711
806 712 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 ) {
713 + if ( $this->_chart->post_status === 'auto-draft' ) {
811 714 $this->_chart->post_status = 'publish';
812 715
813 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.
814 717 // we do not want any difference in data so disable revisions temporarily.
@@ -816,15 +719,10 @@
816 719
817 720 wp_update_post( $this->_chart->to_array() );
818 721 }
819 722 // 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 );
723 + if ( ! ( isset( $_POST['cancel'] ) && 1 === intval( $_POST['cancel'] ) ) ) {
724 + update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, $_POST );
827 725
828 726 // we will keep a parameter called 'internal_title' that will be set to the given title or, if empty, the chart ID
829 727 // this will help in searching with the chart id.
830 728 $settings = get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
@@ -838,14 +736,8 @@
838 736 if ( empty( $title ) ) {
839 737 $title = $this->_chart->ID;
840 738 }
841 739 $settings['internal_title'] = $title;
842 - $settings_label = isset( $settings['pieResidueSliceLabel'] ) ? $settings['pieResidueSliceLabel'] : '';
843 - if ( empty( $settings_label ) ) {
844 - $settings['pieResidueSliceLabel'] = esc_html__( 'Other', 'visualizer' );
845 - } else {
846 - $settings['pieResidueSliceLabel'] = $settings_label;
847 - }
848 740 update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, $settings );
849 741 }
850 742 $render = new Visualizer_Render_Page_Send();
851 743 $render->text = sprintf( '[visualizer id="%d"]', $this->_chart->ID );
@@ -871,9 +763,8 @@
871 763 wp_enqueue_style( 'visualizer-frame' );
872 764 wp_enqueue_script( 'visualizer-preview' );
873 765 wp_enqueue_script( 'visualizer-chosen' );
874 766 wp_enqueue_script( 'visualizer-render' );
875 - wp_enqueue_code_editor( array( 'type' => 'application/json' ) );
876 767
877 768 if ( Visualizer_Module::can_show_feature( 'simple-editor' ) ) {
878 769 wp_enqueue_script( 'visualizer-editor-simple' );
879 770 wp_localize_script(
@@ -881,10 +772,12 @@
881 772 'visualizer1',
882 773 array(
883 774 'ajax' => array(
884 775 'url' => admin_url( 'admin-ajax.php' ),
885 - 'nonces' => array(),
886 - 'actions' => array(),
776 + 'nonces' => array(
777 + ),
778 + 'actions' => array(
779 + ),
887 780 ),
888 781 )
889 782 );
890 783 }
@@ -895,15 +788,13 @@
895 788 'visualizer-render',
896 789 'visualizer',
897 790 array(
898 791 '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' ),
792 + 'invalid_source' => esc_html__( 'You have entered an invalid URL. Please provide a valid URL.', 'visualizer' ),
793 + 'loading' => esc_html__( 'Loading...', 'visualizer' ),
794 + 'json_error' => esc_html__( 'An error occured in fetching data.', 'visualizer' ),
795 + 'select_columns' => esc_html__( 'Please select a few columns to include in the chart.', 'visualizer' ),
796 + '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 797 ),
907 798 'charts' => array(
908 799 'canvas' => $data,
909 800 'id' => $this->_chart->ID,
@@ -934,9 +825,8 @@
934 825 'page_type' => 'chart',
935 826 'json_tag_separator' => Visualizer_Source_Json::TAG_SEPARATOR,
936 827 'json_tag_separator_view' => Visualizer_Source_Json::TAG_SEPARATOR_VIEW,
937 828 'is_front' => false,
938 - 'rest_base' => get_rest_url( null, 'wc/v3/reports/' ),
939 829 )
940 830 );
941 831
942 832 $render = new Visualizer_Render_Page_Data();
@@ -947,10 +837,11 @@
947 837 if ( filter_input( INPUT_GET, 'library', FILTER_VALIDATE_BOOLEAN ) ) {
948 838 $render->button = filter_input( INPUT_GET, 'action' ) === Visualizer_Plugin::ACTION_EDIT_CHART
949 839 ? esc_html__( 'Save Chart', 'visualizer' )
950 840 : esc_html__( 'Create Chart', 'visualizer' );
951 -
952 - $render->cancel_button = esc_html__( 'Cancel', 'visualizer' );
841 + if ( filter_input( INPUT_GET, 'action' ) === Visualizer_Plugin::ACTION_EDIT_CHART ) {
842 + $render->cancel_button = esc_html__( 'Cancel', 'visualizer' );
843 + }
953 844 } else {
954 845 $render->button = esc_attr__( 'Insert Chart', 'visualizer' );
955 846 }
956 847
@@ -973,12 +864,12 @@
973 864 * @access private
974 865 */
975 866 private function _handleTypesPage() {
976 867 // process post request
977 - if ( $_SERVER['REQUEST_METHOD'] === 'POST' && wp_verify_nonce( filter_input( INPUT_POST, 'nonce' ), 'visualizer-upload-data' ) ) {
868 + if ( $_SERVER['REQUEST_METHOD'] === 'POST' && wp_verify_nonce( filter_input( INPUT_POST, 'nonce' ) ) ) {
978 869 $type = filter_input( INPUT_POST, 'type' );
979 870 $library = filter_input( INPUT_POST, 'chart-library' );
980 - if ( Visualizer_Module_Admin::checkChartStatus( $type ) ) {
871 + if ( in_array( $type, Visualizer_Plugin::getChartTypes(), true ) ) {
981 872 if ( empty( $library ) ) {
982 873 // library cannot be empty.
983 874 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, 'Chart library empty while creating the chart! Aborting...', 'error', __FILE__, __LINE__ );
984 875 return;
@@ -1030,80 +921,8 @@
1030 921 * Processes the CSV that is sent in the request as a string.
1031 922 *
1032 923 * @since 3.2.0
1033 924 */
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 925 private function handleCSVasString( $data, $editor_type ) {
1107 926 $source = null;
1108 927
1109 928 switch ( $editor_type ) {
@@ -1118,9 +937,9 @@
1118 937 continue;
1119 938 }
1120 939 $row = explode( ',', $row );
1121 940 $row = array_map(
1122 - function ( $r ) {
941 + function( $r ) {
1123 942 return '' === $r ? ' ' : $r;
1124 943 },
1125 944 $row
1126 945 );
@@ -1169,9 +988,9 @@
1169 988 foreach ( $types as $type ) {
1170 989 if ( empty( $type ) ) {
1171 990 $exclude[] = $index;
1172 991 }
1173 - ++$index;
992 + $index++;
1174 993 }
1175 994
1176 995 // when N headers are being renamed, the number of headers increases by N
1177 996 // because of the way datatable duplicates header information
@@ -1231,13 +1050,9 @@
1231 1050 // otherwise, assume this is a normal web request.
1232 1051 $can_die = ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE );
1233 1052
1234 1053 // validate nonce
1235 - if (
1236 - ! isset( $_GET['nonce'] ) ||
1237 - ! wp_verify_nonce( $_GET['nonce'], 'visualizer-upload-data' ) ||
1238 - ! current_user_can( 'edit_posts' )
1239 - ) {
1054 + if ( ! isset( $_GET['nonce'] ) || ! wp_verify_nonce( $_GET['nonce'] ) ) {
1240 1055 if ( ! $can_die ) {
1241 1056 return;
1242 1057 }
1243 1058 status_header( 403 );
@@ -1246,15 +1061,9 @@
1246 1061
1247 1062 // check chart, if chart exists
1248 1063 // do not use filter_input as it does not work for phpunit test cases, use filter_var instead
1249 1064 $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 - ) {
1065 + if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ) {
1257 1066 if ( ! $can_die ) {
1258 1067 return;
1259 1068 }
1260 1069 status_header( 400 );
@@ -1295,30 +1104,16 @@
1295 1104 delete_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE );
1296 1105
1297 1106 $source = null;
1298 1107 $render = new Visualizer_Render_Page_Update();
1299 -
1300 - $remote_data = false;
1301 - if ( isset( $_POST['remote_data'] ) && function_exists( 'wp_http_validate_url' ) ) {
1302 - $remote_data = wp_http_validate_url( $_POST['remote_data'] );
1303 - }
1304 - 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 - }
1108 + if ( isset( $_POST['remote_data'] ) && filter_var( $_POST['remote_data'], FILTER_VALIDATE_URL ) ) {
1109 + $source = new Visualizer_Source_Csv_Remote( $_POST['remote_data'] );
1311 1110 if ( isset( $_POST['vz-import-time'] ) ) {
1312 - apply_filters( 'visualizer_pro_chart_schedule', $chart_id, $remote_data, $_POST['vz-import-time'] );
1111 + apply_filters( 'visualizer_pro_chart_schedule', $chart_id, $_POST['remote_data'], $_POST['vz-import-time'] );
1313 1112 }
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 - }
1113 + // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
1114 + } elseif ( isset( $_FILES['local_data'] ) && $_FILES['local_data']['error'] == 0 ) {
1115 + $source = new Visualizer_Source_Csv( $_FILES['local_data']['tmp_name'] );
1321 1116 } elseif ( isset( $_POST['chart_data'] ) && strlen( $_POST['chart_data'] ) > 0 ) {
1322 1117 $source = $this->handleCSVasString( $_POST['chart_data'], $_POST['editor-type'] );
1323 1118 update_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR, $_POST['editor-type'] );
1324 1119 } elseif ( isset( $_POST['table_data'] ) && 'yes' === $_POST['table_data'] ) {
@@ -1325,10 +1120,10 @@
1325 1120 $source = $this->handleTabularData();
1326 1121 update_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR, $_POST['editor-type'] );
1327 1122 } else {
1328 1123 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' ) );
1124 + $render->message = esc_html__( 'CSV file with chart data was not uploaded. Please try again.', 'visualizer' );
1125 + update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, esc_html__( 'CSV file with chart data was not uploaded. Please try again.', 'visualizer' ) );
1331 1126 }
1332 1127
1333 1128 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 1129
@@ -1376,9 +1171,9 @@
1376 1171 $render->settings = json_encode( $settings );
1377 1172 } else {
1378 1173 $error = $source->get_error();
1379 1174 if ( empty( $error ) ) {
1380 - $error = esc_html__( 'The CSV file couldn\'t be read. Check that it\'s properly formatted and try again.', 'visualizer' );
1175 + $error = esc_html__( 'CSV file is broken or invalid. Please try again.', 'visualizer' );
1381 1176 }
1382 1177 $render->message = $error;
1383 1178 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( '%s for chart %d.', $error, $chart_id ), 'error', __FILE__, __LINE__ );
1384 1179 update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, $error );
@@ -1389,9 +1184,9 @@
1389 1184 $render->render();
1390 1185 if ( ! $can_die ) {
1391 1186 return;
1392 1187 }
1393 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
1188 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1394 1189 }
1395 1190
1396 1191 /**
1397 1192 * Clones the chart.
@@ -1438,18 +1233,17 @@
1438 1233 'type' => filter_input( INPUT_GET, 'type' ),
1439 1234 'vaction' => false,
1440 1235 ),
1441 1236 admin_url( 'admin.php' )
1442 - ),
1443 - null,
1444 - 'db'
1237 + )
1445 1238 );
1446 1239 }
1447 1240 }
1448 1241
1449 - if ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) {
1242 + if ( defined( 'WP_TESTS_DOMAIN' ) ) {
1450 1243 wp_die();
1451 1244 }
1245 +
1452 1246 wp_redirect( $redirect );
1453 1247 exit;
1454 1248 }
1455 1249
@@ -1480,9 +1274,9 @@
1480 1274 }
1481 1275 }
1482 1276 }
1483 1277
1484 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
1278 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1485 1279 }
1486 1280
1487 1281 /**
1488 1282 * Handles chart data page.
@@ -1506,11 +1300,10 @@
1506 1300 'visualizer-render',
1507 1301 'visualizer',
1508 1302 array(
1509 1303 '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' ),
1304 + 'invalid_source' => esc_html__( 'You have entered an invalid URL. Please provide a valid URL.', 'visualizer' ),
1305 + 'loading' => esc_html__( 'Loading...', 'visualizer' ),
1513 1306 ),
1514 1307 'charts' => array(
1515 1308 'canvas' => $data,
1516 1309 ),
@@ -1536,24 +1329,12 @@
1536 1329 */
1537 1330 public function getQueryData() {
1538 1331 check_ajax_referer( Visualizer_Plugin::ACTION_FETCH_DB_DATA . Visualizer_Plugin::VERSION, 'security' );
1539 1332
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 1333 $params = wp_parse_args( $_POST['params'] );
1552 1334 $chart_id = filter_var( $params['chart_id'], FILTER_VALIDATE_INT );
1553 - $query = trim( $params['query'], ';' );
1554 1335
1555 - $source = new Visualizer_Source_Query( stripslashes( $query ), $chart_id, $params );
1336 + $source = new Visualizer_Source_Query( stripslashes( $params['query'] ), $chart_id, $params );
1556 1337 $html = $source->fetch( true );
1557 1338 $error = $source->get_error();
1558 1339 if ( ! empty( $error ) ) {
1559 1340 wp_send_json_error( array( 'msg' => $error ) );
@@ -1568,19 +1349,8 @@
1568 1349 */
1569 1350 public function saveQuery() {
1570 1351 check_ajax_referer( Visualizer_Plugin::ACTION_SAVE_DB_QUERY . Visualizer_Plugin::VERSION, 'security' );
1571 1352
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 1353 $chart_id = filter_input(
1584 1354 INPUT_GET,
1585 1355 'chart',
1586 1356 FILTER_VALIDATE_INT,
@@ -1609,14 +1379,13 @@
1609 1379
1610 1380 $render = new Visualizer_Render_Page_Update();
1611 1381 if ( $chart_id ) {
1612 1382 $params = wp_parse_args( $_POST['params'] );
1613 - $query = trim( $params['query'], ';' );
1614 - $source = new Visualizer_Source_Query( stripslashes( $query ), $chart_id, $params );
1383 + $source = new Visualizer_Source_Query( stripslashes( $params['query'] ), $chart_id, $params );
1615 1384 $source->fetch( false );
1616 1385 $error = $source->get_error();
1617 1386 if ( empty( $error ) ) {
1618 - update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY, stripslashes( $query ) );
1387 + update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY, stripslashes( $params['query'] ) );
1619 1388 update_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
1620 1389 update_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
1621 1390 update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_SCHEDULE, $hours );
1622 1391 update_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, 0 );
@@ -1645,9 +1414,9 @@
1645 1414 }
1646 1415 }
1647 1416 $render->render();
1648 1417 if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
1649 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
1418 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1650 1419 }
1651 1420 }
1652 1421
1653 1422
@@ -1672,9 +1441,9 @@
1672 1441
1673 1442 $hours = filter_input(
1674 1443 INPUT_POST,
1675 1444 'refresh',
1676 - FILTER_VALIDATE_FLOAT,
1445 + FILTER_VALIDATE_INT,
1677 1446 array(
1678 1447 'options' => array(
1679 1448 'min_range' => -1,
1680 1449 'max_range' => apply_filters( 'visualizer_is_business', false ) ? PHP_INT_MAX : -1,
@@ -1681,9 +1450,9 @@
1681 1450 ),
1682 1451 )
1683 1452 );
1684 1453
1685 - if ( ! is_numeric( $hours ) ) {
1454 + if ( 0 !== $hours && empty( $hours ) ) {
1686 1455 $hours = -1;
1687 1456 }
1688 1457
1689 1458 do_action( 'visualizer_save_filter', $chart_id, $hours );
@@ -1688,9 +1457,9 @@
1688 1457
1689 1458 do_action( 'visualizer_save_filter', $chart_id, $hours );
1690 1459
1691 1460 if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
1692 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
1461 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1693 1462 }
1694 1463 }
1695 1464
1696 1465 /**
@@ -1726,12 +1495,8 @@
1726 1495 // Save the image in the uploads directory.
1727 1496 require_once ABSPATH . '/wp-admin/includes/file.php';
1728 1497 \WP_Filesystem();
1729 1498 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 1499 $upload_file = $wp_filesystem->put_contents( $upload_path . $hashed_filename, $decoded );
1735 1500
1736 1501 // Insert new chart image.
1737 1502 $attachment = array(