PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.4.7
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.4.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 +135 -524 4.0.73.4.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.
@@ -107,10 +108,11 @@
107 108 */
108 109 public function setJsonSchedule() {
109 110 check_ajax_referer( Visualizer_Plugin::ACTION_JSON_SET_SCHEDULE . Visualizer_Plugin::VERSION, 'security' );
110 111
111 - $chart_id = isset( $_POST['chart'] ) ? filter_var(
112 - $_POST['chart'],
112 + $chart_id = filter_input(
113 + INPUT_POST,
114 + 'chart',
113 115 FILTER_VALIDATE_INT,
114 116 array(
115 117 'options' => array(
116 118 'min_range' => 1,
@@ -115,18 +117,14 @@
115 117 'options' => array(
116 118 'min_range' => 1,
117 119 ),
118 120 )
119 - ) : false;
121 + );
120 122
121 123 if ( ! $chart_id ) {
122 124 wp_send_json_error();
123 125 }
124 126
125 - if ( ! self::can_edit_chart( $chart_id ) ) {
126 - wp_send_json_error( array( 'msg' => esc_html__( 'You do not have permission to perform this action.', 'visualizer' ) ), 403 );
127 - }
128 -
129 127 $time = filter_input(
130 128 INPUT_POST,
131 129 'time',
132 130 FILTER_VALIDATE_INT,
@@ -136,30 +134,12 @@
136 134 ),
137 135 )
138 136 );
139 137
140 - if ( Visualizer_Module::is_pro() ) {
141 - $is_woocommerce_report = filter_input(
142 - INPUT_POST,
143 - 'is_woocommerce_report',
144 - FILTER_VALIDATE_BOOLEAN
145 - );
146 -
147 - if ( $is_woocommerce_report ) {
148 - update_post_meta( $chart_id, Visualizer_Plugin::CF_IS_WOOCOMMERCE_SOURCE, true );
149 - } else {
150 - delete_post_meta( $chart_id, Visualizer_Plugin::CF_IS_WOOCOMMERCE_SOURCE );
151 - }
152 - }
153 -
154 138 delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_SCHEDULE );
155 139
156 140 if ( -1 < $time ) {
157 141 add_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_SCHEDULE, $time );
158 - // Update schedules.
159 - $schedules = get_option( Visualizer_Plugin::CF_JSON_SCHEDULE, array() );
160 - $schedules[ $chart_id ] = time() + $time * HOUR_IN_SECONDS;
161 - update_option( Visualizer_Plugin::CF_JSON_SCHEDULE, $schedules );
162 142 }
163 143 wp_send_json_success();
164 144 }
165 145
@@ -172,12 +152,8 @@
172 152 */
173 153 public function getJsonRoots() {
174 154 check_ajax_referer( Visualizer_Plugin::ACTION_JSON_GET_ROOTS . Visualizer_Plugin::VERSION, 'security' );
175 155
176 - if ( ! current_user_can( 'edit_posts' ) ) {
177 - wp_send_json_error( array( 'msg' => esc_html__( 'You do not have permission to perform this action.', 'visualizer' ) ) );
178 - }
179 -
180 156 $params = wp_parse_args( $_POST['params'] );
181 157
182 158 $source = new Visualizer_Source_Json( $params );
183 159
@@ -198,18 +174,13 @@
198 174 */
199 175 public function getJsonData() {
200 176 check_ajax_referer( Visualizer_Plugin::ACTION_JSON_GET_DATA . Visualizer_Plugin::VERSION, 'security' );
201 177
202 - if ( ! current_user_can( 'edit_posts' ) ) {
203 - wp_send_json_error( array( 'msg' => esc_html__( 'You do not have permission to perform this action.', 'visualizer' ) ) );
204 - }
205 -
206 178 $params = wp_parse_args( $_POST['params'] );
207 179
208 180 $chart_id = $params['chart'];
209 181
210 - $chart = $chart_id ? get_post( $chart_id ) : null;
211 - if ( ! $chart || Visualizer_Plugin::CPT_VISUALIZER !== $chart->post_type || ! current_user_can( 'edit_post', $chart_id ) ) {
182 + if ( empty( $chart_id ) ) {
212 183 wp_die();
213 184 }
214 185
215 186 $source = new Visualizer_Source_Json( $params );
@@ -234,12 +205,12 @@
234 205 public function setJsonData() {
235 206 check_ajax_referer( Visualizer_Plugin::ACTION_JSON_SET_DATA . Visualizer_Plugin::VERSION, 'security' );
236 207
237 208 $params = $_POST;
238 - $chart_id = isset( $_GET['chart'] ) ? absint( $_GET['chart'] ) : 0;
209 + $chart_id = $_GET['chart'];
239 210
240 - if ( ! self::can_edit_chart( $chart_id ) ) {
241 - wp_die( esc_html__( 'You do not have permission to perform this action.', 'visualizer' ), '', array( 'response' => 403 ) );
211 + if ( empty( $chart_id ) ) {
212 + wp_die();
242 213 }
243 214
244 215 $chart = get_post( $chart_id );
245 216
@@ -270,16 +241,8 @@
270 241 if ( ! empty( $params['paging'] ) ) {
271 242 add_post_meta( $chart->ID, Visualizer_Plugin::CF_JSON_PAGING, $params['paging'] );
272 243 }
273 244
274 - if ( Visualizer_Module::is_pro() ) {
275 - if ( ! empty( $params['vz_woo_source'] ) ) {
276 - update_post_meta( $chart->ID, Visualizer_Plugin::CF_JSON_WOOCOMMERCE_SOURCE, $params['vz_woo_source'] );
277 - } else {
278 - delete_post_meta( $chart->ID, Visualizer_Plugin::CF_JSON_WOOCOMMERCE_SOURCE );
279 - }
280 - }
281 -
282 245 $time = filter_input(
283 246 INPUT_POST,
284 247 'time',
285 248 FILTER_VALIDATE_INT,
@@ -307,9 +270,9 @@
307 270 $render->data = json_encode( $source->getRawData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ) );
308 271 $render->series = json_encode( $source->getSeries() );
309 272 $render->render();
310 273
311 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
274 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
312 275 }
313 276
314 277
315 278 /**
@@ -321,14 +284,8 @@
321 284 *
322 285 * @access public
323 286 */
324 287 public function getCharts() {
325 - check_ajax_referer( Visualizer_Plugin::ACTION_GET_CHARTS, 'nonce' );
326 -
327 - if ( ! current_user_can( 'edit_posts' ) ) {
328 - wp_send_json_error( array( 'msg' => esc_html__( 'You do not have permission to perform this action.', 'visualizer' ) ), 403 );
329 - }
330 -
331 288 $query_args = array(
332 289 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
333 290 'posts_per_page' => 9,
334 291 'paged' => filter_input(
@@ -342,11 +299,8 @@
342 299 ),
343 300 )
344 301 ),
345 302 );
346 - if ( ! current_user_can( 'edit_others_posts' ) ) {
347 - $query_args['author'] = get_current_user_id();
348 - }
349 303 $filter = filter_input( INPUT_GET, 's', FILTER_SANITIZE_STRING );
350 304 if ( empty( $filter ) ) {
351 305 // 'filter' is from the modal from the add media button.
352 306 $filter = filter_input( INPUT_GET, 'filter', FILTER_SANITIZE_STRING );
@@ -394,13 +348,13 @@
394 348 * @since 1.0.0
395 349 *
396 350 * @access private
397 351 *
398 - * @param WP_Post|null $chart The chart object.
352 + * @param WP_Post $chart The chart object.
399 353 *
400 354 * @return array The array of chart data.
401 355 */
402 - private function _getChartArray( $chart = null ) {
356 + private function _getChartArray( WP_Post $chart = null ) {
403 357 if ( is_null( $chart ) ) {
404 358 $chart = $this->_chart;
405 359 }
406 360 $type = get_post_meta( $chart->ID, Visualizer_Plugin::CF_CHART_TYPE, true );
@@ -422,13 +376,8 @@
422 376 }
423 377
424 378 $date_formats = Visualizer_Source::get_date_formats_if_exists( $series, $data );
425 379
426 - $code = '';
427 - if ( 'd3' === $library ) {
428 - $code = get_post_meta( $chart->ID, Visualizer_Module_AIBuilder::CF_D3_CODE, true );
429 - }
430 -
431 380 return array(
432 381 'type' => $type,
433 382 'series' => $series,
434 383 'settings' => $settings,
@@ -433,9 +382,8 @@
433 382 'series' => $series,
434 383 'settings' => $settings,
435 384 'data' => $data,
436 385 'library' => $library,
437 - 'code' => $code,
438 386 'css' => $css,
439 387 'date_formats' => $date_formats,
440 388 );
441 389 }
@@ -452,9 +400,9 @@
452 400 public static function _sendResponse( $results ) {
453 401 header( 'Content-type: application/json' );
454 402 nocache_headers();
455 403 echo json_encode( $results );
456 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
404 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
457 405 }
458 406
459 407 /**
460 408 * Deletes a chart from database.
@@ -465,14 +413,16 @@
465 413 * @access public
466 414 */
467 415 public function deleteChart() {
468 416 $is_post = $_SERVER['REQUEST_METHOD'] === 'POST';
469 - $input = $is_post ? $_POST : $_GET;
417 + $input_method = $is_post ? INPUT_POST : INPUT_GET;
470 418 $chart_id = $success = false;
471 - $nonce = isset( $input['nonce'] ) && wp_verify_nonce( $input['nonce'] );
472 - if ( $nonce ) {
473 - $chart_id = isset( $input['chart'] ) ? filter_var(
474 - $input['chart'],
419 + $nonce = wp_verify_nonce( filter_input( $input_method, 'nonce' ) );
420 + $capable = current_user_can( 'delete_posts' );
421 + if ( $nonce && $capable ) {
422 + $chart_id = filter_input(
423 + $input_method,
424 + 'chart',
475 425 FILTER_VALIDATE_INT,
476 426 array(
477 427 'options' => array(
478 428 'min_range' => 1,
@@ -477,34 +427,16 @@
477 427 'options' => array(
478 428 'min_range' => 1,
479 429 ),
480 430 )
481 - ) : false;
431 + );
482 432 if ( $chart_id ) {
483 433 $chart = get_post( $chart_id );
484 - $success = $chart
485 - && $chart->post_type === Visualizer_Plugin::CPT_VISUALIZER
486 - && (
487 - current_user_can( 'delete_post', $chart_id )
488 - || ( (int) $chart->post_author === get_current_user_id() && current_user_can( 'delete_posts' ) )
489 - );
434 + $success = $chart && $chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
490 435 }
491 436 }
492 437 if ( $success ) {
493 - global $sitepress;
494 - if ( Visualizer_Module::is_pro() && ( function_exists( 'icl_get_languages' ) && $sitepress instanceof \SitePress ) ) {
495 - $trid = $sitepress->get_element_trid( $chart_id, 'post_' . Visualizer_Plugin::CPT_VISUALIZER );
496 - $translations = $sitepress->get_element_translations( $trid );
497 - if ( ! empty( $translations ) ) {
498 - foreach ( $translations as $translated_post ) {
499 - wp_delete_post( $translated_post->element_id, true );
500 - }
501 - } else {
502 - wp_delete_post( $chart_id, true );
503 - }
504 - } else {
505 - wp_delete_post( $chart_id, true );
506 - }
438 + wp_delete_post( $chart_id, true );
507 439 }
508 440 if ( $is_post ) {
509 441 self::_sendResponse(
510 442 array(
@@ -552,111 +484,44 @@
552 484 *
553 485 * @access public
554 486 */
555 487 public function renderChartPages() {
556 - if ( ! current_user_can( 'edit_posts' ) ) {
557 - wp_die( __( 'You do not have permission to access this page.', 'visualizer' ) );
558 - }
559 -
560 488 defined( 'IFRAME_REQUEST' ) || define( 'IFRAME_REQUEST', 1 );
561 - if ( ! defined( 'ET_BUILDER_PRODUCT_VERSION' ) && function_exists( 'et_get_theme_version' ) ) {
562 - define( 'ET_BUILDER_PRODUCT_VERSION', et_get_theme_version() );
563 - }
564 - // Set current screen for the render chart.
565 - set_current_screen( 'visualizer_render_chart' );
566 489 // check chart, if chart not exists, will create new one and redirects to the same page with proper chart id
567 490 $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
568 - if ( ! empty( $_POST ) ) {
569 - $_POST = map_deep( $_POST, 'wp_strip_all_tags' );
570 - }
571 - $chart = $chart_id ? get_post( $chart_id ) : null;
572 - if ( $chart && ! self::can_edit_chart( $chart_id ) ) {
573 - wp_die( esc_html__( 'You do not have permission to access this page.', 'visualizer' ), '', array( 'response' => 403 ) );
574 - }
575 - if ( ! $chart_id || ! $chart || $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ) {
576 - if ( empty( $_GET['lang'] ) || empty( $_GET['parent_chart_id'] ) ) {
577 - $this->deleteOldCharts();
578 - $default_type = isset( $_GET['type'] ) && ! empty( $_GET['type'] ) ? $_GET['type'] : 'line';
579 - $chart_status = Visualizer_Module_Admin::checkChartStatus( $default_type );
580 - if ( ! $chart_status ) {
581 - $default_type = 'line';
582 - }
583 - $source = new Visualizer_Source_Csv( VISUALIZER_ABSPATH . DIRECTORY_SEPARATOR . 'samples' . DIRECTORY_SEPARATOR . $default_type . '.csv' );
584 - $source->fetch();
585 - $chart_id = wp_insert_post(
491 + if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ) {
492 + $this->deleteOldCharts();
493 + $default_type = isset( $_GET['type'] ) && ! empty( $_GET['type'] ) ? $_GET['type'] : 'line';
494 + $source = new Visualizer_Source_Csv( VISUALIZER_ABSPATH . DIRECTORY_SEPARATOR . 'samples' . DIRECTORY_SEPARATOR . $default_type . '.csv' );
495 + $source->fetch();
496 + $chart_id = wp_insert_post(
497 + array(
498 + 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
499 + 'post_title' => 'Visualization',
500 + 'post_author' => get_current_user_id(),
501 + 'post_status' => 'auto-draft',
502 + 'post_content' => $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ),
503 + )
504 + );
505 + if ( $chart_id && ! is_wp_error( $chart_id ) ) {
506 + add_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_TYPE, $default_type );
507 + add_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, 1 );
508 + add_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
509 + add_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
510 + add_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_LIBRARY, '' );
511 + add_post_meta(
512 + $chart_id,
513 + Visualizer_Plugin::CF_SETTINGS,
586 514 array(
587 - 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
588 - 'post_title' => 'Visualization',
589 - 'post_author' => get_current_user_id(),
590 - 'post_status' => 'auto-draft',
591 - 'post_content' => $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ),
515 + 'focusTarget' => 'datum',
592 516 )
593 517 );
594 - if ( $chart_id && ! is_wp_error( $chart_id ) ) {
595 - add_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_TYPE, $default_type );
596 - add_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, 1 );
597 - add_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
598 - add_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
599 - add_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_LIBRARY, '' );
600 - add_post_meta(
601 - $chart_id,
602 - Visualizer_Plugin::CF_SETTINGS,
603 - array(
604 - 'focusTarget' => 'datum',
605 - )
606 - );
607 -
608 - do_action( 'visualizer_pro_new_chart_defaults', $chart_id );
609 - }
610 - } else {
611 - $parent_chart_id = filter_var( $_GET['parent_chart_id'], FILTER_VALIDATE_INT );
612 - $success = false;
613 - if ( $parent_chart_id && self::can_edit_chart( $parent_chart_id ) ) {
614 - $parent_chart = get_post( $parent_chart_id );
615 - $success = $parent_chart && $parent_chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
616 - }
617 - if ( $success ) {
618 - $new_chart_id = wp_insert_post(
619 - array(
620 - 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
621 - 'post_title' => 'Visualization',
622 - 'post_author' => get_current_user_id(),
623 - 'post_status' => $parent_chart->post_status,
624 - 'post_content' => $parent_chart->post_content,
625 - )
626 - );
627 -
628 - if ( is_wp_error( $new_chart_id ) ) {
629 - do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Error while cloning chart %d = %s', $parent_chart_id, print_r( $new_chart_id, true ) ), 'error', __FILE__, __LINE__ );
630 - } else {
631 - $post_meta = get_post_meta( $parent_chart_id );
632 - $chart_id = $new_chart_id;
633 - foreach ( $post_meta as $key => $value ) {
634 - if ( strpos( $key, 'visualizer-' ) !== false ) {
635 - add_post_meta( $new_chart_id, $key, self::maybe_decode_content( $value[0] ) );
636 - }
637 - }
638 - }
639 - }
640 518 do_action( 'visualizer_pro_new_chart_defaults', $chart_id );
641 519 }
642 - wp_redirect( esc_url_raw( add_query_arg( 'chart', (int) $chart_id ) ) );
643 -
644 - if ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) {
645 - wp_die();
646 - }
647 - exit();
520 + wp_redirect( add_query_arg( 'chart', (int) $chart_id ) );
521 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
648 522 }
649 523
650 - $_POST['save_chart_image'] = isset( $_POST['save_chart_image'] ) && 'yes' === $_POST['save_chart_image'] ? true : false;
651 - $_POST['lazy_load_chart'] = isset( $_POST['lazy_load_chart'] ) && 'yes' === $_POST['lazy_load_chart'] ? true : false;
652 -
653 - if ( isset( $_POST['chart-img'] ) && ! empty( $_POST['chart-img'] ) ) {
654 - $attachment_id = $this->save_chart_image( $_POST['chart-img'], $chart_id, $_POST['save_chart_image'] );
655 - if ( $attachment_id ) {
656 - update_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_IMAGE, $attachment_id );
657 - }
658 - }
659 524 $lib = $this->load_chart_type( $chart_id );
660 525
661 526 // the alpha color picker (RGBA) is not supported by google.
662 527 $color_picker_dep = 'wp-color-picker';
@@ -718,16 +583,8 @@
718 583 // if the edit button is clicked.
719 584 $this->_chart = $this->handleExistingRevisions( $chart_id, $this->_chart );
720 585 }
721 586
722 - // Clear existing chart cache.
723 - if ( isset( $_POST['save'] ) && 1 === intval( $_POST['save'] ) ) {
724 - $cache_key = Visualizer_Plugin::CF_CHART_CACHE . '_' . $chart_id;
725 - if ( get_transient( $cache_key ) ) {
726 - delete_transient( $cache_key );
727 - }
728 - }
729 -
730 587 switch ( $tab ) {
731 588 case 'settings':
732 589 $this->_handleDataAndSettingsPage();
733 590 break;
@@ -738,9 +595,9 @@
738 595 default:
739 596 // this should never happen.
740 597 break;
741 598 }
742 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
599 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
743 600 }
744 601
745 602 /**
746 603 * Load code editor assets.
@@ -752,9 +609,9 @@
752 609
753 610 // data tables assets.
754 611 wp_register_script( 'visualizer-datatables', VISUALIZER_ABSURL . 'js/lib/datatables.min.js', array( 'jquery-ui-core' ), Visualizer_Plugin::VERSION );
755 612 wp_register_style( 'visualizer-datatables', VISUALIZER_ABSURL . 'css/lib/datatables.min.css', array(), Visualizer_Plugin::VERSION );
756 - 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 );
613 + 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 );
757 614 wp_enqueue_script( 'visualizer-datatables' );
758 615 wp_enqueue_style( 'visualizer-jquery-ui' );
759 616
760 617 if ( ! Visualizer_Module::is_pro() ) {
@@ -770,9 +627,9 @@
770 627 wp_register_script( 'visualizer-codemirror-matchbrackets', '//codemirror.net/addon/edit/matchbrackets.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
771 628 wp_register_script( 'visualizer-codemirror-closebrackets', '//codemirror.net/addon/edit/closebrackets.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
772 629 wp_register_script( 'visualizer-codemirror-sql', '//codemirror.net/mode/sql/sql.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
773 630 wp_register_script( 'visualizer-codemirror-sql-hint', '//codemirror.net/addon/hint/sql-hint.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
774 - wp_register_script( 'visualizer-codemirror-hint', '//codemirror.net/addon/hint/show-hint.js', array( 'visualizer-codemirror-sql', 'visualizer-codemirror-sql-hint', 'visualizer-codemirror-placeholder', 'visualizer-codemirror-matchbrackets', 'visualizer-codemirror-closebrackets' ), Visualizer_Plugin::VERSION );
631 + wp_register_script( 'visualizer-codemirror-hint', '//codemirror.net/addon/hint/show-hint.js', array( 'visualizer-codemirror-sql', 'visualizer-codemirror-sql-hint', 'visualizer-codemirror-placeholder', 'visualizer-codemirror-matchbrackets', 'visualizer-codemirror-closebrackets' ), Visualizer_Plugin::VERSION );
775 632 wp_register_style( 'visualizer-codemirror-core', '//codemirror.net/lib/codemirror.css', array(), Visualizer_Plugin::VERSION );
776 633 wp_register_style( 'visualizer-codemirror-hint', '//codemirror.net/addon/hint/show-hint.css', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
777 634
778 635 wp_enqueue_script( 'visualizer-codemirror-hint' );
@@ -786,9 +643,9 @@
786 643 'lineWrapping' => true,
787 644 'dragDrop' => false,
788 645 'matchBrackets' => true,
789 646 'autoCloseBrackets' => true,
790 - 'extraKeys' => array( 'Shift-Space' => 'autocomplete' ),
647 + 'extraKeys' => array( 'Ctrl-Space' => 'autocomplete' ),
791 648 'hintOptions' => array( 'tables' => $table_col_mapping ),
792 649 ),
793 650 )
794 651 );
@@ -817,17 +674,14 @@
817 674 /**
818 675 * Handle data and settings page
819 676 */
820 677 private function _handleDataAndSettingsPage() {
678 + if ( isset( $_POST['map_api_key'] ) ) {
679 + update_option( 'visualizer-map-api-key', $_POST['map_api_key'] );
680 + }
681 +
821 682 if ( $_SERVER['REQUEST_METHOD'] === 'POST' && isset( $_GET['nonce'] ) && wp_verify_nonce( $_GET['nonce'] ) ) {
822 - $is_canceled = isset( $_POST['cancel'] ) && 1 === intval( $_POST['cancel'] );
823 - $is_newly_created = $this->_chart->post_status === 'auto-draft';
824 -
825 - if ( isset( $_POST['map_api_key'] ) && current_user_can( 'manage_options' ) ) {
826 - update_option( 'visualizer-map-api-key', sanitize_text_field( wp_unslash( $_POST['map_api_key'] ) ) );
827 - }
828 -
829 - if ( $is_newly_created && ! $is_canceled ) {
683 + if ( $this->_chart->post_status === 'auto-draft' ) {
830 684 $this->_chart->post_status = 'publish';
831 685
832 686 // ensure that a revision is not created. If a revision is created it will have the proper data and the parent of the revision will have default data.
833 687 // we do not want any difference in data so disable revisions temporarily.
@@ -835,16 +689,10 @@
835 689
836 690 wp_update_post( $this->_chart->to_array() );
837 691 }
838 692 // save meta data only when it is NOT being canceled.
839 - if ( ! $is_canceled ) {
840 - $post_settings = $_POST;
841 - $existing = get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
842 - if ( isset( $existing['colors'] ) && is_array( $existing['colors'] ) && ! isset( $post_settings['colors'] ) ) {
843 - $post_settings['colors'] = $existing['colors'];
844 - }
845 - $post_settings = $this->sanitizeSettings( $post_settings );
846 - update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, $post_settings );
693 + if ( ! ( isset( $_POST['cancel'] ) && 1 === intval( $_POST['cancel'] ) ) ) {
694 + update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, $_POST );
847 695
848 696 // we will keep a parameter called 'internal_title' that will be set to the given title or, if empty, the chart ID
849 697 // this will help in searching with the chart id.
850 698 $settings = get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
@@ -858,14 +706,8 @@
858 706 if ( empty( $title ) ) {
859 707 $title = $this->_chart->ID;
860 708 }
861 709 $settings['internal_title'] = $title;
862 - $settings_label = isset( $settings['pieResidueSliceLabel'] ) ? $settings['pieResidueSliceLabel'] : '';
863 - if ( empty( $settings_label ) ) {
864 - $settings['pieResidueSliceLabel'] = esc_html__( 'Other', 'visualizer' );
865 - } else {
866 - $settings['pieResidueSliceLabel'] = $settings_label;
867 - }
868 710 update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, $settings );
869 711 }
870 712 $render = new Visualizer_Render_Page_Send();
871 713 $render->text = sprintf( '[visualizer id="%d"]', $this->_chart->ID );
@@ -891,9 +733,8 @@
891 733 wp_enqueue_style( 'visualizer-frame' );
892 734 wp_enqueue_script( 'visualizer-preview' );
893 735 wp_enqueue_script( 'visualizer-chosen' );
894 736 wp_enqueue_script( 'visualizer-render' );
895 - wp_enqueue_code_editor( array( 'type' => 'application/json' ) );
896 737
897 738 if ( Visualizer_Module::can_show_feature( 'simple-editor' ) ) {
898 739 wp_enqueue_script( 'visualizer-editor-simple' );
899 740 wp_localize_script(
@@ -901,10 +742,12 @@
901 742 'visualizer1',
902 743 array(
903 744 'ajax' => array(
904 745 'url' => admin_url( 'admin-ajax.php' ),
905 - 'nonces' => array(),
906 - 'actions' => array(),
746 + 'nonces' => array(
747 + ),
748 + 'actions' => array(
749 + ),
907 750 ),
908 751 )
909 752 );
910 753 }
@@ -915,15 +758,13 @@
915 758 'visualizer-render',
916 759 'visualizer',
917 760 array(
918 761 'l10n' => array(
919 - 'invalid_source' => esc_html__( 'The URL you entered is invalid. Please enter a valid URL.', 'visualizer' ),
920 - 'loading' => esc_html__( 'Loading...', 'visualizer' ),
921 - 'json_error' => esc_html__( 'An error occured in fetching data.', 'visualizer' ),
922 - 'select_columns' => esc_html__( 'Please select a few columns to include in the chart.', 'visualizer' ),
923 - 'save_settings' => __( 'You have modified the chart\'s settings. To modify the source/data again, you must save this chart and reopen it for editing. If you continue without saving the chart, you may lose your changes.', 'visualizer' ),
924 - 'copied' => __( 'The data has been copied to your clipboard. Hit Ctrl-V/Cmd-V in your spreadsheet editor to paste the data.', 'visualizer' ),
925 - 'invalid_format' => esc_html__( 'This format pattern is not supported in the series settings field. Use the Manual Configuration option instead.', 'visualizer' ),
762 + 'invalid_source' => esc_html__( 'You have entered an invalid URL. Please provide a valid URL.', 'visualizer' ),
763 + 'loading' => esc_html__( 'Loading...', 'visualizer' ),
764 + 'json_error' => esc_html__( 'An error occured in fetching data.', 'visualizer' ),
765 + 'select_columns' => esc_html__( 'Please select a few columns to include in the chart.', 'visualizer' ),
766 + 'save_settings' => __( 'You have modified the chart\'s settings. To modify the source/data again, you must save this chart and reopen it for editing. If you continue without saving the chart, you may lose your changes.', 'visualizer' ),
926 767 ),
927 768 'charts' => array(
928 769 'canvas' => $data,
929 770 'id' => $this->_chart->ID,
@@ -954,9 +795,8 @@
954 795 'page_type' => 'chart',
955 796 'json_tag_separator' => Visualizer_Source_Json::TAG_SEPARATOR,
956 797 'json_tag_separator_view' => Visualizer_Source_Json::TAG_SEPARATOR_VIEW,
957 798 'is_front' => false,
958 - 'rest_base' => get_rest_url( null, 'wc/v3/reports/' ),
959 799 )
960 800 );
961 801
962 802 $render = new Visualizer_Render_Page_Data();
@@ -967,10 +807,11 @@
967 807 if ( filter_input( INPUT_GET, 'library', FILTER_VALIDATE_BOOLEAN ) ) {
968 808 $render->button = filter_input( INPUT_GET, 'action' ) === Visualizer_Plugin::ACTION_EDIT_CHART
969 809 ? esc_html__( 'Save Chart', 'visualizer' )
970 810 : esc_html__( 'Create Chart', 'visualizer' );
971 -
972 - $render->cancel_button = esc_html__( 'Cancel', 'visualizer' );
811 + if ( filter_input( INPUT_GET, 'action' ) === Visualizer_Plugin::ACTION_EDIT_CHART ) {
812 + $render->cancel_button = esc_html__( 'Cancel', 'visualizer' );
813 + }
973 814 } else {
974 815 $render->button = esc_attr__( 'Insert Chart', 'visualizer' );
975 816 }
976 817
@@ -993,12 +834,12 @@
993 834 * @access private
994 835 */
995 836 private function _handleTypesPage() {
996 837 // process post request
997 - if ( $_SERVER['REQUEST_METHOD'] === 'POST' && wp_verify_nonce( filter_input( INPUT_POST, 'nonce' ), 'visualizer-upload-data' ) ) {
838 + if ( $_SERVER['REQUEST_METHOD'] === 'POST' && wp_verify_nonce( filter_input( INPUT_POST, 'nonce' ) ) ) {
998 839 $type = filter_input( INPUT_POST, 'type' );
999 840 $library = filter_input( INPUT_POST, 'chart-library' );
1000 - if ( Visualizer_Module_Admin::checkChartStatus( $type ) ) {
841 + if ( in_array( $type, Visualizer_Plugin::getChartTypes(), true ) ) {
1001 842 if ( empty( $library ) ) {
1002 843 // library cannot be empty.
1003 844 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, 'Chart library empty while creating the chart! Aborting...', 'error', __FILE__, __LINE__ );
1004 845 return;
@@ -1019,9 +860,9 @@
1019 860 Visualizer_Module_Utility::set_defaults( $this->_chart );
1020 861
1021 862 // redirect to next tab
1022 863 // changed by Ash/Upwork
1023 - wp_redirect( esc_url_raw( add_query_arg( 'tab', 'settings' ) ) );
864 + wp_redirect( add_query_arg( 'tab', 'settings' ) );
1024 865
1025 866 return;
1026 867 }
1027 868 }
@@ -1034,35 +875,8 @@
1034 875 wp_iframe( array( $render, 'render' ) );
1035 876 }
1036 877
1037 878 /**
1038 - * Sanitize settings data from the request.
1039 - *
1040 - * @param array<string, mixed> $post_data The POST data to sanitize.
1041 - * @return array<string, mixed> The sanitized settings data.
1042 - */
1043 - private function sanitizeSettings( $post_data ): array {
1044 - $chart_img = '';
1045 - if ( isset( $post_data['chart-img'] ) ) {
1046 - $chart_img = wp_unslash( $post_data['chart-img'] );
1047 - unset( $post_data['chart-img'] );
1048 - }
1049 -
1050 - $post_data = map_deep(
1051 - $post_data,
1052 - 'sanitize_textarea_field'
1053 - );
1054 -
1055 - // The value is a client-side canvas export; keep it only when it is a
1056 - // base64 image data URI so nothing else is ever stored unsanitized.
1057 - if ( is_string( $chart_img ) && preg_match( '#^data:image/(png|jpeg|webp);base64,[A-Za-z0-9+/ ]+=*$#', $chart_img ) ) {
1058 - $post_data['chart-img'] = $chart_img;
1059 - }
1060 -
1061 - return $post_data;
1062 - }
1063 -
1064 - /**
1065 879 * Renders flattr script in the iframe <head>
1066 880 *
1067 881 * @since 1.4.2
1068 882 * @action admin_head
@@ -1077,81 +891,8 @@
1077 891 * Processes the CSV that is sent in the request as a string.
1078 892 *
1079 893 * @since 3.2.0
1080 894 */
1081 - /**
1082 - * Determines whether a remote URL serves an XLSX file.
1083 - *
1084 - * Used as a fallback when the URL path has no recognisable file extension
1085 - * (e.g. SharePoint, signed S3 URLs, or "download?id=…" endpoints).
1086 - *
1087 - * Uses the shared remote-fetch policy to block non-public destinations,
1088 - * and streams the response to a temp file so no body data is held in memory
1089 - * regardless of whether the server honours the Range header.
1090 - *
1091 - * The check relies on the ZIP magic number (PK\x03\x04) that every XLSX
1092 - * file begins with, making it immune to misleading Content-Type headers
1093 - * such as application/octet-stream. Content-Type is used as a last-resort
1094 - * fallback only when the temp file is empty (e.g. a HEAD-only server).
1095 - *
1096 - * @access private
1097 - * @param string $url The remote URL to probe.
1098 - * @return bool TRUE if the file appears to be XLSX, FALSE otherwise.
1099 - */
1100 - private static function _url_is_xlsx( $url ) {
1101 - $tmpfile = wp_tempnam( 'visualizer_xlsx_probe' );
1102 - if ( ! $tmpfile ) {
1103 - return false;
1104 - }
1105 -
1106 - $response = Visualizer_Remote_Fetch::request(
1107 - $url,
1108 - array(
1109 - 'timeout' => 10,
1110 - 'user-agent' => 'WordPress/' . get_bloginfo( 'version' ),
1111 - 'headers' => array( 'Range' => 'bytes=0-3' ),
1112 - 'stream' => true,
1113 - 'filename' => $tmpfile,
1114 - 'limit_response_size' => 4,
1115 - )
1116 - );
1117 -
1118 - if ( is_wp_error( $response ) ) {
1119 - @unlink( $tmpfile ); // phpcs:ignore WordPress.PHP.NoSilencedErrors
1120 - return false;
1121 - }
1122 -
1123 - $magic = '';
1124 - // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen
1125 - $fh = @fopen( $tmpfile, 'rb' ); // phpcs:ignore WordPress.PHP.NoSilencedErrors
1126 - if ( $fh ) {
1127 - $magic = fread( $fh, 4 ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fread
1128 - fclose( $fh ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose
1129 - }
1130 - @unlink( $tmpfile ); // phpcs:ignore WordPress.PHP.NoSilencedErrors
1131 -
1132 - if ( strlen( $magic ) >= 4 ) {
1133 - // XLSX (and all ZIP-based Office formats) start with PK\x03\x04.
1134 - return $magic === "PK\x03\x04";
1135 - }
1136 -
1137 - // Last resort: server returned an empty body (e.g. ignored Range and
1138 - // returned only headers). Check Content-Type from the same response.
1139 - // application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
1140 - return false !== strpos(
1141 - wp_remote_retrieve_header( $response, 'content-type' ),
1142 - 'spreadsheetml'
1143 - );
1144 - }
1145 -
1146 - /**
1147 - * Parses a raw CSV string or editor payload and returns a source object.
1148 - *
1149 - * @access private
1150 - * @param string $data The raw CSV data string.
1151 - * @param string $editor_type The editor type ('text' or 'tabular').
1152 - * @return Visualizer_Source|null The populated source object, or null on failure.
1153 - */
1154 895 private function handleCSVasString( $data, $editor_type ) {
1155 896 $source = null;
1156 897
1157 898 switch ( $editor_type ) {
@@ -1164,16 +905,8 @@
1164 905 foreach ( $values as $row ) {
1165 906 if ( empty( $row ) ) {
1166 907 continue;
1167 908 }
1168 - $row = explode( ',', $row );
1169 - $row = array_map(
1170 - function ( $r ) {
1171 - return '' === $r ? ' ' : $r;
1172 - },
1173 - $row
1174 - );
1175 - $row = implode( ',', $row );
1176 909 // don't use fpucsv here because we need to just dump the data
1177 910 // minus the empty rows
1178 911 // because fputcsv needs to tokenize
1179 912 // we can standardize the CSV enclosure here and replace all ' with "
@@ -1217,9 +950,9 @@
1217 950 foreach ( $types as $type ) {
1218 951 if ( empty( $type ) ) {
1219 952 $exclude[] = $index;
1220 953 }
1221 - ++$index;
954 + $index++;
1222 955 }
1223 956
1224 957 // when N headers are being renamed, the number of headers increases by N
1225 958 // because of the way datatable duplicates header information
@@ -1277,16 +1010,11 @@
1277 1010 public function uploadData() {
1278 1011 // if this is being called internally from pro and VISUALIZER_DO_NOT_DIE is set.
1279 1012 // otherwise, assume this is a normal web request.
1280 1013 $can_die = ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE );
1281 - // $can_die also gates the capability checks below, so VISUALIZER_DO_NOT_DIE must stay internal-only (never set from request input or globally).
1282 1014
1283 - // validate nonce; capability check applies to web requests only, not trusted internal calls.
1284 - if (
1285 - ! isset( $_GET['nonce'] ) ||
1286 - ! wp_verify_nonce( $_GET['nonce'], 'visualizer-upload-data' ) ||
1287 - ( $can_die && ! current_user_can( 'edit_posts' ) )
1288 - ) {
1015 + // validate nonce
1016 + if ( ! isset( $_GET['nonce'] ) || ! wp_verify_nonce( $_GET['nonce'] ) ) {
1289 1017 if ( ! $can_die ) {
1290 1018 return;
1291 1019 }
1292 1020 status_header( 403 );
@@ -1295,15 +1023,9 @@
1295 1023
1296 1024 // check chart, if chart exists
1297 1025 // do not use filter_input as it does not work for phpunit test cases, use filter_var instead
1298 1026 $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
1299 - $chart = $chart_id ? get_post( $chart_id ) : null;
1300 - if (
1301 - ! $chart_id ||
1302 - ! $chart ||
1303 - $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ||
1304 - ( $can_die && ! current_user_can( 'edit_post', $chart_id ) )
1305 - ) {
1027 + if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ) {
1306 1028 if ( ! $can_die ) {
1307 1029 return;
1308 1030 }
1309 1031 status_header( 400 );
@@ -1339,35 +1061,18 @@
1339 1061
1340 1062 // delete editor related data.
1341 1063 delete_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR );
1342 1064
1343 - // delete this so that a JSON import can be later edited manually without a problem.
1344 - delete_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE );
1345 -
1346 1065 $source = null;
1347 1066 $render = new Visualizer_Render_Page_Update();
1348 -
1349 - $remote_data = false;
1350 - if ( isset( $_POST['remote_data'] ) && function_exists( 'wp_http_validate_url' ) ) {
1351 - $remote_data = wp_http_validate_url( $_POST['remote_data'] );
1352 - }
1353 - if ( false !== $remote_data ) {
1354 - $remote_ext = strtolower( pathinfo( parse_url( $remote_data, PHP_URL_PATH ), PATHINFO_EXTENSION ) );
1355 - if ( 'xlsx' === $remote_ext || ( 'csv' !== $remote_ext && self::_url_is_xlsx( $remote_data ) ) ) {
1356 - $source = new Visualizer_Source_Xlsx_Remote( $remote_data );
1357 - } else {
1358 - $source = new Visualizer_Source_Csv_Remote( $remote_data );
1359 - }
1067 + if ( isset( $_POST['remote_data'] ) && filter_var( $_POST['remote_data'], FILTER_VALIDATE_URL ) ) {
1068 + $source = new Visualizer_Source_Csv_Remote( $_POST['remote_data'] );
1360 1069 if ( isset( $_POST['vz-import-time'] ) ) {
1361 - apply_filters( 'visualizer_pro_chart_schedule', $chart_id, $remote_data, $_POST['vz-import-time'] );
1070 + apply_filters( 'visualizer_pro_chart_schedule', $chart_id, $_POST['remote_data'], $_POST['vz-import-time'] );
1362 1071 }
1363 - } elseif ( isset( $_FILES['local_data'] ) && $_FILES['local_data']['error'] === 0 ) {
1364 - $local_ext = strtolower( pathinfo( isset( $_FILES['local_data']['name'] ) ? $_FILES['local_data']['name'] : '', PATHINFO_EXTENSION ) );
1365 - if ( 'xlsx' === $local_ext ) {
1366 - $source = new Visualizer_Source_Xlsx( $_FILES['local_data']['tmp_name'] );
1367 - } else {
1368 - $source = new Visualizer_Source_Csv( $_FILES['local_data']['tmp_name'] );
1369 - }
1072 + // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
1073 + } elseif ( isset( $_FILES['local_data'] ) && $_FILES['local_data']['error'] == 0 ) {
1074 + $source = new Visualizer_Source_Csv( $_FILES['local_data']['tmp_name'] );
1370 1075 } elseif ( isset( $_POST['chart_data'] ) && strlen( $_POST['chart_data'] ) > 0 ) {
1371 1076 $source = $this->handleCSVasString( $_POST['chart_data'], $_POST['editor-type'] );
1372 1077 update_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR, $_POST['editor-type'] );
1373 1078 } elseif ( isset( $_POST['table_data'] ) && 'yes' === $_POST['table_data'] ) {
@@ -1374,10 +1079,10 @@
1374 1079 $source = $this->handleTabularData();
1375 1080 update_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR, $_POST['editor-type'] );
1376 1081 } else {
1377 1082 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'CSV file with chart data was not uploaded for chart %d.', $chart_id ), 'error', __FILE__, __LINE__ );
1378 - $render->message = esc_html__( 'No CSV file was received. Select a file and try uploading again.', 'visualizer' );
1379 - update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, esc_html__( 'No CSV file was received. Select a file and try uploading again.', 'visualizer' ) );
1083 + $render->message = esc_html__( 'CSV file with chart data was not uploaded. Please try again.', 'visualizer' );
1084 + update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, esc_html__( 'CSV file with chart data was not uploaded. Please try again.', 'visualizer' ) );
1380 1085 }
1381 1086
1382 1087 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Uploaded data for chart %d with source %s', $chart_id, print_r( $source, true ) ), 'debug', __FILE__, __LINE__ );
1383 1088
@@ -1384,10 +1089,10 @@
1384 1089 if ( $source ) {
1385 1090 if ( $source->fetch() ) {
1386 1091 $content = $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) );
1387 1092 $populate = true;
1388 - $json = self::decode_content( $content );
1389 - if ( is_array( $json ) ) {
1093 + if ( is_string( $content ) && is_array( unserialize( $content ) ) ) {
1094 + $json = unserialize( $content );
1390 1095 // if source exists, so should data. if source exists but data is blank, do not populate the chart.
1391 1096 // if we populate the data even if it is empty, the chart will show "Table has no columns".
1392 1097 if ( array_key_exists( 'source', $json ) && ! empty( $json['source'] ) && ( ! array_key_exists( 'data', $json ) || empty( $json['data'] ) ) ) {
1393 1098 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Not populating chart data as source exists (%s) but data is empty!', $json['source'] ), 'warn', __FILE__, __LINE__ );
@@ -1398,9 +1103,8 @@
1398 1103 if ( $populate ) {
1399 1104 $chart->post_content = $content;
1400 1105 }
1401 1106 wp_update_post( $chart->to_array() );
1402 -
1403 1107 update_post_meta( $chart->ID, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
1404 1108 update_post_meta( $chart->ID, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
1405 1109 update_post_meta( $chart->ID, Visualizer_Plugin::CF_DEFAULT_DATA, 0 );
1406 1110
@@ -1408,17 +1112,8 @@
1408 1112
1409 1113 Visualizer_Module_Utility::set_defaults( $chart, null );
1410 1114
1411 1115 $settings = get_post_meta( $chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
1412 - if ( isset( $settings['series'] ) && ! ( count( $settings['series'] ) - count( $source->getSeries() ) > 1 ) ) {
1413 - $diff_total_series = abs( count( $settings['series'] ) - count( $source->getSeries() ) );
1414 - if ( $diff_total_series ) {
1415 - foreach ( range( 1, $diff_total_series ) as $k => $diff_series ) {
1416 - $settings['series'][] = end( $settings['series'] );
1417 - }
1418 - update_post_meta( $chart->ID, Visualizer_Plugin::CF_SETTINGS, $settings );
1419 - }
1420 - }
1421 1116
1422 1117 $render->id = $chart->ID;
1423 1118 $render->data = json_encode( $source->getRawData( get_post_meta( $chart->ID, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ) );
1424 1119 $render->series = json_encode( $source->getSeries() );
@@ -1425,9 +1120,9 @@
1425 1120 $render->settings = json_encode( $settings );
1426 1121 } else {
1427 1122 $error = $source->get_error();
1428 1123 if ( empty( $error ) ) {
1429 - $error = esc_html__( 'The CSV file couldn\'t be read. Check that it\'s properly formatted and try again.', 'visualizer' );
1124 + $error = esc_html__( 'CSV file is broken or invalid. Please try again.', 'visualizer' );
1430 1125 }
1431 1126 $render->message = $error;
1432 1127 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( '%s for chart %d.', $error, $chart_id ), 'error', __FILE__, __LINE__ );
1433 1128 update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, $error );
@@ -1438,9 +1133,9 @@
1438 1133 $render->render();
1439 1134 if ( ! $can_die ) {
1440 1135 return;
1441 1136 }
1442 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
1137 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1443 1138 }
1444 1139
1445 1140 /**
1446 1141 * Clones the chart.
@@ -1451,11 +1146,12 @@
1451 1146 */
1452 1147 public function cloneChart() {
1453 1148 $chart_id = $success = false;
1454 1149 $nonce = isset( $_GET['nonce'] ) && wp_verify_nonce( $_GET['nonce'], Visualizer_Plugin::ACTION_CLONE_CHART );
1455 - if ( $nonce ) {
1150 + $capable = current_user_can( 'edit_posts' );
1151 + if ( $nonce && $capable ) {
1456 1152 $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
1457 - if ( $chart_id && self::can_edit_chart( $chart_id ) ) {
1153 + if ( $chart_id ) {
1458 1154 $chart = get_post( $chart_id );
1459 1155 $success = $chart && $chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
1460 1156 }
1461 1157 }
@@ -1475,29 +1171,26 @@
1475 1171 } else {
1476 1172 $post_meta = get_post_meta( $chart_id );
1477 1173 foreach ( $post_meta as $key => $value ) {
1478 1174 if ( strpos( $key, 'visualizer-' ) !== false ) {
1479 - add_post_meta( $new_chart_id, $key, self::maybe_decode_content( $value[0] ) );
1175 + add_post_meta( $new_chart_id, $key, maybe_unserialize( $value[0] ) );
1480 1176 }
1481 1177 }
1482 - $redirect = esc_url(
1483 - add_query_arg(
1484 - array(
1485 - 'page' => 'visualizer',
1486 - 'type' => filter_input( INPUT_GET, 'type' ),
1487 - 'vaction' => false,
1488 - ),
1489 - admin_url( 'admin.php' )
1178 + $redirect = add_query_arg(
1179 + array(
1180 + 'page' => 'visualizer',
1181 + 'type' => filter_input( INPUT_GET, 'type' ),
1182 + 'vaction' => false,
1490 1183 ),
1491 - null,
1492 - 'db'
1184 + admin_url( 'admin.php' )
1493 1185 );
1494 1186 }
1495 1187 }
1496 1188
1497 - if ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) {
1189 + if ( defined( 'WP_TESTS_DOMAIN' ) ) {
1498 1190 wp_die();
1499 1191 }
1192 +
1500 1193 wp_redirect( $redirect );
1501 1194 exit;
1502 1195 }
1503 1196
@@ -1509,25 +1202,28 @@
1509 1202 * @access public
1510 1203 */
1511 1204 public function exportData() {
1512 1205 check_ajax_referer( Visualizer_Plugin::ACTION_EXPORT_DATA . Visualizer_Plugin::VERSION, 'security' );
1513 - $chart_id = isset( $_GET['chart'] ) ? filter_var(
1514 - $_GET['chart'],
1515 - FILTER_VALIDATE_INT,
1516 - array(
1517 - 'options' => array(
1518 - 'min_range' => 1,
1519 - ),
1520 - )
1521 - ) : '';
1522 - if ( $chart_id && self::can_edit_chart( $chart_id ) ) {
1523 - $data = $this->_getDataAs( $chart_id, 'csv' );
1524 - if ( $data ) {
1525 - echo wp_send_json_success( $data );
1206 + $capable = current_user_can( 'edit_posts' );
1207 + if ( $capable ) {
1208 + $chart_id = isset( $_GET['chart'] ) ? filter_var(
1209 + $_GET['chart'],
1210 + FILTER_VALIDATE_INT,
1211 + array(
1212 + 'options' => array(
1213 + 'min_range' => 1,
1214 + ),
1215 + )
1216 + ) : '';
1217 + if ( $chart_id ) {
1218 + $data = $this->_getDataAs( $chart_id, 'csv' );
1219 + if ( $data ) {
1220 + echo wp_send_json_success( $data );
1221 + }
1526 1222 }
1527 1223 }
1528 1224
1529 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
1225 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1530 1226 }
1531 1227
1532 1228 /**
1533 1229 * Handles chart data page.
@@ -1551,11 +1247,10 @@
1551 1247 'visualizer-render',
1552 1248 'visualizer',
1553 1249 array(
1554 1250 'l10n' => array(
1555 - 'invalid_source' => esc_html__( 'The URL you entered is invalid. Please enter a valid URL.', 'visualizer' ),
1556 - 'loading' => esc_html__( 'Loading...', 'visualizer' ),
1557 - 'invalid_format' => esc_html__( 'This format pattern is not supported in the series settings field. To display percentages, use the Manual Configuration option instead.', 'visualizer' ),
1251 + 'invalid_source' => esc_html__( 'You have entered an invalid URL. Please provide a valid URL.', 'visualizer' ),
1252 + 'loading' => esc_html__( 'Loading...', 'visualizer' ),
1558 1253 ),
1559 1254 'charts' => array(
1560 1255 'canvas' => $data,
1561 1256 ),
@@ -1581,24 +1276,12 @@
1581 1276 */
1582 1277 public function getQueryData() {
1583 1278 check_ajax_referer( Visualizer_Plugin::ACTION_FETCH_DB_DATA . Visualizer_Plugin::VERSION, 'security' );
1584 1279
1585 - if ( ! current_user_can( 'administrator' ) ) {
1586 - wp_send_json_error( array( 'msg' => __( 'Action not allowed for this user.', 'visualizer' ) ) );
1587 - }
1588 - if ( ! is_super_admin() ) {
1589 - wp_send_json_error( array( 'msg' => __( 'Action not allowed for this user.', 'visualizer' ) ) );
1590 - }
1591 -
1592 - if ( ! Visualizer_Module::is_pro() ) {
1593 - wp_send_json_error( array( 'msg' => __( 'Feature is not available.', 'visualizer' ) ) );
1594 - }
1595 -
1596 1280 $params = wp_parse_args( $_POST['params'] );
1597 1281 $chart_id = filter_var( $params['chart_id'], FILTER_VALIDATE_INT );
1598 - $query = trim( $params['query'], ';' );
1599 1282
1600 - $source = new Visualizer_Source_Query( stripslashes( $query ), $chart_id, $params );
1283 + $source = new Visualizer_Source_Query( stripslashes( $params['query'] ), $chart_id, $params );
1601 1284 $html = $source->fetch( true );
1602 1285 $error = $source->get_error();
1603 1286 if ( ! empty( $error ) ) {
1604 1287 wp_send_json_error( array( 'msg' => $error ) );
@@ -1613,19 +1296,8 @@
1613 1296 */
1614 1297 public function saveQuery() {
1615 1298 check_ajax_referer( Visualizer_Plugin::ACTION_SAVE_DB_QUERY . Visualizer_Plugin::VERSION, 'security' );
1616 1299
1617 - if ( ! current_user_can( 'administrator' ) ) {
1618 - wp_send_json_error( array( 'msg' => __( 'Action not allowed for this user.', 'visualizer' ) ) );
1619 - }
1620 - if ( ! is_super_admin() ) {
1621 - wp_send_json_error( array( 'msg' => __( 'Action not allowed for this user.', 'visualizer' ) ) );
1622 - }
1623 -
1624 - if ( ! Visualizer_Module::is_pro() ) {
1625 - wp_send_json_error( array( 'msg' => __( 'Feature is not available.', 'visualizer' ) ) );
1626 - }
1627 -
1628 1300 $chart_id = filter_input(
1629 1301 INPUT_GET,
1630 1302 'chart',
1631 1303 FILTER_VALIDATE_INT,
@@ -1638,9 +1310,9 @@
1638 1310
1639 1311 $hours = filter_input(
1640 1312 INPUT_POST,
1641 1313 'refresh',
1642 - FILTER_VALIDATE_FLOAT,
1314 + FILTER_VALIDATE_INT,
1643 1315 array(
1644 1316 'options' => array(
1645 1317 'min_range' => -1,
1646 1318 'max_range' => apply_filters( 'visualizer_is_business', false ) ? PHP_INT_MAX : -1,
@@ -1647,9 +1319,9 @@
1647 1319 ),
1648 1320 )
1649 1321 );
1650 1322
1651 - if ( ! is_numeric( $hours ) ) {
1323 + if ( ! is_int( $hours ) ) {
1652 1324 $hours = -1;
1653 1325 }
1654 1326
1655 1327 $render = new Visualizer_Render_Page_Update();
@@ -1654,14 +1326,13 @@
1654 1326
1655 1327 $render = new Visualizer_Render_Page_Update();
1656 1328 if ( $chart_id ) {
1657 1329 $params = wp_parse_args( $_POST['params'] );
1658 - $query = trim( $params['query'], ';' );
1659 - $source = new Visualizer_Source_Query( stripslashes( $query ), $chart_id, $params );
1330 + $source = new Visualizer_Source_Query( stripslashes( $params['query'] ), $chart_id, $params );
1660 1331 $source->fetch( false );
1661 1332 $error = $source->get_error();
1662 1333 if ( empty( $error ) ) {
1663 - update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY, stripslashes( $query ) );
1334 + update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY, stripslashes( $params['query'] ) );
1664 1335 update_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
1665 1336 update_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
1666 1337 update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_SCHEDULE, $hours );
1667 1338 update_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, 0 );
@@ -1690,9 +1361,9 @@
1690 1361 }
1691 1362 }
1692 1363 $render->render();
1693 1364 if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
1694 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
1365 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1695 1366 }
1696 1367 }
1697 1368
1698 1369
@@ -1703,10 +1374,11 @@
1703 1374 */
1704 1375 public function saveFilter() {
1705 1376 check_ajax_referer( Visualizer_Plugin::ACTION_SAVE_FILTER_QUERY . Visualizer_Plugin::VERSION, 'security' );
1706 1377
1707 - $chart_id = isset( $_GET['chart'] ) ? filter_var(
1708 - $_GET['chart'],
1378 + $chart_id = filter_input(
1379 + INPUT_GET,
1380 + 'chart',
1709 1381 FILTER_VALIDATE_INT,
1710 1382 array(
1711 1383 'options' => array(
1712 1384 'min_range' => 1,
@@ -1711,18 +1383,14 @@
1711 1383 'options' => array(
1712 1384 'min_range' => 1,
1713 1385 ),
1714 1386 )
1715 - ) : false;
1387 + );
1716 1388
1717 - if ( ! self::can_edit_chart( $chart_id ) ) {
1718 - wp_send_json_error( array( 'msg' => esc_html__( 'You do not have permission to perform this action.', 'visualizer' ) ), 403 );
1719 - }
1720 -
1721 1389 $hours = filter_input(
1722 1390 INPUT_POST,
1723 1391 'refresh',
1724 - FILTER_VALIDATE_FLOAT,
1392 + FILTER_VALIDATE_INT,
1725 1393 array(
1726 1394 'options' => array(
1727 1395 'min_range' => -1,
1728 1396 'max_range' => apply_filters( 'visualizer_is_business', false ) ? PHP_INT_MAX : -1,
@@ -1729,9 +1397,9 @@
1729 1397 ),
1730 1398 )
1731 1399 );
1732 1400
1733 - if ( ! is_numeric( $hours ) ) {
1401 + if ( ! $hours ) {
1734 1402 $hours = -1;
1735 1403 }
1736 1404
1737 1405 do_action( 'visualizer_save_filter', $chart_id, $hours );
@@ -1736,65 +1404,8 @@
1736 1404
1737 1405 do_action( 'visualizer_save_filter', $chart_id, $hours );
1738 1406
1739 1407 if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
1740 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
1408 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1741 1409 }
1742 - }
1743 -
1744 - /**
1745 - * Save chart image.
1746 - *
1747 - * @param string $base64_img Chart image.
1748 - * @param int $chart_id Chart ID.
1749 - * @param bool $save_attachment Save attachment.
1750 - * @return int Attachment ID, or 0 when no attachment was saved.
1751 - */
1752 - public function save_chart_image( $base64_img, $chart_id, $save_attachment = true ) {
1753 - // Delete old chart image.
1754 - $old_attachment_id = get_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_IMAGE, true );
1755 - if ( $old_attachment_id ) {
1756 - wp_delete_attachment( $old_attachment_id, true );
1757 - }
1758 -
1759 - if ( ! $save_attachment ) {
1760 - return 0;
1761 - }
1762 -
1763 - // Upload dir.
1764 - $upload_dir = wp_upload_dir();
1765 - $upload_path = str_replace( '/', DIRECTORY_SEPARATOR, $upload_dir['path'] ) . DIRECTORY_SEPARATOR;
1766 -
1767 - $img = str_replace( 'data:image/png;base64,', '', (string) $base64_img );
1768 - $img = str_replace( ' ', '+', $img );
1769 - $decoded = base64_decode( $img, true );
1770 - // The value comes from an untrusted request; only write real PNG bytes to uploads.
1771 - if ( false === $decoded || 0 !== strncmp( $decoded, "\x89PNG\r\n\x1a\n", 8 ) ) {
1772 - return 0;
1773 - }
1774 - $filename = 'visualization-' . $chart_id . '.png';
1775 - $file_type = 'image/png';
1776 - $hashed_filename = $filename;
1777 -
1778 - // Save the image in the uploads directory.
1779 - require_once ABSPATH . '/wp-admin/includes/file.php';
1780 - \WP_Filesystem();
1781 - global $wp_filesystem;
1782 - if ( ! is_a( $wp_filesystem, 'WP_Filesystem_Base' ) ) {
1783 - $creds = request_filesystem_credentials( site_url() );
1784 - wp_filesystem( $creds );
1785 - }
1786 - $upload_file = $wp_filesystem->put_contents( $upload_path . $hashed_filename, $decoded );
1787 -
1788 - // Insert new chart image.
1789 - $attachment = array(
1790 - 'post_mime_type' => $file_type,
1791 - 'post_title' => preg_replace( '/\.[^.]+$/', '', basename( $hashed_filename ) ),
1792 - 'post_content' => '',
1793 - 'post_status' => 'inherit',
1794 - 'guid' => $upload_dir['url'] . '/' . basename( $hashed_filename ),
1795 - );
1796 -
1797 - $attach_id = wp_insert_attachment( $attachment, $upload_dir['path'] . '/' . $hashed_filename );
1798 - return $attach_id;
1799 1410 }
1800 1411 }