PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.7.1
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.7.1
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 +86 -323 4.0.43.7.1 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,101 +488,47 @@
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 - if ( ! defined( 'ET_BUILDER_PRODUCT_VERSION' ) && function_exists( 'et_get_theme_version' ) ) {
538 - define( 'ET_BUILDER_PRODUCT_VERSION', et_get_theme_version() );
539 - }
540 493 // Set current screen for the render chart.
541 494 set_current_screen( 'visualizer_render_chart' );
542 495 // check chart, if chart not exists, will create new one and redirects to the same page with proper chart id
543 496 $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(
497 + if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ) {
498 + $this->deleteOldCharts();
499 + $default_type = isset( $_GET['type'] ) && ! empty( $_GET['type'] ) ? $_GET['type'] : 'line';
500 + $source = new Visualizer_Source_Csv( VISUALIZER_ABSPATH . DIRECTORY_SEPARATOR . 'samples' . DIRECTORY_SEPARATOR . $default_type . '.csv' );
501 + $source->fetch();
502 + $chart_id = wp_insert_post(
503 + array(
504 + 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
505 + 'post_title' => 'Visualization',
506 + 'post_author' => get_current_user_id(),
507 + 'post_status' => 'auto-draft',
508 + 'post_content' => $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ),
509 + )
510 + );
511 + if ( $chart_id && ! is_wp_error( $chart_id ) ) {
512 + add_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_TYPE, $default_type );
513 + add_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, 1 );
514 + add_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
515 + add_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
516 + add_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_LIBRARY, '' );
517 + add_post_meta(
518 + $chart_id,
519 + Visualizer_Plugin::CF_SETTINGS,
559 520 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 ) ),
521 + 'focusTarget' => 'datum',
565 522 )
566 523 );
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 524 do_action( 'visualizer_pro_new_chart_defaults', $chart_id );
614 525 }
615 - wp_redirect( esc_url_raw( add_query_arg( 'chart', (int) $chart_id ) ) );
616 -
617 - if ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) {
618 - wp_die();
619 - }
620 - exit();
526 + wp_redirect( add_query_arg( 'chart', (int) $chart_id ) );
527 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
621 528 }
622 529
623 530 $_POST['save_chart_image'] = isset( $_POST['save_chart_image'] ) && 'yes' === $_POST['save_chart_image'] ? true : false;
624 - $_POST['lazy_load_chart'] = isset( $_POST['lazy_load_chart'] ) && 'yes' === $_POST['lazy_load_chart'] ? true : false;
625 531
626 532 if ( isset( $_POST['chart-img'] ) && ! empty( $_POST['chart-img'] ) ) {
627 533 $attachment_id = $this->save_chart_image( $_POST['chart-img'], $chart_id, $_POST['save_chart_image'] );
628 534 if ( $attachment_id ) {
@@ -711,9 +617,9 @@
711 617 default:
712 618 // this should never happen.
713 619 break;
714 620 }
715 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
621 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
716 622 }
717 623
718 624 /**
719 625 * Load code editor assets.
@@ -725,9 +631,9 @@
725 631
726 632 // data tables assets.
727 633 wp_register_script( 'visualizer-datatables', VISUALIZER_ABSURL . 'js/lib/datatables.min.js', array( 'jquery-ui-core' ), Visualizer_Plugin::VERSION );
728 634 wp_register_style( 'visualizer-datatables', VISUALIZER_ABSURL . 'css/lib/datatables.min.css', array(), Visualizer_Plugin::VERSION );
729 - wp_register_style( 'visualizer-jquery-ui', sprintf( '//code.jquery.com/ui/%s/themes/smoothness/jquery-ui.css', $wp_scripts->registered['jquery-ui-core']->ver ), array( 'visualizer-datatables' ), Visualizer_Plugin::VERSION );
635 + wp_register_style( 'visualizer-jquery-ui', sprintf( '//ajax.googleapis.com/ajax/libs/jqueryui/%s/themes/smoothness/jquery-ui.css', $wp_scripts->registered['jquery-ui-core']->ver ), array( 'visualizer-datatables' ), Visualizer_Plugin::VERSION );
730 636 wp_enqueue_script( 'visualizer-datatables' );
731 637 wp_enqueue_style( 'visualizer-jquery-ui' );
732 638
733 639 if ( ! Visualizer_Module::is_pro() ) {
@@ -743,9 +649,9 @@
743 649 wp_register_script( 'visualizer-codemirror-matchbrackets', '//codemirror.net/addon/edit/matchbrackets.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
744 650 wp_register_script( 'visualizer-codemirror-closebrackets', '//codemirror.net/addon/edit/closebrackets.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
745 651 wp_register_script( 'visualizer-codemirror-sql', '//codemirror.net/mode/sql/sql.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
746 652 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 );
653 + 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 654 wp_register_style( 'visualizer-codemirror-core', '//codemirror.net/lib/codemirror.css', array(), Visualizer_Plugin::VERSION );
749 655 wp_register_style( 'visualizer-codemirror-hint', '//codemirror.net/addon/hint/show-hint.css', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
750 656
751 657 wp_enqueue_script( 'visualizer-codemirror-hint' );
@@ -759,9 +665,9 @@
759 665 'lineWrapping' => true,
760 666 'dragDrop' => false,
761 667 'matchBrackets' => true,
762 668 'autoCloseBrackets' => true,
763 - 'extraKeys' => array( 'Shift-Space' => 'autocomplete' ),
669 + 'extraKeys' => array( 'Ctrl-Space' => 'autocomplete' ),
764 670 'hintOptions' => array( 'tables' => $table_col_mapping ),
765 671 ),
766 672 )
767 673 );
@@ -795,12 +701,9 @@
795 701 update_option( 'visualizer-map-api-key', $_POST['map_api_key'] );
796 702 }
797 703
798 704 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 ) {
705 + if ( $this->_chart->post_status === 'auto-draft' ) {
803 706 $this->_chart->post_status = 'publish';
804 707
805 708 // 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 709 // we do not want any difference in data so disable revisions temporarily.
@@ -808,15 +711,10 @@
808 711
809 712 wp_update_post( $this->_chart->to_array() );
810 713 }
811 714 // 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 );
715 + if ( ! ( isset( $_POST['cancel'] ) && 1 === intval( $_POST['cancel'] ) ) ) {
716 + update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, $_POST );
819 717
820 718 // we will keep a parameter called 'internal_title' that will be set to the given title or, if empty, the chart ID
821 719 // this will help in searching with the chart id.
822 720 $settings = get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
@@ -830,14 +728,8 @@
830 728 if ( empty( $title ) ) {
831 729 $title = $this->_chart->ID;
832 730 }
833 731 $settings['internal_title'] = $title;
834 - $settings_label = isset( $settings['pieResidueSliceLabel'] ) ? $settings['pieResidueSliceLabel'] : '';
835 - if ( empty( $settings_label ) ) {
836 - $settings['pieResidueSliceLabel'] = esc_html__( 'Other', 'visualizer' );
837 - } else {
838 - $settings['pieResidueSliceLabel'] = $settings_label;
839 - }
840 732 update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, $settings );
841 733 }
842 734 $render = new Visualizer_Render_Page_Send();
843 735 $render->text = sprintf( '[visualizer id="%d"]', $this->_chart->ID );
@@ -863,9 +755,8 @@
863 755 wp_enqueue_style( 'visualizer-frame' );
864 756 wp_enqueue_script( 'visualizer-preview' );
865 757 wp_enqueue_script( 'visualizer-chosen' );
866 758 wp_enqueue_script( 'visualizer-render' );
867 - wp_enqueue_code_editor( array( 'type' => 'application/json' ) );
868 759
869 760 if ( Visualizer_Module::can_show_feature( 'simple-editor' ) ) {
870 761 wp_enqueue_script( 'visualizer-editor-simple' );
871 762 wp_localize_script(
@@ -873,10 +764,12 @@
873 764 'visualizer1',
874 765 array(
875 766 'ajax' => array(
876 767 'url' => admin_url( 'admin-ajax.php' ),
877 - 'nonces' => array(),
878 - 'actions' => array(),
768 + 'nonces' => array(
769 + ),
770 + 'actions' => array(
771 + ),
879 772 ),
880 773 )
881 774 );
882 775 }
@@ -887,15 +780,13 @@
887 780 'visualizer-render',
888 781 'visualizer',
889 782 array(
890 783 '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' ),
784 + 'invalid_source' => esc_html__( 'You have entered an invalid URL. Please provide a valid URL.', 'visualizer' ),
785 + 'loading' => esc_html__( 'Loading...', 'visualizer' ),
786 + 'json_error' => esc_html__( 'An error occured in fetching data.', 'visualizer' ),
787 + 'select_columns' => esc_html__( 'Please select a few columns to include in the chart.', 'visualizer' ),
788 + '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 789 ),
899 790 'charts' => array(
900 791 'canvas' => $data,
901 792 'id' => $this->_chart->ID,
@@ -926,9 +817,8 @@
926 817 'page_type' => 'chart',
927 818 'json_tag_separator' => Visualizer_Source_Json::TAG_SEPARATOR,
928 819 'json_tag_separator_view' => Visualizer_Source_Json::TAG_SEPARATOR_VIEW,
929 820 'is_front' => false,
930 - 'rest_base' => get_rest_url( null, 'wc/v3/reports/' ),
931 821 )
932 822 );
933 823
934 824 $render = new Visualizer_Render_Page_Data();
@@ -939,10 +829,11 @@
939 829 if ( filter_input( INPUT_GET, 'library', FILTER_VALIDATE_BOOLEAN ) ) {
940 830 $render->button = filter_input( INPUT_GET, 'action' ) === Visualizer_Plugin::ACTION_EDIT_CHART
941 831 ? esc_html__( 'Save Chart', 'visualizer' )
942 832 : esc_html__( 'Create Chart', 'visualizer' );
943 -
944 - $render->cancel_button = esc_html__( 'Cancel', 'visualizer' );
833 + if ( filter_input( INPUT_GET, 'action' ) === Visualizer_Plugin::ACTION_EDIT_CHART ) {
834 + $render->cancel_button = esc_html__( 'Cancel', 'visualizer' );
835 + }
945 836 } else {
946 837 $render->button = esc_attr__( 'Insert Chart', 'visualizer' );
947 838 }
948 839
@@ -965,12 +856,12 @@
965 856 * @access private
966 857 */
967 858 private function _handleTypesPage() {
968 859 // process post request
969 - if ( $_SERVER['REQUEST_METHOD'] === 'POST' && wp_verify_nonce( filter_input( INPUT_POST, 'nonce' ), 'visualizer-upload-data' ) ) {
860 + if ( $_SERVER['REQUEST_METHOD'] === 'POST' && wp_verify_nonce( filter_input( INPUT_POST, 'nonce' ) ) ) {
970 861 $type = filter_input( INPUT_POST, 'type' );
971 862 $library = filter_input( INPUT_POST, 'chart-library' );
972 - if ( Visualizer_Module_Admin::checkChartStatus( $type ) ) {
863 + if ( in_array( $type, Visualizer_Plugin::getChartTypes(), true ) ) {
973 864 if ( empty( $library ) ) {
974 865 // library cannot be empty.
975 866 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, 'Chart library empty while creating the chart! Aborting...', 'error', __FILE__, __LINE__ );
976 867 return;
@@ -991,9 +882,9 @@
991 882 Visualizer_Module_Utility::set_defaults( $this->_chart );
992 883
993 884 // redirect to next tab
994 885 // changed by Ash/Upwork
995 - wp_redirect( esc_url_raw( add_query_arg( 'tab', 'settings' ) ) );
886 + wp_redirect( add_query_arg( 'tab', 'settings' ) );
996 887
997 888 return;
998 889 }
999 890 }
@@ -1022,80 +913,8 @@
1022 913 * Processes the CSV that is sent in the request as a string.
1023 914 *
1024 915 * @since 3.2.0
1025 916 */
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 917 private function handleCSVasString( $data, $editor_type ) {
1099 918 $source = null;
1100 919
1101 920 switch ( $editor_type ) {
@@ -1110,9 +929,9 @@
1110 929 continue;
1111 930 }
1112 931 $row = explode( ',', $row );
1113 932 $row = array_map(
1114 - function ( $r ) {
933 + function( $r ) {
1115 934 return '' === $r ? ' ' : $r;
1116 935 },
1117 936 $row
1118 937 );
@@ -1161,9 +980,9 @@
1161 980 foreach ( $types as $type ) {
1162 981 if ( empty( $type ) ) {
1163 982 $exclude[] = $index;
1164 983 }
1165 - ++$index;
984 + $index++;
1166 985 }
1167 986
1168 987 // when N headers are being renamed, the number of headers increases by N
1169 988 // because of the way datatable duplicates header information
@@ -1223,13 +1042,9 @@
1223 1042 // otherwise, assume this is a normal web request.
1224 1043 $can_die = ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE );
1225 1044
1226 1045 // validate nonce
1227 - if (
1228 - ! isset( $_GET['nonce'] ) ||
1229 - ! wp_verify_nonce( $_GET['nonce'], 'visualizer-upload-data' ) ||
1230 - ! current_user_can( 'edit_posts' )
1231 - ) {
1046 + if ( ! isset( $_GET['nonce'] ) || ! wp_verify_nonce( $_GET['nonce'] ) ) {
1232 1047 if ( ! $can_die ) {
1233 1048 return;
1234 1049 }
1235 1050 status_header( 403 );
@@ -1238,15 +1053,9 @@
1238 1053
1239 1054 // check chart, if chart exists
1240 1055 // do not use filter_input as it does not work for phpunit test cases, use filter_var instead
1241 1056 $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 - ) {
1057 + if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ) {
1249 1058 if ( ! $can_die ) {
1250 1059 return;
1251 1060 }
1252 1061 status_header( 400 );
@@ -1287,30 +1096,16 @@
1287 1096 delete_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE );
1288 1097
1289 1098 $source = null;
1290 1099 $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 - }
1100 + if ( isset( $_POST['remote_data'] ) && filter_var( $_POST['remote_data'], FILTER_VALIDATE_URL ) ) {
1101 + $source = new Visualizer_Source_Csv_Remote( $_POST['remote_data'] );
1303 1102 if ( isset( $_POST['vz-import-time'] ) ) {
1304 - apply_filters( 'visualizer_pro_chart_schedule', $chart_id, $remote_data, $_POST['vz-import-time'] );
1103 + apply_filters( 'visualizer_pro_chart_schedule', $chart_id, $_POST['remote_data'], $_POST['vz-import-time'] );
1305 1104 }
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 - }
1105 + // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
1106 + } elseif ( isset( $_FILES['local_data'] ) && $_FILES['local_data']['error'] == 0 ) {
1107 + $source = new Visualizer_Source_Csv( $_FILES['local_data']['tmp_name'] );
1313 1108 } elseif ( isset( $_POST['chart_data'] ) && strlen( $_POST['chart_data'] ) > 0 ) {
1314 1109 $source = $this->handleCSVasString( $_POST['chart_data'], $_POST['editor-type'] );
1315 1110 update_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR, $_POST['editor-type'] );
1316 1111 } elseif ( isset( $_POST['table_data'] ) && 'yes' === $_POST['table_data'] ) {
@@ -1317,10 +1112,10 @@
1317 1112 $source = $this->handleTabularData();
1318 1113 update_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR, $_POST['editor-type'] );
1319 1114 } else {
1320 1115 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' ) );
1116 + $render->message = esc_html__( 'CSV file with chart data was not uploaded. Please try again.', 'visualizer' );
1117 + update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, esc_html__( 'CSV file with chart data was not uploaded. Please try again.', 'visualizer' ) );
1323 1118 }
1324 1119
1325 1120 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 1121
@@ -1368,9 +1163,9 @@
1368 1163 $render->settings = json_encode( $settings );
1369 1164 } else {
1370 1165 $error = $source->get_error();
1371 1166 if ( empty( $error ) ) {
1372 - $error = esc_html__( 'The CSV file couldn\'t be read. Check that it\'s properly formatted and try again.', 'visualizer' );
1167 + $error = esc_html__( 'CSV file is broken or invalid. Please try again.', 'visualizer' );
1373 1168 }
1374 1169 $render->message = $error;
1375 1170 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( '%s for chart %d.', $error, $chart_id ), 'error', __FILE__, __LINE__ );
1376 1171 update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, $error );
@@ -1381,9 +1176,9 @@
1381 1176 $render->render();
1382 1177 if ( ! $can_die ) {
1383 1178 return;
1384 1179 }
1385 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
1180 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1386 1181 }
1387 1182
1388 1183 /**
1389 1184 * Clones the chart.
@@ -1422,26 +1217,23 @@
1422 1217 if ( strpos( $key, 'visualizer-' ) !== false ) {
1423 1218 add_post_meta( $new_chart_id, $key, maybe_unserialize( $value[0] ) );
1424 1219 }
1425 1220 }
1426 - $redirect = esc_url(
1427 - add_query_arg(
1428 - array(
1429 - 'page' => 'visualizer',
1430 - 'type' => filter_input( INPUT_GET, 'type' ),
1431 - 'vaction' => false,
1432 - ),
1433 - admin_url( 'admin.php' )
1221 + $redirect = add_query_arg(
1222 + array(
1223 + 'page' => 'visualizer',
1224 + 'type' => filter_input( INPUT_GET, 'type' ),
1225 + 'vaction' => false,
1434 1226 ),
1435 - null,
1436 - 'db'
1227 + admin_url( 'admin.php' )
1437 1228 );
1438 1229 }
1439 1230 }
1440 1231
1441 - if ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) {
1232 + if ( defined( 'WP_TESTS_DOMAIN' ) ) {
1442 1233 wp_die();
1443 1234 }
1235 +
1444 1236 wp_redirect( $redirect );
1445 1237 exit;
1446 1238 }
1447 1239
@@ -1472,9 +1264,9 @@
1472 1264 }
1473 1265 }
1474 1266 }
1475 1267
1476 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
1268 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1477 1269 }
1478 1270
1479 1271 /**
1480 1272 * Handles chart data page.
@@ -1498,11 +1290,10 @@
1498 1290 'visualizer-render',
1499 1291 'visualizer',
1500 1292 array(
1501 1293 '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' ),
1294 + 'invalid_source' => esc_html__( 'You have entered an invalid URL. Please provide a valid URL.', 'visualizer' ),
1295 + 'loading' => esc_html__( 'Loading...', 'visualizer' ),
1505 1296 ),
1506 1297 'charts' => array(
1507 1298 'canvas' => $data,
1508 1299 ),
@@ -1528,24 +1319,12 @@
1528 1319 */
1529 1320 public function getQueryData() {
1530 1321 check_ajax_referer( Visualizer_Plugin::ACTION_FETCH_DB_DATA . Visualizer_Plugin::VERSION, 'security' );
1531 1322
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 1323 $params = wp_parse_args( $_POST['params'] );
1544 1324 $chart_id = filter_var( $params['chart_id'], FILTER_VALIDATE_INT );
1545 - $query = trim( $params['query'], ';' );
1546 1325
1547 - $source = new Visualizer_Source_Query( stripslashes( $query ), $chart_id, $params );
1326 + $source = new Visualizer_Source_Query( stripslashes( $params['query'] ), $chart_id, $params );
1548 1327 $html = $source->fetch( true );
1549 1328 $error = $source->get_error();
1550 1329 if ( ! empty( $error ) ) {
1551 1330 wp_send_json_error( array( 'msg' => $error ) );
@@ -1560,19 +1339,8 @@
1560 1339 */
1561 1340 public function saveQuery() {
1562 1341 check_ajax_referer( Visualizer_Plugin::ACTION_SAVE_DB_QUERY . Visualizer_Plugin::VERSION, 'security' );
1563 1342
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 1343 $chart_id = filter_input(
1576 1344 INPUT_GET,
1577 1345 'chart',
1578 1346 FILTER_VALIDATE_INT,
@@ -1601,14 +1369,13 @@
1601 1369
1602 1370 $render = new Visualizer_Render_Page_Update();
1603 1371 if ( $chart_id ) {
1604 1372 $params = wp_parse_args( $_POST['params'] );
1605 - $query = trim( $params['query'], ';' );
1606 - $source = new Visualizer_Source_Query( stripslashes( $query ), $chart_id, $params );
1373 + $source = new Visualizer_Source_Query( stripslashes( $params['query'] ), $chart_id, $params );
1607 1374 $source->fetch( false );
1608 1375 $error = $source->get_error();
1609 1376 if ( empty( $error ) ) {
1610 - update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY, stripslashes( $query ) );
1377 + update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY, stripslashes( $params['query'] ) );
1611 1378 update_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
1612 1379 update_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
1613 1380 update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_SCHEDULE, $hours );
1614 1381 update_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, 0 );
@@ -1637,9 +1404,9 @@
1637 1404 }
1638 1405 }
1639 1406 $render->render();
1640 1407 if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
1641 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
1408 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1642 1409 }
1643 1410 }
1644 1411
1645 1412
@@ -1664,9 +1431,9 @@
1664 1431
1665 1432 $hours = filter_input(
1666 1433 INPUT_POST,
1667 1434 'refresh',
1668 - FILTER_VALIDATE_FLOAT,
1435 + FILTER_VALIDATE_INT,
1669 1436 array(
1670 1437 'options' => array(
1671 1438 'min_range' => -1,
1672 1439 'max_range' => apply_filters( 'visualizer_is_business', false ) ? PHP_INT_MAX : -1,
@@ -1673,9 +1440,9 @@
1673 1440 ),
1674 1441 )
1675 1442 );
1676 1443
1677 - if ( ! is_numeric( $hours ) ) {
1444 + if ( 0 !== $hours && empty( $hours ) ) {
1678 1445 $hours = -1;
1679 1446 }
1680 1447
1681 1448 do_action( 'visualizer_save_filter', $chart_id, $hours );
@@ -1680,9 +1447,9 @@
1680 1447
1681 1448 do_action( 'visualizer_save_filter', $chart_id, $hours );
1682 1449
1683 1450 if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
1684 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
1451 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1685 1452 }
1686 1453 }
1687 1454
1688 1455 /**
@@ -1718,12 +1485,8 @@
1718 1485 // Save the image in the uploads directory.
1719 1486 require_once ABSPATH . '/wp-admin/includes/file.php';
1720 1487 \WP_Filesystem();
1721 1488 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 1489 $upload_file = $wp_filesystem->put_contents( $upload_path . $hashed_filename, $decoded );
1727 1490
1728 1491 // Insert new chart image.
1729 1492 $attachment = array(