PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.5.1
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.5.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 +135 -515 4.0.83.5.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.
@@ -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,22 +134,8 @@
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 );
@@ -172,12 +156,8 @@
172 156 */
173 157 public function getJsonRoots() {
174 158 check_ajax_referer( Visualizer_Plugin::ACTION_JSON_GET_ROOTS . Visualizer_Plugin::VERSION, 'security' );
175 159
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 160 $params = wp_parse_args( $_POST['params'] );
181 161
182 162 $source = new Visualizer_Source_Json( $params );
183 163
@@ -198,18 +178,13 @@
198 178 */
199 179 public function getJsonData() {
200 180 check_ajax_referer( Visualizer_Plugin::ACTION_JSON_GET_DATA . Visualizer_Plugin::VERSION, 'security' );
201 181
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 182 $params = wp_parse_args( $_POST['params'] );
207 183
208 184 $chart_id = $params['chart'];
209 185
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 ) ) {
186 + if ( empty( $chart_id ) ) {
212 187 wp_die();
213 188 }
214 189
215 190 $source = new Visualizer_Source_Json( $params );
@@ -234,12 +209,12 @@
234 209 public function setJsonData() {
235 210 check_ajax_referer( Visualizer_Plugin::ACTION_JSON_SET_DATA . Visualizer_Plugin::VERSION, 'security' );
236 211
237 212 $params = $_POST;
238 - $chart_id = isset( $_GET['chart'] ) ? absint( $_GET['chart'] ) : 0;
213 + $chart_id = $_GET['chart'];
239 214
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 ) );
215 + if ( empty( $chart_id ) ) {
216 + wp_die();
242 217 }
243 218
244 219 $chart = get_post( $chart_id );
245 220
@@ -270,16 +245,8 @@
270 245 if ( ! empty( $params['paging'] ) ) {
271 246 add_post_meta( $chart->ID, Visualizer_Plugin::CF_JSON_PAGING, $params['paging'] );
272 247 }
273 248
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 249 $time = filter_input(
283 250 INPUT_POST,
284 251 'time',
285 252 FILTER_VALIDATE_INT,
@@ -307,9 +274,9 @@
307 274 $render->data = json_encode( $source->getRawData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ) );
308 275 $render->series = json_encode( $source->getSeries() );
309 276 $render->render();
310 277
311 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
278 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
312 279 }
313 280
314 281
315 282 /**
@@ -321,14 +288,8 @@
321 288 *
322 289 * @access public
323 290 */
324 291 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 292 $query_args = array(
332 293 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
333 294 'posts_per_page' => 9,
334 295 'paged' => filter_input(
@@ -342,11 +303,8 @@
342 303 ),
343 304 )
344 305 ),
345 306 );
346 - if ( ! current_user_can( 'edit_others_posts' ) ) {
347 - $query_args['author'] = get_current_user_id();
348 - }
349 307 $filter = filter_input( INPUT_GET, 's', FILTER_SANITIZE_STRING );
350 308 if ( empty( $filter ) ) {
351 309 // 'filter' is from the modal from the add media button.
352 310 $filter = filter_input( INPUT_GET, 'filter', FILTER_SANITIZE_STRING );
@@ -394,13 +352,13 @@
394 352 * @since 1.0.0
395 353 *
396 354 * @access private
397 355 *
398 - * @param WP_Post|null $chart The chart object.
356 + * @param WP_Post $chart The chart object.
399 357 *
400 358 * @return array The array of chart data.
401 359 */
402 - private function _getChartArray( $chart = null ) {
360 + private function _getChartArray( WP_Post $chart = null ) {
403 361 if ( is_null( $chart ) ) {
404 362 $chart = $this->_chart;
405 363 }
406 364 $type = get_post_meta( $chart->ID, Visualizer_Plugin::CF_CHART_TYPE, true );
@@ -422,13 +380,8 @@
422 380 }
423 381
424 382 $date_formats = Visualizer_Source::get_date_formats_if_exists( $series, $data );
425 383
426 - $code = '';
427 - if ( 'd3' === $library ) {
428 - $code = get_post_meta( $chart->ID, Visualizer_Module_AIBuilder::CF_D3_CODE, true );
429 - }
430 -
431 384 return array(
432 385 'type' => $type,
433 386 'series' => $series,
434 387 'settings' => $settings,
@@ -433,9 +386,8 @@
433 386 'series' => $series,
434 387 'settings' => $settings,
435 388 'data' => $data,
436 389 'library' => $library,
437 - 'code' => $code,
438 390 'css' => $css,
439 391 'date_formats' => $date_formats,
440 392 );
441 393 }
@@ -452,9 +404,9 @@
452 404 public static function _sendResponse( $results ) {
453 405 header( 'Content-type: application/json' );
454 406 nocache_headers();
455 407 echo json_encode( $results );
456 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
408 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
457 409 }
458 410
459 411 /**
460 412 * Deletes a chart from database.
@@ -465,14 +417,16 @@
465 417 * @access public
466 418 */
467 419 public function deleteChart() {
468 420 $is_post = $_SERVER['REQUEST_METHOD'] === 'POST';
469 - $input = $is_post ? $_POST : $_GET;
421 + $input_method = $is_post ? INPUT_POST : INPUT_GET;
470 422 $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'],
423 + $nonce = wp_verify_nonce( filter_input( $input_method, 'nonce' ) );
424 + $capable = current_user_can( 'delete_posts' );
425 + if ( $nonce && $capable ) {
426 + $chart_id = filter_input(
427 + $input_method,
428 + 'chart',
475 429 FILTER_VALIDATE_INT,
476 430 array(
477 431 'options' => array(
478 432 'min_range' => 1,
@@ -477,34 +431,16 @@
477 431 'options' => array(
478 432 'min_range' => 1,
479 433 ),
480 434 )
481 - ) : false;
435 + );
482 436 if ( $chart_id ) {
483 437 $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 - );
438 + $success = $chart && $chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
490 439 }
491 440 }
492 441 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 - }
442 + wp_delete_post( $chart_id, true );
507 443 }
508 444 if ( $is_post ) {
509 445 self::_sendResponse(
510 446 array(
@@ -552,111 +488,46 @@
552 488 *
553 489 * @access public
554 490 */
555 491 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 492 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 493 // Set current screen for the render chart.
565 494 set_current_screen( 'visualizer_render_chart' );
566 495 // check chart, if chart not exists, will create new one and redirects to the same page with proper chart id
567 496 $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(
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,
586 520 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 ) ),
521 + 'focusTarget' => 'datum',
592 522 )
593 523 );
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 524 do_action( 'visualizer_pro_new_chart_defaults', $chart_id );
641 525 }
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();
526 + wp_redirect( add_query_arg( 'chart', (int) $chart_id ) );
527 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
648 528 }
649 529
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 530 $lib = $this->load_chart_type( $chart_id );
660 531
661 532 // the alpha color picker (RGBA) is not supported by google.
662 533 $color_picker_dep = 'wp-color-picker';
@@ -718,16 +589,8 @@
718 589 // if the edit button is clicked.
719 590 $this->_chart = $this->handleExistingRevisions( $chart_id, $this->_chart );
720 591 }
721 592
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 593 switch ( $tab ) {
731 594 case 'settings':
732 595 $this->_handleDataAndSettingsPage();
733 596 break;
@@ -738,9 +601,9 @@
738 601 default:
739 602 // this should never happen.
740 603 break;
741 604 }
742 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
605 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
743 606 }
744 607
745 608 /**
746 609 * Load code editor assets.
@@ -752,9 +615,9 @@
752 615
753 616 // data tables assets.
754 617 wp_register_script( 'visualizer-datatables', VISUALIZER_ABSURL . 'js/lib/datatables.min.js', array( 'jquery-ui-core' ), Visualizer_Plugin::VERSION );
755 618 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 );
619 + 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 620 wp_enqueue_script( 'visualizer-datatables' );
758 621 wp_enqueue_style( 'visualizer-jquery-ui' );
759 622
760 623 if ( ! Visualizer_Module::is_pro() ) {
@@ -770,9 +633,9 @@
770 633 wp_register_script( 'visualizer-codemirror-matchbrackets', '//codemirror.net/addon/edit/matchbrackets.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
771 634 wp_register_script( 'visualizer-codemirror-closebrackets', '//codemirror.net/addon/edit/closebrackets.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
772 635 wp_register_script( 'visualizer-codemirror-sql', '//codemirror.net/mode/sql/sql.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
773 636 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 );
637 + 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 638 wp_register_style( 'visualizer-codemirror-core', '//codemirror.net/lib/codemirror.css', array(), Visualizer_Plugin::VERSION );
776 639 wp_register_style( 'visualizer-codemirror-hint', '//codemirror.net/addon/hint/show-hint.css', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
777 640
778 641 wp_enqueue_script( 'visualizer-codemirror-hint' );
@@ -786,9 +649,9 @@
786 649 'lineWrapping' => true,
787 650 'dragDrop' => false,
788 651 'matchBrackets' => true,
789 652 'autoCloseBrackets' => true,
790 - 'extraKeys' => array( 'Shift-Space' => 'autocomplete' ),
653 + 'extraKeys' => array( 'Ctrl-Space' => 'autocomplete' ),
791 654 'hintOptions' => array( 'tables' => $table_col_mapping ),
792 655 ),
793 656 )
794 657 );
@@ -817,17 +680,14 @@
817 680 /**
818 681 * Handle data and settings page
819 682 */
820 683 private function _handleDataAndSettingsPage() {
684 + if ( isset( $_POST['map_api_key'] ) ) {
685 + update_option( 'visualizer-map-api-key', $_POST['map_api_key'] );
686 + }
687 +
821 688 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 ) {
689 + if ( $this->_chart->post_status === 'auto-draft' ) {
830 690 $this->_chart->post_status = 'publish';
831 691
832 692 // 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 693 // we do not want any difference in data so disable revisions temporarily.
@@ -835,16 +695,10 @@
835 695
836 696 wp_update_post( $this->_chart->to_array() );
837 697 }
838 698 // 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 );
699 + if ( ! ( isset( $_POST['cancel'] ) && 1 === intval( $_POST['cancel'] ) ) ) {
700 + update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, $_POST );
847 701
848 702 // we will keep a parameter called 'internal_title' that will be set to the given title or, if empty, the chart ID
849 703 // this will help in searching with the chart id.
850 704 $settings = get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
@@ -858,14 +712,8 @@
858 712 if ( empty( $title ) ) {
859 713 $title = $this->_chart->ID;
860 714 }
861 715 $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 716 update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, $settings );
869 717 }
870 718 $render = new Visualizer_Render_Page_Send();
871 719 $render->text = sprintf( '[visualizer id="%d"]', $this->_chart->ID );
@@ -891,9 +739,8 @@
891 739 wp_enqueue_style( 'visualizer-frame' );
892 740 wp_enqueue_script( 'visualizer-preview' );
893 741 wp_enqueue_script( 'visualizer-chosen' );
894 742 wp_enqueue_script( 'visualizer-render' );
895 - wp_enqueue_code_editor( array( 'type' => 'application/json' ) );
896 743
897 744 if ( Visualizer_Module::can_show_feature( 'simple-editor' ) ) {
898 745 wp_enqueue_script( 'visualizer-editor-simple' );
899 746 wp_localize_script(
@@ -901,10 +748,12 @@
901 748 'visualizer1',
902 749 array(
903 750 'ajax' => array(
904 751 'url' => admin_url( 'admin-ajax.php' ),
905 - 'nonces' => array(),
906 - 'actions' => array(),
752 + 'nonces' => array(
753 + ),
754 + 'actions' => array(
755 + ),
907 756 ),
908 757 )
909 758 );
910 759 }
@@ -915,15 +764,13 @@
915 764 'visualizer-render',
916 765 'visualizer',
917 766 array(
918 767 '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' ),
768 + 'invalid_source' => esc_html__( 'You have entered an invalid URL. Please provide a valid URL.', 'visualizer' ),
769 + 'loading' => esc_html__( 'Loading...', 'visualizer' ),
770 + 'json_error' => esc_html__( 'An error occured in fetching data.', 'visualizer' ),
771 + 'select_columns' => esc_html__( 'Please select a few columns to include in the chart.', 'visualizer' ),
772 + '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 773 ),
927 774 'charts' => array(
928 775 'canvas' => $data,
929 776 'id' => $this->_chart->ID,
@@ -954,9 +801,8 @@
954 801 'page_type' => 'chart',
955 802 'json_tag_separator' => Visualizer_Source_Json::TAG_SEPARATOR,
956 803 'json_tag_separator_view' => Visualizer_Source_Json::TAG_SEPARATOR_VIEW,
957 804 'is_front' => false,
958 - 'rest_base' => get_rest_url( null, 'wc/v3/reports/' ),
959 805 )
960 806 );
961 807
962 808 $render = new Visualizer_Render_Page_Data();
@@ -967,10 +813,11 @@
967 813 if ( filter_input( INPUT_GET, 'library', FILTER_VALIDATE_BOOLEAN ) ) {
968 814 $render->button = filter_input( INPUT_GET, 'action' ) === Visualizer_Plugin::ACTION_EDIT_CHART
969 815 ? esc_html__( 'Save Chart', 'visualizer' )
970 816 : esc_html__( 'Create Chart', 'visualizer' );
971 -
972 - $render->cancel_button = esc_html__( 'Cancel', 'visualizer' );
817 + if ( filter_input( INPUT_GET, 'action' ) === Visualizer_Plugin::ACTION_EDIT_CHART ) {
818 + $render->cancel_button = esc_html__( 'Cancel', 'visualizer' );
819 + }
973 820 } else {
974 821 $render->button = esc_attr__( 'Insert Chart', 'visualizer' );
975 822 }
976 823
@@ -993,12 +840,12 @@
993 840 * @access private
994 841 */
995 842 private function _handleTypesPage() {
996 843 // process post request
997 - if ( $_SERVER['REQUEST_METHOD'] === 'POST' && wp_verify_nonce( filter_input( INPUT_POST, 'nonce' ), 'visualizer-upload-data' ) ) {
844 + if ( $_SERVER['REQUEST_METHOD'] === 'POST' && wp_verify_nonce( filter_input( INPUT_POST, 'nonce' ) ) ) {
998 845 $type = filter_input( INPUT_POST, 'type' );
999 846 $library = filter_input( INPUT_POST, 'chart-library' );
1000 - if ( Visualizer_Module_Admin::checkChartStatus( $type ) ) {
847 + if ( in_array( $type, Visualizer_Plugin::getChartTypes(), true ) ) {
1001 848 if ( empty( $library ) ) {
1002 849 // library cannot be empty.
1003 850 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, 'Chart library empty while creating the chart! Aborting...', 'error', __FILE__, __LINE__ );
1004 851 return;
@@ -1019,9 +866,9 @@
1019 866 Visualizer_Module_Utility::set_defaults( $this->_chart );
1020 867
1021 868 // redirect to next tab
1022 869 // changed by Ash/Upwork
1023 - wp_redirect( esc_url_raw( add_query_arg( 'tab', 'settings' ) ) );
870 + wp_redirect( add_query_arg( 'tab', 'settings' ) );
1024 871
1025 872 return;
1026 873 }
1027 874 }
@@ -1034,35 +881,8 @@
1034 881 wp_iframe( array( $render, 'render' ) );
1035 882 }
1036 883
1037 884 /**
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 885 * Renders flattr script in the iframe <head>
1066 886 *
1067 887 * @since 1.4.2
1068 888 * @action admin_head
@@ -1077,81 +897,8 @@
1077 897 * Processes the CSV that is sent in the request as a string.
1078 898 *
1079 899 * @since 3.2.0
1080 900 */
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 901 private function handleCSVasString( $data, $editor_type ) {
1155 902 $source = null;
1156 903
1157 904 switch ( $editor_type ) {
@@ -1164,16 +911,8 @@
1164 911 foreach ( $values as $row ) {
1165 912 if ( empty( $row ) ) {
1166 913 continue;
1167 914 }
1168 - $row = explode( ',', $row );
1169 - $row = array_map(
1170 - function ( $r ) {
1171 - return '' === $r ? ' ' : $r;
1172 - },
1173 - $row
1174 - );
1175 - $row = implode( ',', $row );
1176 915 // don't use fpucsv here because we need to just dump the data
1177 916 // minus the empty rows
1178 917 // because fputcsv needs to tokenize
1179 918 // we can standardize the CSV enclosure here and replace all ' with "
@@ -1217,9 +956,9 @@
1217 956 foreach ( $types as $type ) {
1218 957 if ( empty( $type ) ) {
1219 958 $exclude[] = $index;
1220 959 }
1221 - ++$index;
960 + $index++;
1222 961 }
1223 962
1224 963 // when N headers are being renamed, the number of headers increases by N
1225 964 // because of the way datatable duplicates header information
@@ -1277,16 +1016,11 @@
1277 1016 public function uploadData() {
1278 1017 // if this is being called internally from pro and VISUALIZER_DO_NOT_DIE is set.
1279 1018 // otherwise, assume this is a normal web request.
1280 1019 $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 1020
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 - ) {
1021 + // validate nonce
1022 + if ( ! isset( $_GET['nonce'] ) || ! wp_verify_nonce( $_GET['nonce'] ) ) {
1289 1023 if ( ! $can_die ) {
1290 1024 return;
1291 1025 }
1292 1026 status_header( 403 );
@@ -1295,15 +1029,9 @@
1295 1029
1296 1030 // check chart, if chart exists
1297 1031 // do not use filter_input as it does not work for phpunit test cases, use filter_var instead
1298 1032 $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 - ) {
1033 + if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ) {
1306 1034 if ( ! $can_die ) {
1307 1035 return;
1308 1036 }
1309 1037 status_header( 400 );
@@ -1344,30 +1072,16 @@
1344 1072 delete_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE );
1345 1073
1346 1074 $source = null;
1347 1075 $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 - }
1076 + if ( isset( $_POST['remote_data'] ) && filter_var( $_POST['remote_data'], FILTER_VALIDATE_URL ) ) {
1077 + $source = new Visualizer_Source_Csv_Remote( $_POST['remote_data'] );
1360 1078 if ( isset( $_POST['vz-import-time'] ) ) {
1361 - apply_filters( 'visualizer_pro_chart_schedule', $chart_id, $remote_data, $_POST['vz-import-time'] );
1079 + apply_filters( 'visualizer_pro_chart_schedule', $chart_id, $_POST['remote_data'], $_POST['vz-import-time'] );
1362 1080 }
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 - }
1081 + // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
1082 + } elseif ( isset( $_FILES['local_data'] ) && $_FILES['local_data']['error'] == 0 ) {
1083 + $source = new Visualizer_Source_Csv( $_FILES['local_data']['tmp_name'] );
1370 1084 } elseif ( isset( $_POST['chart_data'] ) && strlen( $_POST['chart_data'] ) > 0 ) {
1371 1085 $source = $this->handleCSVasString( $_POST['chart_data'], $_POST['editor-type'] );
1372 1086 update_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR, $_POST['editor-type'] );
1373 1087 } elseif ( isset( $_POST['table_data'] ) && 'yes' === $_POST['table_data'] ) {
@@ -1374,10 +1088,10 @@
1374 1088 $source = $this->handleTabularData();
1375 1089 update_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR, $_POST['editor-type'] );
1376 1090 } else {
1377 1091 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' ) );
1092 + $render->message = esc_html__( 'CSV file with chart data was not uploaded. Please try again.', 'visualizer' );
1093 + update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, esc_html__( 'CSV file with chart data was not uploaded. Please try again.', 'visualizer' ) );
1380 1094 }
1381 1095
1382 1096 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 1097
@@ -1384,10 +1098,10 @@
1384 1098 if ( $source ) {
1385 1099 if ( $source->fetch() ) {
1386 1100 $content = $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) );
1387 1101 $populate = true;
1388 - $json = self::decode_content( $content );
1389 - if ( is_array( $json ) ) {
1102 + if ( is_string( $content ) && is_array( unserialize( $content ) ) ) {
1103 + $json = unserialize( $content );
1390 1104 // if source exists, so should data. if source exists but data is blank, do not populate the chart.
1391 1105 // if we populate the data even if it is empty, the chart will show "Table has no columns".
1392 1106 if ( array_key_exists( 'source', $json ) && ! empty( $json['source'] ) && ( ! array_key_exists( 'data', $json ) || empty( $json['data'] ) ) ) {
1393 1107 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 +1112,8 @@
1398 1112 if ( $populate ) {
1399 1113 $chart->post_content = $content;
1400 1114 }
1401 1115 wp_update_post( $chart->to_array() );
1402 -
1403 1116 update_post_meta( $chart->ID, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
1404 1117 update_post_meta( $chart->ID, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
1405 1118 update_post_meta( $chart->ID, Visualizer_Plugin::CF_DEFAULT_DATA, 0 );
1406 1119
@@ -1408,17 +1121,8 @@
1408 1121
1409 1122 Visualizer_Module_Utility::set_defaults( $chart, null );
1410 1123
1411 1124 $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 1125
1422 1126 $render->id = $chart->ID;
1423 1127 $render->data = json_encode( $source->getRawData( get_post_meta( $chart->ID, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ) );
1424 1128 $render->series = json_encode( $source->getSeries() );
@@ -1425,9 +1129,9 @@
1425 1129 $render->settings = json_encode( $settings );
1426 1130 } else {
1427 1131 $error = $source->get_error();
1428 1132 if ( empty( $error ) ) {
1429 - $error = esc_html__( 'The CSV file couldn\'t be read. Check that it\'s properly formatted and try again.', 'visualizer' );
1133 + $error = esc_html__( 'CSV file is broken or invalid. Please try again.', 'visualizer' );
1430 1134 }
1431 1135 $render->message = $error;
1432 1136 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( '%s for chart %d.', $error, $chart_id ), 'error', __FILE__, __LINE__ );
1433 1137 update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, $error );
@@ -1438,9 +1142,9 @@
1438 1142 $render->render();
1439 1143 if ( ! $can_die ) {
1440 1144 return;
1441 1145 }
1442 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
1146 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1443 1147 }
1444 1148
1445 1149 /**
1446 1150 * Clones the chart.
@@ -1451,11 +1155,12 @@
1451 1155 */
1452 1156 public function cloneChart() {
1453 1157 $chart_id = $success = false;
1454 1158 $nonce = isset( $_GET['nonce'] ) && wp_verify_nonce( $_GET['nonce'], Visualizer_Plugin::ACTION_CLONE_CHART );
1455 - if ( $nonce ) {
1159 + $capable = current_user_can( 'edit_posts' );
1160 + if ( $nonce && $capable ) {
1456 1161 $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
1457 - if ( $chart_id && self::can_edit_chart( $chart_id ) ) {
1162 + if ( $chart_id ) {
1458 1163 $chart = get_post( $chart_id );
1459 1164 $success = $chart && $chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
1460 1165 }
1461 1166 }
@@ -1475,29 +1180,26 @@
1475 1180 } else {
1476 1181 $post_meta = get_post_meta( $chart_id );
1477 1182 foreach ( $post_meta as $key => $value ) {
1478 1183 if ( strpos( $key, 'visualizer-' ) !== false ) {
1479 - add_post_meta( $new_chart_id, $key, self::maybe_decode_content( $value[0] ) );
1184 + add_post_meta( $new_chart_id, $key, maybe_unserialize( $value[0] ) );
1480 1185 }
1481 1186 }
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' )
1187 + $redirect = add_query_arg(
1188 + array(
1189 + 'page' => 'visualizer',
1190 + 'type' => filter_input( INPUT_GET, 'type' ),
1191 + 'vaction' => false,
1490 1192 ),
1491 - null,
1492 - 'db'
1193 + admin_url( 'admin.php' )
1493 1194 );
1494 1195 }
1495 1196 }
1496 1197
1497 - if ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) {
1198 + if ( defined( 'WP_TESTS_DOMAIN' ) ) {
1498 1199 wp_die();
1499 1200 }
1201 +
1500 1202 wp_redirect( $redirect );
1501 1203 exit;
1502 1204 }
1503 1205
@@ -1509,25 +1211,28 @@
1509 1211 * @access public
1510 1212 */
1511 1213 public function exportData() {
1512 1214 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 );
1215 + $capable = current_user_can( 'edit_posts' );
1216 + if ( $capable ) {
1217 + $chart_id = isset( $_GET['chart'] ) ? filter_var(
1218 + $_GET['chart'],
1219 + FILTER_VALIDATE_INT,
1220 + array(
1221 + 'options' => array(
1222 + 'min_range' => 1,
1223 + ),
1224 + )
1225 + ) : '';
1226 + if ( $chart_id ) {
1227 + $data = $this->_getDataAs( $chart_id, 'csv' );
1228 + if ( $data ) {
1229 + echo wp_send_json_success( $data );
1230 + }
1526 1231 }
1527 1232 }
1528 1233
1529 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
1234 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1530 1235 }
1531 1236
1532 1237 /**
1533 1238 * Handles chart data page.
@@ -1551,11 +1256,10 @@
1551 1256 'visualizer-render',
1552 1257 'visualizer',
1553 1258 array(
1554 1259 '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' ),
1260 + 'invalid_source' => esc_html__( 'You have entered an invalid URL. Please provide a valid URL.', 'visualizer' ),
1261 + 'loading' => esc_html__( 'Loading...', 'visualizer' ),
1558 1262 ),
1559 1263 'charts' => array(
1560 1264 'canvas' => $data,
1561 1265 ),
@@ -1581,24 +1285,12 @@
1581 1285 */
1582 1286 public function getQueryData() {
1583 1287 check_ajax_referer( Visualizer_Plugin::ACTION_FETCH_DB_DATA . Visualizer_Plugin::VERSION, 'security' );
1584 1288
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 1289 $params = wp_parse_args( $_POST['params'] );
1597 1290 $chart_id = filter_var( $params['chart_id'], FILTER_VALIDATE_INT );
1598 - $query = trim( $params['query'], ';' );
1599 1291
1600 - $source = new Visualizer_Source_Query( stripslashes( $query ), $chart_id, $params );
1292 + $source = new Visualizer_Source_Query( stripslashes( $params['query'] ), $chart_id, $params );
1601 1293 $html = $source->fetch( true );
1602 1294 $error = $source->get_error();
1603 1295 if ( ! empty( $error ) ) {
1604 1296 wp_send_json_error( array( 'msg' => $error ) );
@@ -1613,19 +1305,8 @@
1613 1305 */
1614 1306 public function saveQuery() {
1615 1307 check_ajax_referer( Visualizer_Plugin::ACTION_SAVE_DB_QUERY . Visualizer_Plugin::VERSION, 'security' );
1616 1308
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 1309 $chart_id = filter_input(
1629 1310 INPUT_GET,
1630 1311 'chart',
1631 1312 FILTER_VALIDATE_INT,
@@ -1638,9 +1319,9 @@
1638 1319
1639 1320 $hours = filter_input(
1640 1321 INPUT_POST,
1641 1322 'refresh',
1642 - FILTER_VALIDATE_FLOAT,
1323 + FILTER_VALIDATE_INT,
1643 1324 array(
1644 1325 'options' => array(
1645 1326 'min_range' => -1,
1646 1327 'max_range' => apply_filters( 'visualizer_is_business', false ) ? PHP_INT_MAX : -1,
@@ -1647,9 +1328,9 @@
1647 1328 ),
1648 1329 )
1649 1330 );
1650 1331
1651 - if ( ! is_numeric( $hours ) ) {
1332 + if ( ! is_int( $hours ) ) {
1652 1333 $hours = -1;
1653 1334 }
1654 1335
1655 1336 $render = new Visualizer_Render_Page_Update();
@@ -1654,14 +1335,13 @@
1654 1335
1655 1336 $render = new Visualizer_Render_Page_Update();
1656 1337 if ( $chart_id ) {
1657 1338 $params = wp_parse_args( $_POST['params'] );
1658 - $query = trim( $params['query'], ';' );
1659 - $source = new Visualizer_Source_Query( stripslashes( $query ), $chart_id, $params );
1339 + $source = new Visualizer_Source_Query( stripslashes( $params['query'] ), $chart_id, $params );
1660 1340 $source->fetch( false );
1661 1341 $error = $source->get_error();
1662 1342 if ( empty( $error ) ) {
1663 - update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY, stripslashes( $query ) );
1343 + update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY, stripslashes( $params['query'] ) );
1664 1344 update_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
1665 1345 update_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
1666 1346 update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_SCHEDULE, $hours );
1667 1347 update_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, 0 );
@@ -1690,9 +1370,9 @@
1690 1370 }
1691 1371 }
1692 1372 $render->render();
1693 1373 if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
1694 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
1374 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1695 1375 }
1696 1376 }
1697 1377
1698 1378
@@ -1703,10 +1383,11 @@
1703 1383 */
1704 1384 public function saveFilter() {
1705 1385 check_ajax_referer( Visualizer_Plugin::ACTION_SAVE_FILTER_QUERY . Visualizer_Plugin::VERSION, 'security' );
1706 1386
1707 - $chart_id = isset( $_GET['chart'] ) ? filter_var(
1708 - $_GET['chart'],
1387 + $chart_id = filter_input(
1388 + INPUT_GET,
1389 + 'chart',
1709 1390 FILTER_VALIDATE_INT,
1710 1391 array(
1711 1392 'options' => array(
1712 1393 'min_range' => 1,
@@ -1711,18 +1392,14 @@
1711 1392 'options' => array(
1712 1393 'min_range' => 1,
1713 1394 ),
1714 1395 )
1715 - ) : false;
1396 + );
1716 1397
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 1398 $hours = filter_input(
1722 1399 INPUT_POST,
1723 1400 'refresh',
1724 - FILTER_VALIDATE_FLOAT,
1401 + FILTER_VALIDATE_INT,
1725 1402 array(
1726 1403 'options' => array(
1727 1404 'min_range' => -1,
1728 1405 'max_range' => apply_filters( 'visualizer_is_business', false ) ? PHP_INT_MAX : -1,
@@ -1729,9 +1406,9 @@
1729 1406 ),
1730 1407 )
1731 1408 );
1732 1409
1733 - if ( ! is_numeric( $hours ) ) {
1410 + if ( 0 !== $hours && empty( $hours ) ) {
1734 1411 $hours = -1;
1735 1412 }
1736 1413
1737 1414 do_action( 'visualizer_save_filter', $chart_id, $hours );
@@ -1736,65 +1413,8 @@
1736 1413
1737 1414 do_action( 'visualizer_save_filter', $chart_id, $hours );
1738 1415
1739 1416 if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
1740 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
1417 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1741 1418 }
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 1419 }
1800 1420 }