PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.4.3
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.4.3
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 +143 -534 4.0.83.4.3 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,21 +205,20 @@
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
246 217 $source = new Visualizer_Source_Json( $params );
247 - update_post_meta( $chart->ID, Visualizer_Plugin::CF_EDITABLE_TABLE, true );
248 218 $source->fetchFromEditableTable();
249 219
250 - $content = $source->getData( get_post_meta( $chart->ID, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) );
220 + $content = $source->getData();
251 221 $chart->post_content = $content;
252 222 wp_update_post( $chart->to_array() );
253 223 update_post_meta( $chart->ID, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
254 224 update_post_meta( $chart->ID, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
@@ -270,16 +240,8 @@
270 240 if ( ! empty( $params['paging'] ) ) {
271 241 add_post_meta( $chart->ID, Visualizer_Plugin::CF_JSON_PAGING, $params['paging'] );
272 242 }
273 243
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 244 $time = filter_input(
283 245 INPUT_POST,
284 246 'time',
285 247 FILTER_VALIDATE_INT,
@@ -303,13 +265,13 @@
303 265 delete_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_SCHEDULE );
304 266
305 267 $render = new Visualizer_Render_Page_Update();
306 268 $render->id = $chart->ID;
307 - $render->data = json_encode( $source->getRawData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ) );
269 + $render->data = json_encode( $source->getRawData() );
308 270 $render->series = json_encode( $source->getSeries() );
309 271 $render->render();
310 272
311 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
273 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
312 274 }
313 275
314 276
315 277 /**
@@ -321,14 +283,8 @@
321 283 *
322 284 * @access public
323 285 */
324 286 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 287 $query_args = array(
332 288 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
333 289 'posts_per_page' => 9,
334 290 'paged' => filter_input(
@@ -342,11 +298,8 @@
342 298 ),
343 299 )
344 300 ),
345 301 );
346 - if ( ! current_user_can( 'edit_others_posts' ) ) {
347 - $query_args['author'] = get_current_user_id();
348 - }
349 302 $filter = filter_input( INPUT_GET, 's', FILTER_SANITIZE_STRING );
350 303 if ( empty( $filter ) ) {
351 304 // 'filter' is from the modal from the add media button.
352 305 $filter = filter_input( INPUT_GET, 'filter', FILTER_SANITIZE_STRING );
@@ -394,13 +347,13 @@
394 347 * @since 1.0.0
395 348 *
396 349 * @access private
397 350 *
398 - * @param WP_Post|null $chart The chart object.
351 + * @param WP_Post $chart The chart object.
399 352 *
400 353 * @return array The array of chart data.
401 354 */
402 - private function _getChartArray( $chart = null ) {
355 + private function _getChartArray( WP_Post $chart = null ) {
403 356 if ( is_null( $chart ) ) {
404 357 $chart = $this->_chart;
405 358 }
406 359 $type = get_post_meta( $chart->ID, Visualizer_Plugin::CF_CHART_TYPE, true );
@@ -422,13 +375,8 @@
422 375 }
423 376
424 377 $date_formats = Visualizer_Source::get_date_formats_if_exists( $series, $data );
425 378
426 - $code = '';
427 - if ( 'd3' === $library ) {
428 - $code = get_post_meta( $chart->ID, Visualizer_Module_AIBuilder::CF_D3_CODE, true );
429 - }
430 -
431 379 return array(
432 380 'type' => $type,
433 381 'series' => $series,
434 382 'settings' => $settings,
@@ -433,9 +381,8 @@
433 381 'series' => $series,
434 382 'settings' => $settings,
435 383 'data' => $data,
436 384 'library' => $library,
437 - 'code' => $code,
438 385 'css' => $css,
439 386 'date_formats' => $date_formats,
440 387 );
441 388 }
@@ -452,9 +399,9 @@
452 399 public static function _sendResponse( $results ) {
453 400 header( 'Content-type: application/json' );
454 401 nocache_headers();
455 402 echo json_encode( $results );
456 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
403 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
457 404 }
458 405
459 406 /**
460 407 * Deletes a chart from database.
@@ -465,14 +412,16 @@
465 412 * @access public
466 413 */
467 414 public function deleteChart() {
468 415 $is_post = $_SERVER['REQUEST_METHOD'] === 'POST';
469 - $input = $is_post ? $_POST : $_GET;
416 + $input_method = $is_post ? INPUT_POST : INPUT_GET;
470 417 $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'],
418 + $nonce = wp_verify_nonce( filter_input( $input_method, 'nonce' ) );
419 + $capable = current_user_can( 'delete_posts' );
420 + if ( $nonce && $capable ) {
421 + $chart_id = filter_input(
422 + $input_method,
423 + 'chart',
475 424 FILTER_VALIDATE_INT,
476 425 array(
477 426 'options' => array(
478 427 'min_range' => 1,
@@ -477,34 +426,16 @@
477 426 'options' => array(
478 427 'min_range' => 1,
479 428 ),
480 429 )
481 - ) : false;
430 + );
482 431 if ( $chart_id ) {
483 432 $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 - );
433 + $success = $chart && $chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
490 434 }
491 435 }
492 436 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 - }
437 + wp_delete_post( $chart_id, true );
507 438 }
508 439 if ( $is_post ) {
509 440 self::_sendResponse(
510 441 array(
@@ -552,111 +483,44 @@
552 483 *
553 484 * @access public
554 485 */
555 486 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 487 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 488 // check chart, if chart not exists, will create new one and redirects to the same page with proper chart id
567 489 $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(
490 + if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ) {
491 + $this->deleteOldCharts();
492 + $default_type = isset( $_GET['type'] ) && ! empty( $_GET['type'] ) ? $_GET['type'] : 'line';
493 + $source = new Visualizer_Source_Csv( VISUALIZER_ABSPATH . DIRECTORY_SEPARATOR . 'samples' . DIRECTORY_SEPARATOR . $default_type . '.csv' );
494 + $source->fetch();
495 + $chart_id = wp_insert_post(
496 + array(
497 + 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
498 + 'post_title' => 'Visualization',
499 + 'post_author' => get_current_user_id(),
500 + 'post_status' => 'auto-draft',
501 + 'post_content' => $source->getData(),
502 + )
503 + );
504 + if ( $chart_id && ! is_wp_error( $chart_id ) ) {
505 + add_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_TYPE, $default_type );
506 + add_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, 1 );
507 + add_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
508 + add_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
509 + add_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_LIBRARY, '' );
510 + add_post_meta(
511 + $chart_id,
512 + Visualizer_Plugin::CF_SETTINGS,
586 513 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 ) ),
514 + 'focusTarget' => 'datum',
592 515 )
593 516 );
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 517 do_action( 'visualizer_pro_new_chart_defaults', $chart_id );
641 518 }
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();
519 + wp_redirect( add_query_arg( 'chart', (int) $chart_id ) );
520 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
648 521 }
649 522
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 523 $lib = $this->load_chart_type( $chart_id );
660 524
661 525 // the alpha color picker (RGBA) is not supported by google.
662 526 $color_picker_dep = 'wp-color-picker';
@@ -718,16 +582,8 @@
718 582 // if the edit button is clicked.
719 583 $this->_chart = $this->handleExistingRevisions( $chart_id, $this->_chart );
720 584 }
721 585
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 586 switch ( $tab ) {
731 587 case 'settings':
732 588 $this->_handleDataAndSettingsPage();
733 589 break;
@@ -738,9 +594,9 @@
738 594 default:
739 595 // this should never happen.
740 596 break;
741 597 }
742 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
598 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
743 599 }
744 600
745 601 /**
746 602 * Load code editor assets.
@@ -752,9 +608,9 @@
752 608
753 609 // data tables assets.
754 610 wp_register_script( 'visualizer-datatables', VISUALIZER_ABSURL . 'js/lib/datatables.min.js', array( 'jquery-ui-core' ), Visualizer_Plugin::VERSION );
755 611 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 );
612 + 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 613 wp_enqueue_script( 'visualizer-datatables' );
758 614 wp_enqueue_style( 'visualizer-jquery-ui' );
759 615
760 616 if ( ! Visualizer_Module::is_pro() ) {
@@ -770,9 +626,9 @@
770 626 wp_register_script( 'visualizer-codemirror-matchbrackets', '//codemirror.net/addon/edit/matchbrackets.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
771 627 wp_register_script( 'visualizer-codemirror-closebrackets', '//codemirror.net/addon/edit/closebrackets.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
772 628 wp_register_script( 'visualizer-codemirror-sql', '//codemirror.net/mode/sql/sql.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
773 629 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 );
630 + 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 631 wp_register_style( 'visualizer-codemirror-core', '//codemirror.net/lib/codemirror.css', array(), Visualizer_Plugin::VERSION );
776 632 wp_register_style( 'visualizer-codemirror-hint', '//codemirror.net/addon/hint/show-hint.css', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
777 633
778 634 wp_enqueue_script( 'visualizer-codemirror-hint' );
@@ -786,9 +642,9 @@
786 642 'lineWrapping' => true,
787 643 'dragDrop' => false,
788 644 'matchBrackets' => true,
789 645 'autoCloseBrackets' => true,
790 - 'extraKeys' => array( 'Shift-Space' => 'autocomplete' ),
646 + 'extraKeys' => array( 'Ctrl-Space' => 'autocomplete' ),
791 647 'hintOptions' => array( 'tables' => $table_col_mapping ),
792 648 ),
793 649 )
794 650 );
@@ -817,34 +673,24 @@
817 673 /**
818 674 * Handle data and settings page
819 675 */
820 676 private function _handleDataAndSettingsPage() {
677 + if ( isset( $_POST['map_api_key'] ) ) {
678 + update_option( 'visualizer-map-api-key', $_POST['map_api_key'] );
679 + }
680 +
821 681 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 ) {
682 + if ( $this->_chart->post_status === 'auto-draft' ) {
830 683 $this->_chart->post_status = 'publish';
831 684
832 685 // 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 686 // we do not want any difference in data so disable revisions temporarily.
834 - $this->disableRevisionsTemporarily();
835 -
687 + add_filter( 'wp_revisions_to_keep', '__return_false' );
836 688 wp_update_post( $this->_chart->to_array() );
837 689 }
838 690 // 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 );
691 + if ( ! ( isset( $_POST['cancel'] ) && 1 === intval( $_POST['cancel'] ) ) ) {
692 + update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, $_POST );
847 693
848 694 // we will keep a parameter called 'internal_title' that will be set to the given title or, if empty, the chart ID
849 695 // this will help in searching with the chart id.
850 696 $settings = get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
@@ -858,14 +704,8 @@
858 704 if ( empty( $title ) ) {
859 705 $title = $this->_chart->ID;
860 706 }
861 707 $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 708 update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, $settings );
869 709 }
870 710 $render = new Visualizer_Render_Page_Send();
871 711 $render->text = sprintf( '[visualizer id="%d"]', $this->_chart->ID );
@@ -891,9 +731,8 @@
891 731 wp_enqueue_style( 'visualizer-frame' );
892 732 wp_enqueue_script( 'visualizer-preview' );
893 733 wp_enqueue_script( 'visualizer-chosen' );
894 734 wp_enqueue_script( 'visualizer-render' );
895 - wp_enqueue_code_editor( array( 'type' => 'application/json' ) );
896 735
897 736 if ( Visualizer_Module::can_show_feature( 'simple-editor' ) ) {
898 737 wp_enqueue_script( 'visualizer-editor-simple' );
899 738 wp_localize_script(
@@ -901,10 +740,12 @@
901 740 'visualizer1',
902 741 array(
903 742 'ajax' => array(
904 743 'url' => admin_url( 'admin-ajax.php' ),
905 - 'nonces' => array(),
906 - 'actions' => array(),
744 + 'nonces' => array(
745 + ),
746 + 'actions' => array(
747 + ),
907 748 ),
908 749 )
909 750 );
910 751 }
@@ -915,15 +756,13 @@
915 756 'visualizer-render',
916 757 'visualizer',
917 758 array(
918 759 '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' ),
760 + 'invalid_source' => esc_html__( 'You have entered an invalid URL. Please provide a valid URL.', 'visualizer' ),
761 + 'loading' => esc_html__( 'Loading...', 'visualizer' ),
762 + 'json_error' => esc_html__( 'An error occured in fetching data.', 'visualizer' ),
763 + 'select_columns' => esc_html__( 'Please select a few columns to include in the chart.', 'visualizer' ),
764 + '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 765 ),
927 766 'charts' => array(
928 767 'canvas' => $data,
929 768 'id' => $this->_chart->ID,
@@ -954,9 +793,8 @@
954 793 'page_type' => 'chart',
955 794 'json_tag_separator' => Visualizer_Source_Json::TAG_SEPARATOR,
956 795 'json_tag_separator_view' => Visualizer_Source_Json::TAG_SEPARATOR_VIEW,
957 796 'is_front' => false,
958 - 'rest_base' => get_rest_url( null, 'wc/v3/reports/' ),
959 797 )
960 798 );
961 799
962 800 $render = new Visualizer_Render_Page_Data();
@@ -967,10 +805,11 @@
967 805 if ( filter_input( INPUT_GET, 'library', FILTER_VALIDATE_BOOLEAN ) ) {
968 806 $render->button = filter_input( INPUT_GET, 'action' ) === Visualizer_Plugin::ACTION_EDIT_CHART
969 807 ? esc_html__( 'Save Chart', 'visualizer' )
970 808 : esc_html__( 'Create Chart', 'visualizer' );
971 -
972 - $render->cancel_button = esc_html__( 'Cancel', 'visualizer' );
809 + if ( filter_input( INPUT_GET, 'action' ) === Visualizer_Plugin::ACTION_EDIT_CHART ) {
810 + $render->cancel_button = esc_html__( 'Cancel', 'visualizer' );
811 + }
973 812 } else {
974 813 $render->button = esc_attr__( 'Insert Chart', 'visualizer' );
975 814 }
976 815
@@ -993,12 +832,12 @@
993 832 * @access private
994 833 */
995 834 private function _handleTypesPage() {
996 835 // process post request
997 - if ( $_SERVER['REQUEST_METHOD'] === 'POST' && wp_verify_nonce( filter_input( INPUT_POST, 'nonce' ), 'visualizer-upload-data' ) ) {
836 + if ( $_SERVER['REQUEST_METHOD'] === 'POST' && wp_verify_nonce( filter_input( INPUT_POST, 'nonce' ) ) ) {
998 837 $type = filter_input( INPUT_POST, 'type' );
999 838 $library = filter_input( INPUT_POST, 'chart-library' );
1000 - if ( Visualizer_Module_Admin::checkChartStatus( $type ) ) {
839 + if ( in_array( $type, Visualizer_Plugin::getChartTypes(), true ) ) {
1001 840 if ( empty( $library ) ) {
1002 841 // library cannot be empty.
1003 842 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, 'Chart library empty while creating the chart! Aborting...', 'error', __FILE__, __LINE__ );
1004 843 return;
@@ -1010,9 +849,9 @@
1010 849 // if the chart has default data, update it with appropriate default data for new type
1011 850 if ( filter_var( get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_DEFAULT_DATA, true ), FILTER_VALIDATE_BOOLEAN ) ) {
1012 851 $source = new Visualizer_Source_Csv( VISUALIZER_ABSPATH . DIRECTORY_SEPARATOR . 'samples' . DIRECTORY_SEPARATOR . $type . '.csv' );
1013 852 $source->fetch();
1014 - $this->_chart->post_content = $source->getData( get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) );
853 + $this->_chart->post_content = $source->getData();
1015 854 wp_update_post( $this->_chart->to_array() );
1016 855 update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
1017 856 }
1018 857
@@ -1019,9 +858,9 @@
1019 858 Visualizer_Module_Utility::set_defaults( $this->_chart );
1020 859
1021 860 // redirect to next tab
1022 861 // changed by Ash/Upwork
1023 - wp_redirect( esc_url_raw( add_query_arg( 'tab', 'settings' ) ) );
862 + wp_redirect( add_query_arg( 'tab', 'settings' ) );
1024 863
1025 864 return;
1026 865 }
1027 866 }
@@ -1034,35 +873,8 @@
1034 873 wp_iframe( array( $render, 'render' ) );
1035 874 }
1036 875
1037 876 /**
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 877 * Renders flattr script in the iframe <head>
1066 878 *
1067 879 * @since 1.4.2
1068 880 * @action admin_head
@@ -1077,81 +889,8 @@
1077 889 * Processes the CSV that is sent in the request as a string.
1078 890 *
1079 891 * @since 3.2.0
1080 892 */
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 893 private function handleCSVasString( $data, $editor_type ) {
1155 894 $source = null;
1156 895
1157 896 switch ( $editor_type ) {
@@ -1164,16 +903,8 @@
1164 903 foreach ( $values as $row ) {
1165 904 if ( empty( $row ) ) {
1166 905 continue;
1167 906 }
1168 - $row = explode( ',', $row );
1169 - $row = array_map(
1170 - function ( $r ) {
1171 - return '' === $r ? ' ' : $r;
1172 - },
1173 - $row
1174 - );
1175 - $row = implode( ',', $row );
1176 907 // don't use fpucsv here because we need to just dump the data
1177 908 // minus the empty rows
1178 909 // because fputcsv needs to tokenize
1179 910 // we can standardize the CSV enclosure here and replace all ' with "
@@ -1217,9 +948,9 @@
1217 948 foreach ( $types as $type ) {
1218 949 if ( empty( $type ) ) {
1219 950 $exclude[] = $index;
1220 951 }
1221 - ++$index;
952 + $index++;
1222 953 }
1223 954
1224 955 // when N headers are being renamed, the number of headers increases by N
1225 956 // because of the way datatable duplicates header information
@@ -1277,16 +1008,11 @@
1277 1008 public function uploadData() {
1278 1009 // if this is being called internally from pro and VISUALIZER_DO_NOT_DIE is set.
1279 1010 // otherwise, assume this is a normal web request.
1280 1011 $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 1012
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 - ) {
1013 + // validate nonce
1014 + if ( ! isset( $_GET['nonce'] ) || ! wp_verify_nonce( $_GET['nonce'] ) ) {
1289 1015 if ( ! $can_die ) {
1290 1016 return;
1291 1017 }
1292 1018 status_header( 403 );
@@ -1295,15 +1021,9 @@
1295 1021
1296 1022 // check chart, if chart exists
1297 1023 // do not use filter_input as it does not work for phpunit test cases, use filter_var instead
1298 1024 $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 - ) {
1025 + if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ) {
1306 1026 if ( ! $can_die ) {
1307 1027 return;
1308 1028 }
1309 1029 status_header( 400 );
@@ -1339,35 +1059,18 @@
1339 1059
1340 1060 // delete editor related data.
1341 1061 delete_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR );
1342 1062
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 1063 $source = null;
1347 1064 $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 - }
1065 + if ( isset( $_POST['remote_data'] ) && filter_var( $_POST['remote_data'], FILTER_VALIDATE_URL ) ) {
1066 + $source = new Visualizer_Source_Csv_Remote( $_POST['remote_data'] );
1360 1067 if ( isset( $_POST['vz-import-time'] ) ) {
1361 - apply_filters( 'visualizer_pro_chart_schedule', $chart_id, $remote_data, $_POST['vz-import-time'] );
1068 + apply_filters( 'visualizer_pro_chart_schedule', $chart_id, $_POST['remote_data'], $_POST['vz-import-time'] );
1362 1069 }
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 - }
1070 + // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
1071 + } elseif ( isset( $_FILES['local_data'] ) && $_FILES['local_data']['error'] == 0 ) {
1072 + $source = new Visualizer_Source_Csv( $_FILES['local_data']['tmp_name'] );
1370 1073 } elseif ( isset( $_POST['chart_data'] ) && strlen( $_POST['chart_data'] ) > 0 ) {
1371 1074 $source = $this->handleCSVasString( $_POST['chart_data'], $_POST['editor-type'] );
1372 1075 update_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR, $_POST['editor-type'] );
1373 1076 } elseif ( isset( $_POST['table_data'] ) && 'yes' === $_POST['table_data'] ) {
@@ -1374,10 +1077,10 @@
1374 1077 $source = $this->handleTabularData();
1375 1078 update_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR, $_POST['editor-type'] );
1376 1079 } else {
1377 1080 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' ) );
1081 + $render->message = esc_html__( 'CSV file with chart data was not uploaded. Please try again.', 'visualizer' );
1082 + update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, esc_html__( 'CSV file with chart data was not uploaded. Please try again.', 'visualizer' ) );
1380 1083 }
1381 1084
1382 1085 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 1086
@@ -1382,12 +1085,12 @@
1382 1085 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 1086
1384 1087 if ( $source ) {
1385 1088 if ( $source->fetch() ) {
1386 - $content = $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) );
1089 + $content = $source->getData();
1387 1090 $populate = true;
1388 - $json = self::decode_content( $content );
1389 - if ( is_array( $json ) ) {
1091 + if ( is_string( $content ) && is_array( unserialize( $content ) ) ) {
1092 + $json = unserialize( $content );
1390 1093 // if source exists, so should data. if source exists but data is blank, do not populate the chart.
1391 1094 // if we populate the data even if it is empty, the chart will show "Table has no columns".
1392 1095 if ( array_key_exists( 'source', $json ) && ! empty( $json['source'] ) && ( ! array_key_exists( 'data', $json ) || empty( $json['data'] ) ) ) {
1393 1096 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 +1101,8 @@
1398 1101 if ( $populate ) {
1399 1102 $chart->post_content = $content;
1400 1103 }
1401 1104 wp_update_post( $chart->to_array() );
1402 -
1403 1105 update_post_meta( $chart->ID, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
1404 1106 update_post_meta( $chart->ID, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
1405 1107 update_post_meta( $chart->ID, Visualizer_Plugin::CF_DEFAULT_DATA, 0 );
1406 1108
@@ -1408,26 +1110,17 @@
1408 1110
1409 1111 Visualizer_Module_Utility::set_defaults( $chart, null );
1410 1112
1411 1113 $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 1114
1422 1115 $render->id = $chart->ID;
1423 - $render->data = json_encode( $source->getRawData( get_post_meta( $chart->ID, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ) );
1116 + $render->data = json_encode( $source->getRawData() );
1424 1117 $render->series = json_encode( $source->getSeries() );
1425 1118 $render->settings = json_encode( $settings );
1426 1119 } else {
1427 1120 $error = $source->get_error();
1428 1121 if ( empty( $error ) ) {
1429 - $error = esc_html__( 'The CSV file couldn\'t be read. Check that it\'s properly formatted and try again.', 'visualizer' );
1122 + $error = esc_html__( 'CSV file is broken or invalid. Please try again.', 'visualizer' );
1430 1123 }
1431 1124 $render->message = $error;
1432 1125 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( '%s for chart %d.', $error, $chart_id ), 'error', __FILE__, __LINE__ );
1433 1126 update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, $error );
@@ -1438,9 +1131,9 @@
1438 1131 $render->render();
1439 1132 if ( ! $can_die ) {
1440 1133 return;
1441 1134 }
1442 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
1135 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1443 1136 }
1444 1137
1445 1138 /**
1446 1139 * Clones the chart.
@@ -1451,11 +1144,12 @@
1451 1144 */
1452 1145 public function cloneChart() {
1453 1146 $chart_id = $success = false;
1454 1147 $nonce = isset( $_GET['nonce'] ) && wp_verify_nonce( $_GET['nonce'], Visualizer_Plugin::ACTION_CLONE_CHART );
1455 - if ( $nonce ) {
1148 + $capable = current_user_can( 'edit_posts' );
1149 + if ( $nonce && $capable ) {
1456 1150 $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
1457 - if ( $chart_id && self::can_edit_chart( $chart_id ) ) {
1151 + if ( $chart_id ) {
1458 1152 $chart = get_post( $chart_id );
1459 1153 $success = $chart && $chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
1460 1154 }
1461 1155 }
@@ -1475,29 +1169,26 @@
1475 1169 } else {
1476 1170 $post_meta = get_post_meta( $chart_id );
1477 1171 foreach ( $post_meta as $key => $value ) {
1478 1172 if ( strpos( $key, 'visualizer-' ) !== false ) {
1479 - add_post_meta( $new_chart_id, $key, self::maybe_decode_content( $value[0] ) );
1173 + add_post_meta( $new_chart_id, $key, maybe_unserialize( $value[0] ) );
1480 1174 }
1481 1175 }
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' )
1176 + $redirect = add_query_arg(
1177 + array(
1178 + 'page' => 'visualizer',
1179 + 'type' => filter_input( INPUT_GET, 'type' ),
1180 + 'vaction' => false,
1490 1181 ),
1491 - null,
1492 - 'db'
1182 + admin_url( 'admin.php' )
1493 1183 );
1494 1184 }
1495 1185 }
1496 1186
1497 - if ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) {
1187 + if ( defined( 'WP_TESTS_DOMAIN' ) ) {
1498 1188 wp_die();
1499 1189 }
1190 +
1500 1191 wp_redirect( $redirect );
1501 1192 exit;
1502 1193 }
1503 1194
@@ -1509,25 +1200,28 @@
1509 1200 * @access public
1510 1201 */
1511 1202 public function exportData() {
1512 1203 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 );
1204 + $capable = current_user_can( 'edit_posts' );
1205 + if ( $capable ) {
1206 + $chart_id = isset( $_GET['chart'] ) ? filter_var(
1207 + $_GET['chart'],
1208 + FILTER_VALIDATE_INT,
1209 + array(
1210 + 'options' => array(
1211 + 'min_range' => 1,
1212 + ),
1213 + )
1214 + ) : '';
1215 + if ( $chart_id ) {
1216 + $data = $this->_getDataAs( $chart_id, 'csv' );
1217 + if ( $data ) {
1218 + echo wp_send_json_success( $data );
1219 + }
1526 1220 }
1527 1221 }
1528 1222
1529 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
1223 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1530 1224 }
1531 1225
1532 1226 /**
1533 1227 * Handles chart data page.
@@ -1551,11 +1245,10 @@
1551 1245 'visualizer-render',
1552 1246 'visualizer',
1553 1247 array(
1554 1248 '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' ),
1249 + 'invalid_source' => esc_html__( 'You have entered an invalid URL. Please provide a valid URL.', 'visualizer' ),
1250 + 'loading' => esc_html__( 'Loading...', 'visualizer' ),
1558 1251 ),
1559 1252 'charts' => array(
1560 1253 'canvas' => $data,
1561 1254 ),
@@ -1581,24 +1274,12 @@
1581 1274 */
1582 1275 public function getQueryData() {
1583 1276 check_ajax_referer( Visualizer_Plugin::ACTION_FETCH_DB_DATA . Visualizer_Plugin::VERSION, 'security' );
1584 1277
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 1278 $params = wp_parse_args( $_POST['params'] );
1597 1279 $chart_id = filter_var( $params['chart_id'], FILTER_VALIDATE_INT );
1598 - $query = trim( $params['query'], ';' );
1599 1280
1600 - $source = new Visualizer_Source_Query( stripslashes( $query ), $chart_id, $params );
1281 + $source = new Visualizer_Source_Query( stripslashes( $params['query'] ), $chart_id, $params );
1601 1282 $html = $source->fetch( true );
1602 1283 $error = $source->get_error();
1603 1284 if ( ! empty( $error ) ) {
1604 1285 wp_send_json_error( array( 'msg' => $error ) );
@@ -1613,19 +1294,8 @@
1613 1294 */
1614 1295 public function saveQuery() {
1615 1296 check_ajax_referer( Visualizer_Plugin::ACTION_SAVE_DB_QUERY . Visualizer_Plugin::VERSION, 'security' );
1616 1297
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 1298 $chart_id = filter_input(
1629 1299 INPUT_GET,
1630 1300 'chart',
1631 1301 FILTER_VALIDATE_INT,
@@ -1638,9 +1308,9 @@
1638 1308
1639 1309 $hours = filter_input(
1640 1310 INPUT_POST,
1641 1311 'refresh',
1642 - FILTER_VALIDATE_FLOAT,
1312 + FILTER_VALIDATE_INT,
1643 1313 array(
1644 1314 'options' => array(
1645 1315 'min_range' => -1,
1646 1316 'max_range' => apply_filters( 'visualizer_is_business', false ) ? PHP_INT_MAX : -1,
@@ -1647,9 +1317,9 @@
1647 1317 ),
1648 1318 )
1649 1319 );
1650 1320
1651 - if ( ! is_numeric( $hours ) ) {
1321 + if ( ! is_int( $hours ) ) {
1652 1322 $hours = -1;
1653 1323 }
1654 1324
1655 1325 $render = new Visualizer_Render_Page_Update();
@@ -1654,14 +1324,13 @@
1654 1324
1655 1325 $render = new Visualizer_Render_Page_Update();
1656 1326 if ( $chart_id ) {
1657 1327 $params = wp_parse_args( $_POST['params'] );
1658 - $query = trim( $params['query'], ';' );
1659 - $source = new Visualizer_Source_Query( stripslashes( $query ), $chart_id, $params );
1328 + $source = new Visualizer_Source_Query( stripslashes( $params['query'] ), $chart_id, $params );
1660 1329 $source->fetch( false );
1661 1330 $error = $source->get_error();
1662 1331 if ( empty( $error ) ) {
1663 - update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY, stripslashes( $query ) );
1332 + update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY, stripslashes( $params['query'] ) );
1664 1333 update_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
1665 1334 update_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
1666 1335 update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_SCHEDULE, $hours );
1667 1336 update_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, 0 );
@@ -1678,12 +1347,12 @@
1678 1347
1679 1348 wp_update_post(
1680 1349 array(
1681 1350 'ID' => $chart_id,
1682 - 'post_content' => $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ),
1351 + 'post_content' => $source->getData(),
1683 1352 )
1684 1353 );
1685 - $render->data = json_encode( $source->getRawData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ) );
1354 + $render->data = json_encode( $source->getRawData() );
1686 1355 $render->series = json_encode( $source->getSeries() );
1687 1356 $render->id = $chart_id;
1688 1357 } else {
1689 1358 $render->message = $error;
@@ -1690,9 +1359,9 @@
1690 1359 }
1691 1360 }
1692 1361 $render->render();
1693 1362 if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
1694 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
1363 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1695 1364 }
1696 1365 }
1697 1366
1698 1367
@@ -1703,10 +1372,11 @@
1703 1372 */
1704 1373 public function saveFilter() {
1705 1374 check_ajax_referer( Visualizer_Plugin::ACTION_SAVE_FILTER_QUERY . Visualizer_Plugin::VERSION, 'security' );
1706 1375
1707 - $chart_id = isset( $_GET['chart'] ) ? filter_var(
1708 - $_GET['chart'],
1376 + $chart_id = filter_input(
1377 + INPUT_GET,
1378 + 'chart',
1709 1379 FILTER_VALIDATE_INT,
1710 1380 array(
1711 1381 'options' => array(
1712 1382 'min_range' => 1,
@@ -1711,18 +1381,14 @@
1711 1381 'options' => array(
1712 1382 'min_range' => 1,
1713 1383 ),
1714 1384 )
1715 - ) : false;
1385 + );
1716 1386
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 1387 $hours = filter_input(
1722 1388 INPUT_POST,
1723 1389 'refresh',
1724 - FILTER_VALIDATE_FLOAT,
1390 + FILTER_VALIDATE_INT,
1725 1391 array(
1726 1392 'options' => array(
1727 1393 'min_range' => -1,
1728 1394 'max_range' => apply_filters( 'visualizer_is_business', false ) ? PHP_INT_MAX : -1,
@@ -1729,9 +1395,9 @@
1729 1395 ),
1730 1396 )
1731 1397 );
1732 1398
1733 - if ( ! is_numeric( $hours ) ) {
1399 + if ( ! $hours ) {
1734 1400 $hours = -1;
1735 1401 }
1736 1402
1737 1403 do_action( 'visualizer_save_filter', $chart_id, $hours );
@@ -1736,65 +1402,8 @@
1736 1402
1737 1403 do_action( 'visualizer_save_filter', $chart_id, $hours );
1738 1404
1739 1405 if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
1740 - ( defined( 'WP_TESTS_DOMAIN' ) && function_exists( 'tests_add_filter' ) ) ? wp_die() : exit();
1406 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1741 1407 }
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 1408 }
1800 1409 }