PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.9.1
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.9.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 +865 -114 3.1.03.9.1 View file →
@@ -60,11 +60,249 @@
60 60 $this->_addAjaxAction( Visualizer_Plugin::ACTION_EXPORT_DATA, 'exportData' );
61 61
62 62 $this->_addAjaxAction( Visualizer_Plugin::ACTION_FETCH_DB_DATA, 'getQueryData' );
63 63 $this->_addAjaxAction( Visualizer_Plugin::ACTION_SAVE_DB_QUERY, 'saveQuery' );
64 +
65 + $this->_addAjaxAction( Visualizer_Plugin::ACTION_JSON_GET_ROOTS, 'getJsonRoots' );
66 + $this->_addAjaxAction( Visualizer_Plugin::ACTION_JSON_GET_DATA, 'getJsonData' );
67 + $this->_addAjaxAction( Visualizer_Plugin::ACTION_JSON_SET_DATA, 'setJsonData' );
68 + $this->_addAjaxAction( Visualizer_Plugin::ACTION_JSON_SET_SCHEDULE, 'setJsonSchedule' );
69 +
70 + $this->_addAjaxAction( Visualizer_Plugin::ACTION_SAVE_FILTER_QUERY, 'saveFilter' );
71 +
72 + $this->_addFilter( 'visualizer_get_sidebar', 'getSidebar', 10, 2 );
73 +
64 74 }
65 75
66 76 /**
77 + * Generates the HTML of the sidebar for the chart.
78 + *
79 + * @since ?
80 + *
81 + * @access public
82 + */
83 + public function getSidebar( $sidebar, $chart_id ) {
84 + $chart = get_post( $chart_id );
85 + $data = $this->_getChartArray( $chart );
86 + $sidebar = '';
87 + $sidebar_class = $this->load_chart_class_name( $chart_id );
88 + if ( class_exists( $sidebar_class, true ) ) {
89 + $sidebar = new $sidebar_class( $data['settings'] );
90 + $sidebar->__series = $data['series'];
91 + $sidebar->__data = $data['data'];
92 + } else {
93 + $sidebar = apply_filters( 'visualizer_pro_chart_type_sidebar', '', $data );
94 + if ( $sidebar !== '' ) {
95 + $sidebar->__series = $data['series'];
96 + $sidebar->__data = $data['data'];
97 + }
98 + }
99 + return str_replace( "'", '"', $sidebar->__toString() );
100 + }
101 +
102 + /**
103 + * Sets the schedule for how JSON-endpoint charts should be updated.
104 + *
105 + * @since ?
106 + *
107 + * @access public
108 + */
109 + public function setJsonSchedule() {
110 + check_ajax_referer( Visualizer_Plugin::ACTION_JSON_SET_SCHEDULE . Visualizer_Plugin::VERSION, 'security' );
111 +
112 + $chart_id = filter_input(
113 + INPUT_POST,
114 + 'chart',
115 + FILTER_VALIDATE_INT,
116 + array(
117 + 'options' => array(
118 + 'min_range' => 1,
119 + ),
120 + )
121 + );
122 +
123 + if ( ! $chart_id ) {
124 + wp_send_json_error();
125 + }
126 +
127 + $time = filter_input(
128 + INPUT_POST,
129 + 'time',
130 + FILTER_VALIDATE_INT,
131 + array(
132 + 'options' => array(
133 + 'min_range' => -1,
134 + ),
135 + )
136 + );
137 +
138 + if ( Visualizer_Module::is_pro() ) {
139 + $is_woocommerce_report = filter_input(
140 + INPUT_POST,
141 + 'is_woocommerce_report',
142 + FILTER_VALIDATE_BOOLEAN
143 + );
144 +
145 + if ( $is_woocommerce_report ) {
146 + update_post_meta( $chart_id, Visualizer_Plugin::CF_IS_WOOCOMMERCE_SOURCE, true );
147 + } else {
148 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_IS_WOOCOMMERCE_SOURCE );
149 + }
150 + }
151 +
152 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_SCHEDULE );
153 +
154 + if ( -1 < $time ) {
155 + add_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_SCHEDULE, $time );
156 + // Update schedules.
157 + $schedules = get_option( Visualizer_Plugin::CF_JSON_SCHEDULE, array() );
158 + $schedules[ $chart_id ] = time() + $time * HOUR_IN_SECONDS;
159 + update_option( Visualizer_Plugin::CF_JSON_SCHEDULE, $schedules );
160 + }
161 + wp_send_json_success();
162 + }
163 +
164 + /**
165 + * Get the root elements for JSON-endpoint.
166 + *
167 + * @since ?
168 + *
169 + * @access public
170 + */
171 + public function getJsonRoots() {
172 + check_ajax_referer( Visualizer_Plugin::ACTION_JSON_GET_ROOTS . Visualizer_Plugin::VERSION, 'security' );
173 +
174 + $params = wp_parse_args( $_POST['params'] );
175 +
176 + $source = new Visualizer_Source_Json( $params );
177 +
178 + $roots = $source->fetchRoots();
179 + if ( empty( $roots ) ) {
180 + wp_send_json_error( array( 'msg' => $source->get_error() ) );
181 + }
182 +
183 + wp_send_json_success( array( 'url' => $params['url'], 'roots' => $roots ) );
184 + }
185 +
186 + /**
187 + * Get the data for the JSON-endpoint corresponding to the chosen root.
188 + *
189 + * @since ?
190 + *
191 + * @access public
192 + */
193 + public function getJsonData() {
194 + check_ajax_referer( Visualizer_Plugin::ACTION_JSON_GET_DATA . Visualizer_Plugin::VERSION, 'security' );
195 +
196 + $params = wp_parse_args( $_POST['params'] );
197 +
198 + $chart_id = $params['chart'];
199 +
200 + if ( empty( $chart_id ) ) {
201 + wp_die();
202 + }
203 +
204 + $source = new Visualizer_Source_Json( $params );
205 + $source->fetch();
206 + $data = $source->getRawData();
207 +
208 + if ( empty( $data ) ) {
209 + wp_send_json_error( array( 'msg' => esc_html__( 'Unable to fetch data from the endpoint. Please try again.', 'visualizer' ) ) );
210 + }
211 +
212 + $data = Visualizer_Render_Layout::show( 'editor-table', $data, $chart_id, 'viz-json-table', false, false );
213 + wp_send_json_success( array( 'table' => $data, 'root' => $params['root'], 'url' => $params['url'], 'paging' => $source->getPaginationElements() ) );
214 + }
215 +
216 + /**
217 + * Updates the database with the correct post parameters for JSON-endpoint charts.
218 + *
219 + * @since ?
220 + *
221 + * @access public
222 + */
223 + public function setJsonData() {
224 + check_ajax_referer( Visualizer_Plugin::ACTION_JSON_SET_DATA . Visualizer_Plugin::VERSION, 'security' );
225 +
226 + $params = $_POST;
227 + $chart_id = $_GET['chart'];
228 +
229 + if ( empty( $chart_id ) ) {
230 + wp_die();
231 + }
232 +
233 + $chart = get_post( $chart_id );
234 +
235 + $source = new Visualizer_Source_Json( $params );
236 + update_post_meta( $chart->ID, Visualizer_Plugin::CF_EDITABLE_TABLE, true );
237 + $source->fetchFromEditableTable();
238 +
239 + $content = $source->getData( get_post_meta( $chart->ID, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) );
240 + $chart->post_content = $content;
241 + wp_update_post( $chart->to_array() );
242 + update_post_meta( $chart->ID, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
243 + update_post_meta( $chart->ID, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
244 + update_post_meta( $chart->ID, Visualizer_Plugin::CF_DEFAULT_DATA, 0 );
245 + update_post_meta( $chart->ID, Visualizer_Plugin::CF_JSON_URL, $params['url'] );
246 + update_post_meta( $chart->ID, Visualizer_Plugin::CF_JSON_ROOT, $params['root'] );
247 +
248 + delete_post_meta( $chart->ID, Visualizer_Plugin::CF_JSON_HEADERS );
249 + $headers = array( 'method' => $params['method'] );
250 + if ( ! empty( $params['auth'] ) ) {
251 + $headers['auth'] = $params['auth'];
252 + } elseif ( ! empty( $params['username'] ) && ! empty( $params['password'] ) ) {
253 + $headers['auth'] = array( 'username' => $params['username'], 'password' => $params['password'] );
254 + }
255 +
256 + add_post_meta( $chart->ID, Visualizer_Plugin::CF_JSON_HEADERS, $headers );
257 +
258 + delete_post_meta( $chart->ID, Visualizer_Plugin::CF_JSON_PAGING );
259 + if ( ! empty( $params['paging'] ) ) {
260 + add_post_meta( $chart->ID, Visualizer_Plugin::CF_JSON_PAGING, $params['paging'] );
261 + }
262 +
263 + if ( Visualizer_Module::is_pro() ) {
264 + if ( ! empty( $params['vz_woo_source'] ) ) {
265 + update_post_meta( $chart->ID, Visualizer_Plugin::CF_JSON_WOOCOMMERCE_SOURCE, $params['vz_woo_source'] );
266 + } else {
267 + delete_post_meta( $chart->ID, Visualizer_Plugin::CF_JSON_WOOCOMMERCE_SOURCE );
268 + }
269 + }
270 +
271 + $time = filter_input(
272 + INPUT_POST,
273 + 'time',
274 + FILTER_VALIDATE_INT,
275 + array(
276 + 'options' => array(
277 + 'min_range' => -1,
278 + ),
279 + )
280 + );
281 +
282 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_SCHEDULE );
283 +
284 + if ( -1 < $time ) {
285 + add_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_SCHEDULE, $time );
286 + }
287 +
288 + // delete other source specific parameters.
289 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY );
290 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_DB_SCHEDULE );
291 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_URL );
292 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_SCHEDULE );
293 +
294 + $render = new Visualizer_Render_Page_Update();
295 + $render->id = $chart->ID;
296 + $render->data = json_encode( $source->getRawData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ) );
297 + $render->series = json_encode( $source->getSeries() );
298 + $render->render();
299 +
300 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
301 + }
302 +
303 +
304 + /**
67 305 * Fetches charts from database.
68 306 *
69 307 * This method is also called from the media pop-up (classic editor: create a post and add chart from insert content).
70 308 *
@@ -93,9 +331,9 @@
93 331 // 'filter' is from the modal from the add media button.
94 332 $filter = filter_input( INPUT_GET, 'filter', FILTER_SANITIZE_STRING );
95 333 }
96 334
97 - if ( $filter && in_array( $filter, Visualizer_Plugin::getChartTypes() ) ) {
335 + if ( $filter && in_array( $filter, Visualizer_Plugin::getChartTypes(), true ) ) {
98 336 $query_args['meta_query'] = array(
99 337 array(
100 338 'key' => Visualizer_Plugin::CF_CHART_TYPE,
101 339 'value' => $filter,
@@ -146,9 +384,9 @@
146 384 $chart = $this->_chart;
147 385 }
148 386 $type = get_post_meta( $chart->ID, Visualizer_Plugin::CF_CHART_TYPE, true );
149 387 $series = apply_filters( Visualizer_Plugin::FILTER_GET_CHART_SERIES, get_post_meta( $chart->ID, Visualizer_Plugin::CF_SERIES, true ), $chart->ID, $type );
150 - $data = apply_filters( Visualizer_Plugin::FILTER_GET_CHART_DATA, unserialize( $chart->post_content ), $chart->ID, $type );
388 + $data = self::get_chart_data( $chart, $type );
151 389 $library = $this->load_chart_type( $chart->ID );
152 390
153 391 $settings = get_post_meta( $chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
154 392 $settings = apply_filters( Visualizer_Plugin::FILTER_GET_CHART_SETTINGS, $settings, $chart->ID, $type );
@@ -162,8 +400,10 @@
162 400 $css = $arguments[0];
163 401 $settings = $arguments[1];
164 402 }
165 403
404 + $date_formats = Visualizer_Source::get_date_formats_if_exists( $series, $data );
405 +
166 406 return array(
167 407 'type' => $type,
168 408 'series' => $series,
169 409 'settings' => $settings,
@@ -169,8 +409,9 @@
169 409 'settings' => $settings,
170 410 'data' => $data,
171 411 'library' => $library,
172 412 'css' => $css,
413 + 'date_formats' => $date_formats,
173 414 );
174 415 }
175 416
176 417 /**
@@ -197,9 +438,9 @@
197 438 *
198 439 * @access public
199 440 */
200 441 public function deleteChart() {
201 - $is_post = $_SERVER['REQUEST_METHOD'] == 'POST';
442 + $is_post = $_SERVER['REQUEST_METHOD'] === 'POST';
202 443 $input_method = $is_post ? INPUT_POST : INPUT_GET;
203 444 $chart_id = $success = false;
204 445 $nonce = wp_verify_nonce( filter_input( $input_method, 'nonce' ) );
205 446 $capable = current_user_can( 'delete_posts' );
@@ -215,13 +456,26 @@
215 456 )
216 457 );
217 458 if ( $chart_id ) {
218 459 $chart = get_post( $chart_id );
219 - $success = $chart && $chart->post_type == Visualizer_Plugin::CPT_VISUALIZER;
460 + $success = $chart && $chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
220 461 }
221 462 }
222 463 if ( $success ) {
223 - wp_delete_post( $chart_id, true );
464 + global $sitepress;
465 + if ( Visualizer_Module::is_pro() && ( function_exists( 'icl_get_languages' ) && $sitepress instanceof \SitePress ) ) {
466 + $trid = $sitepress->get_element_trid( $chart_id, 'post_' . Visualizer_Plugin::CPT_VISUALIZER );
467 + $translations = $sitepress->get_element_translations( $trid );
468 + if ( ! empty( $translations ) ) {
469 + foreach ( $translations as $translated_post ) {
470 + wp_delete_post( $translated_post->element_id, true );
471 + }
472 + } else {
473 + wp_delete_post( $chart_id, true );
474 + }
475 + } else {
476 + wp_delete_post( $chart_id, true );
477 + }
224 478 }
225 479 if ( $is_post ) {
226 480 self::_sendResponse(
227 481 array(
@@ -228,13 +482,41 @@
228 482 'success' => $success,
229 483 )
230 484 );
231 485 }
232 - wp_redirect( wp_get_referer() );
486 + wp_redirect( remove_query_arg( 'vaction', wp_get_referer() ) );
233 487 exit;
234 488 }
235 489
236 490 /**
491 + * Delete charts that are still in auto-draft mode.
492 + */
493 + private function deleteOldCharts() {
494 + $query = new WP_Query(
495 + array(
496 + 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
497 + 'post_status' => 'auto-draft',
498 + 'fields' => 'ids',
499 + 'update_post_meta_cache' => false,
500 + 'update_post_term_cache' => false,
501 + 'posts_per_page' => 50,
502 + 'date_query' => array(
503 + array(
504 + 'before' => 'today',
505 + ),
506 + ),
507 + )
508 + );
509 +
510 + if ( $query->have_posts() ) {
511 + $ids = array();
512 + while ( $query->have_posts() ) {
513 + wp_delete_post( $query->next_post(), true );
514 + }
515 + }
516 + }
517 +
518 + /**
237 519 * Renders appropriate page for chart builder. Creates new auto draft chart
238 520 * if no chart has been specified.
239 521 *
240 522 * @since 1.0.0
@@ -242,49 +524,112 @@
242 524 * @access public
243 525 */
244 526 public function renderChartPages() {
245 527 defined( 'IFRAME_REQUEST' ) || define( 'IFRAME_REQUEST', 1 );
528 + if ( ! defined( 'ET_BUILDER_PRODUCT_VERSION' ) && function_exists( 'et_get_theme_version' ) ) {
529 + define( 'ET_BUILDER_PRODUCT_VERSION', et_get_theme_version() );
530 + }
531 + // Set current screen for the render chart.
532 + set_current_screen( 'visualizer_render_chart' );
246 533 // check chart, if chart not exists, will create new one and redirects to the same page with proper chart id
247 534 $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
248 - if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type != Visualizer_Plugin::CPT_VISUALIZER ) {
249 - $default_type = isset( $_GET['type'] ) && ! empty( $_GET['type'] ) ? $_GET['type'] : 'line';
250 - $source = new Visualizer_Source_Csv( VISUALIZER_ABSPATH . DIRECTORY_SEPARATOR . 'samples' . DIRECTORY_SEPARATOR . $default_type . '.csv' );
251 - $source->fetch();
252 - $chart_id = wp_insert_post(
253 - array(
254 - 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
255 - 'post_title' => 'Visualization',
256 - 'post_author' => get_current_user_id(),
257 - 'post_status' => 'auto-draft',
258 - 'post_content' => $source->getData(),
259 - )
260 - );
261 - if ( $chart_id && ! is_wp_error( $chart_id ) ) {
262 - add_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_TYPE, $default_type );
263 - add_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, 1 );
264 - add_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
265 - add_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
266 - add_post_meta(
267 - $chart_id,
268 - Visualizer_Plugin::CF_SETTINGS,
535 + if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ) {
536 + if ( empty( $_GET['lang'] ) || empty( $_GET['parent_chart_id'] ) ) {
537 + $this->deleteOldCharts();
538 + $default_type = isset( $_GET['type'] ) && ! empty( $_GET['type'] ) ? $_GET['type'] : 'line';
539 + $source = new Visualizer_Source_Csv( VISUALIZER_ABSPATH . DIRECTORY_SEPARATOR . 'samples' . DIRECTORY_SEPARATOR . $default_type . '.csv' );
540 + $source->fetch();
541 + $chart_id = wp_insert_post(
269 542 array(
270 - 'focusTarget' => 'datum',
543 + 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
544 + 'post_title' => 'Visualization',
545 + 'post_author' => get_current_user_id(),
546 + 'post_status' => 'auto-draft',
547 + 'post_content' => $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ),
271 548 )
272 549 );
550 + if ( $chart_id && ! is_wp_error( $chart_id ) ) {
551 + add_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_TYPE, $default_type );
552 + add_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, 1 );
553 + add_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
554 + add_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
555 + add_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_LIBRARY, '' );
556 + add_post_meta(
557 + $chart_id,
558 + Visualizer_Plugin::CF_SETTINGS,
559 + array(
560 + 'focusTarget' => 'datum',
561 + )
562 + );
563 +
564 + do_action( 'visualizer_pro_new_chart_defaults', $chart_id );
565 + }
566 + } else {
567 + if ( current_user_can( 'edit_posts' ) ) {
568 + $parent_chart_id = isset( $_GET['parent_chart_id'] ) ? filter_var( $_GET['parent_chart_id'], FILTER_VALIDATE_INT ) : '';
569 + $success = false;
570 + if ( $parent_chart_id ) {
571 + $parent_chart = get_post( $parent_chart_id );
572 + $success = $parent_chart && $parent_chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
573 + }
574 + if ( $success ) {
575 + $new_chart_id = wp_insert_post(
576 + array(
577 + 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
578 + 'post_title' => 'Visualization',
579 + 'post_author' => get_current_user_id(),
580 + 'post_status' => $parent_chart->post_status,
581 + 'post_content' => $parent_chart->post_content,
582 + )
583 + );
584 +
585 + if ( is_wp_error( $new_chart_id ) ) {
586 + 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__ );
587 + } else {
588 + $post_meta = get_post_meta( $parent_chart_id );
589 + $chart_id = $new_chart_id;
590 + foreach ( $post_meta as $key => $value ) {
591 + if ( strpos( $key, 'visualizer-' ) !== false ) {
592 + add_post_meta( $new_chart_id, $key, maybe_unserialize( $value[0] ) );
593 + }
594 + }
595 + }
596 + }
597 + }
273 598 do_action( 'visualizer_pro_new_chart_defaults', $chart_id );
274 599 }
275 - wp_redirect( add_query_arg( 'chart', (int) $chart_id ) );
276 - defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
600 + wp_redirect( esc_url_raw( add_query_arg( 'chart', (int) $chart_id ) ) );
601 +
602 + if ( defined( 'WP_TESTS_DOMAIN' ) ) {
603 + wp_die();
604 + }
605 + exit();
277 606 }
278 607
279 - $this->load_chart_type( $chart_id );
608 + $_POST['save_chart_image'] = isset( $_POST['save_chart_image'] ) && 'yes' === $_POST['save_chart_image'] ? true : false;
609 + $_POST['lazy_load_chart'] = isset( $_POST['lazy_load_chart'] ) && 'yes' === $_POST['lazy_load_chart'] ? true : false;
280 610
611 + if ( isset( $_POST['chart-img'] ) && ! empty( $_POST['chart-img'] ) ) {
612 + $attachment_id = $this->save_chart_image( $_POST['chart-img'], $chart_id, $_POST['save_chart_image'] );
613 + if ( $attachment_id ) {
614 + update_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_IMAGE, $attachment_id );
615 + }
616 + }
617 + $lib = $this->load_chart_type( $chart_id );
618 +
619 + // the alpha color picker (RGBA) is not supported by google.
620 + $color_picker_dep = 'wp-color-picker';
621 + if ( in_array( $lib, array( 'chartjs', 'datatables' ), true ) && ! wp_script_is( 'wp-color-picker-alpha', 'registered' ) ) {
622 + wp_register_script( 'wp-color-picker-alpha', VISUALIZER_ABSURL . 'js/lib/wp-color-picker-alpha.min.js', array( 'wp-color-picker' ), Visualizer_Plugin::VERSION );
623 + $color_picker_dep = 'wp-color-picker-alpha';
624 + }
625 +
281 626 // enqueue and register scripts and styles
282 627 wp_register_script( 'visualizer-chosen', VISUALIZER_ABSURL . 'js/lib/chosen.jquery.min.js', array( 'jquery' ), Visualizer_Plugin::VERSION );
283 628 wp_register_style( 'visualizer-chosen', VISUALIZER_ABSURL . 'css/lib/chosen.min.css', array(), Visualizer_Plugin::VERSION );
284 629
285 630 wp_register_style( 'visualizer-frame', VISUALIZER_ABSURL . 'css/frame.css', array( 'visualizer-chosen' ), Visualizer_Plugin::VERSION );
286 - wp_register_script( 'visualizer-frame', VISUALIZER_ABSURL . 'js/frame.js', array( 'visualizer-chosen' ), Visualizer_Plugin::VERSION, true );
631 + wp_register_script( 'visualizer-frame', VISUALIZER_ABSURL . 'js/frame.js', array( 'visualizer-chosen', 'jquery-ui-accordion', 'jquery-ui-tabs' ), Visualizer_Plugin::VERSION, true );
287 632 wp_register_script( 'visualizer-customization', $this->get_user_customization_js(), array(), null, true );
288 633 wp_register_script(
289 634 'visualizer-render',
290 635 VISUALIZER_ABSURL . 'js/render-facade.js',
@@ -295,19 +640,23 @@
295 640 wp_register_script(
296 641 'visualizer-preview',
297 642 VISUALIZER_ABSURL . 'js/preview.js',
298 643 array(
299 - 'wp-color-picker',
644 + $color_picker_dep,
300 645 'visualizer-render',
301 646 ),
302 647 Visualizer_Plugin::VERSION,
303 648 true
304 649 );
305 - // added by Ash/Upwork
306 - if ( VISUALIZER_PRO ) {
650 + wp_register_script( 'visualizer-editor-simple', VISUALIZER_ABSURL . 'js/simple-editor.js', array( 'jquery' ), Visualizer_Plugin::VERSION, true );
651 +
652 + do_action( 'visualizer_add_scripts' );
653 +
654 + if ( Visualizer_Module::is_pro() && Visualizer_Module::is_pro_older_than( '1.9.0' ) ) {
307 655 global $Visualizer_Pro;
308 656 $Visualizer_Pro->_addScriptsAndStyles();
309 657 }
658 +
310 659 // dispatch pages
311 660 $this->_chart = get_post( $chart_id );
312 661 $tab = isset( $_GET['tab'] ) && ! empty( $_GET['tab'] ) ? $_GET['tab'] : 'visualizer';
313 662
@@ -319,8 +668,9 @@
319 668 if ( isset( $_POST['cancel'] ) && 1 === intval( $_POST['cancel'] ) ) {
320 669 // if the cancel button is clicked.
321 670 $this->undoRevisions( $chart_id, true );
322 671 } elseif ( isset( $_POST['save'] ) && 1 === intval( $_POST['save'] ) ) {
672 + $this->handlePermissions();
323 673 // if the save button is clicked.
324 674 $this->undoRevisions( $chart_id, false );
325 675 } else {
326 676 // if the edit button is clicked.
@@ -326,8 +676,16 @@
326 676 // if the edit button is clicked.
327 677 $this->_chart = $this->handleExistingRevisions( $chart_id, $this->_chart );
328 678 }
329 679
680 + // Clear existing chart cache.
681 + if ( isset( $_POST['save'] ) && 1 === intval( $_POST['save'] ) ) {
682 + $cache_key = Visualizer_Plugin::CF_CHART_CACHE . '_' . $chart_id;
683 + if ( get_transient( $cache_key ) ) {
684 + delete_transient( $cache_key );
685 + }
686 + }
687 +
330 688 switch ( $tab ) {
331 689 case 'settings':
332 690 $this->_handleDataAndSettingsPage();
333 691 break;
@@ -335,9 +693,9 @@
335 693 case 'visualizer': // fall through.
336 694 $this->_handleTypesPage();
337 695 break;
338 696 default:
339 - do_action( 'visualizer_pro_handle_tab', $tab, $this->_chart );
697 + // this should never happen.
340 698 break;
341 699 }
342 700 defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
343 701 }
@@ -344,25 +702,26 @@
344 702
345 703 /**
346 704 * Load code editor assets.
347 705 */
348 - private function loadCodeEditorAssets() {
706 + private function loadCodeEditorAssets( $chart_id ) {
349 707 global $wp_version;
350 708
351 - if ( ! VISUALIZER_PRO ) {
709 + $wp_scripts = wp_scripts();
710 +
711 + // data tables assets.
712 + wp_register_script( 'visualizer-datatables', VISUALIZER_ABSURL . 'js/lib/datatables.min.js', array( 'jquery-ui-core' ), Visualizer_Plugin::VERSION );
713 + wp_register_style( 'visualizer-datatables', VISUALIZER_ABSURL . 'css/lib/datatables.min.css', array(), Visualizer_Plugin::VERSION );
714 + 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 );
715 + wp_enqueue_script( 'visualizer-datatables' );
716 + wp_enqueue_style( 'visualizer-jquery-ui' );
717 +
718 + if ( ! Visualizer_Module::is_pro() ) {
352 719 return;
353 720 }
354 721
355 - $table_col_mapping = Visualizer_Source_Query_Params::get_all_db_tables_column_mapping();
722 + $table_col_mapping = Visualizer_Source_Query_Params::get_all_db_tables_column_mapping( $chart_id );
356 723
357 - // data tables assets.
358 - wp_register_script( 'visualizer-datatables', '//cdn.datatables.net/1.10.16/js/jquery.dataTables.min.js', array( 'jquery-ui-core' ), Visualizer_Plugin::VERSION );
359 - wp_register_style( 'visualizer-datatables', '//cdn.datatables.net/1.10.16/css/jquery.dataTables.min.css', array(), Visualizer_Plugin::VERSION );
360 - wp_register_style( 'visualizer-datatables-ui', '//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css', array( 'visualizer-datatables' ), Visualizer_Plugin::VERSION );
361 -
362 - wp_enqueue_script( 'visualizer-datatables' );
363 - wp_enqueue_style( 'visualizer-datatables-ui' );
364 -
365 724 if ( version_compare( $wp_version, '4.9.0', '<' ) ) {
366 725 // code mirror assets.
367 726 wp_register_script( 'visualizer-codemirror-core', '//codemirror.net/lib/codemirror.js', array( 'jquery' ), Visualizer_Plugin::VERSION );
368 727 wp_register_script( 'visualizer-codemirror-placeholder', '//codemirror.net/addon/display/placeholder.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
@@ -396,8 +755,25 @@
396 755 return $table_col_mapping;
397 756 }
398 757
399 758 /**
759 + * Handle permissions from the new conslidated settings sidebar.
760 + */
761 + private function handlePermissions() {
762 + if ( ! Visualizer_Module::is_pro() ) {
763 + return;
764 + }
765 +
766 + // we will not support old free and new pro.
767 + // handling new free and old pro.
768 + if ( has_action( 'visualizer_pro_handle_tab' ) ) {
769 + do_action( 'visualizer_pro_handle_tab', 'permissions', $this->_chart );
770 + } else {
771 + do_action( 'visualizer_handle_permissions', $this->_chart );
772 + }
773 + }
774 +
775 + /**
400 776 * Handle data and settings page
401 777 */
402 778 private function _handleDataAndSettingsPage() {
403 779 if ( isset( $_POST['map_api_key'] ) ) {
@@ -402,30 +778,53 @@
402 778 private function _handleDataAndSettingsPage() {
403 779 if ( isset( $_POST['map_api_key'] ) ) {
404 780 update_option( 'visualizer-map-api-key', $_POST['map_api_key'] );
405 781 }
406 - if ( $_SERVER['REQUEST_METHOD'] == 'POST' && isset( $_GET['nonce'] ) && wp_verify_nonce( $_GET['nonce'] ) ) {
407 - if ( $this->_chart->post_status == 'auto-draft' ) {
782 +
783 + if ( $_SERVER['REQUEST_METHOD'] === 'POST' && isset( $_GET['nonce'] ) && wp_verify_nonce( $_GET['nonce'] ) ) {
784 + if ( $this->_chart->post_status === 'auto-draft' ) {
408 785 $this->_chart->post_status = 'publish';
409 786
410 787 // 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.
411 788 // we do not want any difference in data so disable revisions temporarily.
412 - add_filter( 'wp_revisions_to_keep', '__return_false' );
789 + $this->disableRevisionsTemporarily();
790 +
413 791 wp_update_post( $this->_chart->to_array() );
414 792 }
415 793 // save meta data only when it is NOT being canceled.
416 794 if ( ! ( isset( $_POST['cancel'] ) && 1 === intval( $_POST['cancel'] ) ) ) {
417 795 update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, $_POST );
796 +
797 + // we will keep a parameter called 'internal_title' that will be set to the given title or, if empty, the chart ID
798 + // this will help in searching with the chart id.
799 + $settings = get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
800 + $title = null;
801 + if ( isset( $settings['title'] ) && ! empty( $settings['title'] ) ) {
802 + $title = $settings['title'];
803 + if ( is_array( $title ) ) {
804 + $title = $settings['title']['text'];
805 + }
806 + }
807 + if ( empty( $title ) ) {
808 + $title = $this->_chart->ID;
809 + }
810 + $settings['internal_title'] = $title;
811 + $settings_label = isset( $settings['pieResidueSliceLabel'] ) ? $settings['pieResidueSliceLabel'] : '';
812 + if ( empty( $settings_label ) ) {
813 + $settings['pieResidueSliceLabel'] = esc_html__( 'Other', 'visualizer' );
814 + } else {
815 + $settings['pieResidueSliceLabel'] = $settings_label;
816 + }
817 + update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, $settings );
418 818 }
419 819 $render = new Visualizer_Render_Page_Send();
420 820 $render->text = sprintf( '[visualizer id="%d"]', $this->_chart->ID );
421 821 wp_iframe( array( $render, 'render' ) );
422 -
423 822 return;
424 823 }
425 824 $data = $this->_getChartArray();
426 825 $sidebar = '';
427 - $sidebar_class = 'Visualizer_Render_Sidebar_Type_' . ucfirst( $data['type'] );
826 + $sidebar_class = $this->load_chart_class_name( $this->_chart->ID );
428 827 if ( class_exists( $sidebar_class, true ) ) {
429 828 $sidebar = new $sidebar_class( $data['settings'] );
430 829 $sidebar->__series = $data['series'];
431 830 $sidebar->__data = $data['data'];
@@ -430,14 +829,14 @@
430 829 $sidebar->__series = $data['series'];
431 830 $sidebar->__data = $data['data'];
432 831 } else {
433 832 $sidebar = apply_filters( 'visualizer_pro_chart_type_sidebar', '', $data );
434 - if ( $sidebar != '' ) {
833 + if ( $sidebar !== '' ) {
435 834 $sidebar->__series = $data['series'];
436 835 $sidebar->__data = $data['data'];
437 836 }
438 837 }
439 - unset( $data['settings']['width'], $data['settings']['height'] );
838 + unset( $data['settings']['width'], $data['settings']['height'], $data['settings']['chartArea'] );
440 839 wp_enqueue_style( 'wp-color-picker' );
441 840 wp_enqueue_style( 'visualizer-frame' );
442 841 wp_enqueue_script( 'visualizer-preview' );
443 842 wp_enqueue_script( 'visualizer-chosen' );
@@ -442,17 +841,37 @@
442 841 wp_enqueue_script( 'visualizer-preview' );
443 842 wp_enqueue_script( 'visualizer-chosen' );
444 843 wp_enqueue_script( 'visualizer-render' );
445 844
446 - $table_col_mapping = $this->loadCodeEditorAssets();
845 + if ( Visualizer_Module::can_show_feature( 'simple-editor' ) ) {
846 + wp_enqueue_script( 'visualizer-editor-simple' );
847 + wp_localize_script(
848 + 'visualizer-editor-simple',
849 + 'visualizer1',
850 + array(
851 + 'ajax' => array(
852 + 'url' => admin_url( 'admin-ajax.php' ),
853 + 'nonces' => array(
854 + ),
855 + 'actions' => array(
856 + ),
857 + ),
858 + )
859 + );
860 + }
447 861
862 + $table_col_mapping = $this->loadCodeEditorAssets( $this->_chart->ID );
863 +
448 864 wp_localize_script(
449 865 'visualizer-render',
450 866 'visualizer',
451 867 array(
452 868 'l10n' => array(
453 - 'invalid_source' => esc_html__( 'You have entered invalid URL. Please, insert proper URL.', 'visualizer' ),
869 + 'invalid_source' => esc_html__( 'You have entered an invalid URL. Please provide a valid URL.', 'visualizer' ),
454 870 'loading' => esc_html__( 'Loading...', 'visualizer' ),
871 + 'json_error' => esc_html__( 'An error occured in fetching data.', 'visualizer' ),
872 + 'select_columns' => esc_html__( 'Please select a few columns to include in the chart.', 'visualizer' ),
873 + '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' ),
455 874 ),
456 875 'charts' => array(
457 876 'canvas' => $data,
458 877 'id' => $this->_chart->ID,
@@ -463,19 +882,29 @@
463 882 'url' => admin_url( 'admin-ajax.php' ),
464 883 'nonces' => array(
465 884 'permissions' => wp_create_nonce( Visualizer_Plugin::ACTION_FETCH_PERMISSIONS_DATA ),
466 885 'db_get_data' => wp_create_nonce( Visualizer_Plugin::ACTION_FETCH_DB_DATA . Visualizer_Plugin::VERSION ),
886 + 'json_get_roots' => wp_create_nonce( Visualizer_Plugin::ACTION_JSON_GET_ROOTS . Visualizer_Plugin::VERSION ),
887 + 'json_get_data' => wp_create_nonce( Visualizer_Plugin::ACTION_JSON_GET_DATA . Visualizer_Plugin::VERSION ),
888 + 'json_set_schedule' => wp_create_nonce( Visualizer_Plugin::ACTION_JSON_SET_SCHEDULE . Visualizer_Plugin::VERSION ),
467 889 ),
468 890 'actions' => array(
469 891 'permissions' => Visualizer_Plugin::ACTION_FETCH_PERMISSIONS_DATA,
470 892 'db_get_data' => Visualizer_Plugin::ACTION_FETCH_DB_DATA,
893 + 'json_get_roots' => Visualizer_Plugin::ACTION_JSON_GET_ROOTS,
894 + 'json_get_data' => Visualizer_Plugin::ACTION_JSON_GET_DATA,
895 + 'json_set_schedule' => Visualizer_Plugin::ACTION_JSON_SET_SCHEDULE,
471 896 ),
472 897 ),
473 898 'db_query' => array(
474 899 'tables' => $table_col_mapping,
475 900 ),
476 - 'is_pro' => VISUALIZER_PRO,
901 + 'is_pro' => Visualizer_Module::is_pro(),
477 902 'page_type' => 'chart',
903 + 'json_tag_separator' => Visualizer_Source_Json::TAG_SEPARATOR,
904 + 'json_tag_separator_view' => Visualizer_Source_Json::TAG_SEPARATOR_VIEW,
905 + 'is_front' => false,
906 + 'rest_base' => get_rest_url( null, 'wc/v3/reports/' ),
478 907 )
479 908 );
480 909
481 910 $render = new Visualizer_Render_Page_Data();
@@ -483,21 +912,25 @@
483 912 $render->type = $data['type'];
484 913 $render->custom_css = $data['css'];
485 914 $render->sidebar = $sidebar;
486 915 if ( filter_input( INPUT_GET, 'library', FILTER_VALIDATE_BOOLEAN ) ) {
487 - $render->button = filter_input( INPUT_GET, 'action' ) == Visualizer_Plugin::ACTION_EDIT_CHART
916 + $render->button = filter_input( INPUT_GET, 'action' ) === Visualizer_Plugin::ACTION_EDIT_CHART
488 917 ? esc_html__( 'Save Chart', 'visualizer' )
489 918 : esc_html__( 'Create Chart', 'visualizer' );
490 - if ( filter_input( INPUT_GET, 'action' ) == Visualizer_Plugin::ACTION_EDIT_CHART ) {
919 + if ( filter_input( INPUT_GET, 'action' ) === Visualizer_Plugin::ACTION_EDIT_CHART ) {
491 920 $render->cancel_button = esc_html__( 'Cancel', 'visualizer' );
492 921 }
493 922 } else {
494 923 $render->button = esc_attr__( 'Insert Chart', 'visualizer' );
495 924 }
496 - if ( VISUALIZER_PRO ) {
925 +
926 + do_action( 'visualizer_enqueue_scripts_and_styles', $data, $this->_chart->ID );
927 +
928 + if ( Visualizer_Module::is_pro() && Visualizer_Module::is_pro_older_than( '1.9.0' ) ) {
497 929 global $Visualizer_Pro;
498 - $Visualizer_Pro->_enqueueScriptsAndStyles( $data );
930 + $Visualizer_Pro->_enqueueScriptsAndStyles( $data, $this->_chart->ID );
499 931 }
932 +
500 933 $this->_addAction( 'admin_head', 'renderFlattrScript' );
501 934 wp_iframe( array( $render, 'render' ) );
502 935 }
503 936
@@ -509,24 +942,35 @@
509 942 * @access private
510 943 */
511 944 private function _handleTypesPage() {
512 945 // process post request
513 - if ( $_SERVER['REQUEST_METHOD'] == 'POST' && wp_verify_nonce( filter_input( INPUT_POST, 'nonce' ) ) ) {
946 + if ( $_SERVER['REQUEST_METHOD'] === 'POST' && wp_verify_nonce( filter_input( INPUT_POST, 'nonce' ) ) ) {
514 947 $type = filter_input( INPUT_POST, 'type' );
515 - if ( in_array( $type, Visualizer_Plugin::getChartTypes() ) ) {
948 + $library = filter_input( INPUT_POST, 'chart-library' );
949 + if ( in_array( $type, Visualizer_Plugin::getChartTypes(), true ) ) {
950 + if ( empty( $library ) ) {
951 + // library cannot be empty.
952 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, 'Chart library empty while creating the chart! Aborting...', 'error', __FILE__, __LINE__ );
953 + return;
954 + }
955 +
516 956 // save new chart type
517 957 update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_CHART_TYPE, $type );
958 + update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_CHART_LIBRARY, $library );
518 959 // if the chart has default data, update it with appropriate default data for new type
519 960 if ( filter_var( get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_DEFAULT_DATA, true ), FILTER_VALIDATE_BOOLEAN ) ) {
520 961 $source = new Visualizer_Source_Csv( VISUALIZER_ABSPATH . DIRECTORY_SEPARATOR . 'samples' . DIRECTORY_SEPARATOR . $type . '.csv' );
521 962 $source->fetch();
522 - $this->_chart->post_content = $source->getData();
963 + $this->_chart->post_content = $source->getData( get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) );
523 964 wp_update_post( $this->_chart->to_array() );
524 965 update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
525 966 }
967 +
968 + Visualizer_Module_Utility::set_defaults( $this->_chart );
969 +
526 970 // redirect to next tab
527 971 // changed by Ash/Upwork
528 - wp_redirect( add_query_arg( 'tab', 'settings' ) );
972 + wp_redirect( esc_url_raw( add_query_arg( 'tab', 'settings' ) ) );
529 973
530 974 return;
531 975 }
532 976 }
@@ -531,9 +975,9 @@
531 975 }
532 976 }
533 977 $render = new Visualizer_Render_Page_Types();
534 978 $render->type = get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_CHART_TYPE, true );
535 - $render->types = Visualizer_Module_Admin::_getChartTypesLocalized();
979 + $render->types = Visualizer_Module_Admin::_getChartTypesLocalized( false, false, false, 'types' );
536 980 $render->chart = $this->_chart;
537 981 wp_enqueue_style( 'visualizer-frame' );
538 982 wp_enqueue_script( 'visualizer-frame' );
539 983 wp_iframe( array( $render, 'render' ) );
@@ -549,10 +993,131 @@
549 993 */
550 994 public function renderFlattrScript() {
551 995 echo '';
552 996 }
553 - // changed by Ash/Upwork
997 +
554 998 /**
999 + * Processes the CSV that is sent in the request as a string.
1000 + *
1001 + * @since 3.2.0
1002 + */
1003 + private function handleCSVasString( $data, $editor_type ) {
1004 + $source = null;
1005 +
1006 + switch ( $editor_type ) {
1007 + case 'text':
1008 + // data coming in from the text editor.
1009 + $tmpfile = tempnam( get_temp_dir(), Visualizer_Plugin::NAME );
1010 + $handle = fopen( $tmpfile, 'w' );
1011 + $values = preg_split( '/[\n\r]+/', stripslashes( trim( $data ) ) );
1012 + if ( $values ) {
1013 + foreach ( $values as $row ) {
1014 + if ( empty( $row ) ) {
1015 + continue;
1016 + }
1017 + $row = explode( ',', $row );
1018 + $row = array_map(
1019 + function( $r ) {
1020 + return '' === $r ? ' ' : $r;
1021 + },
1022 + $row
1023 + );
1024 + $row = implode( ',', $row );
1025 + // don't use fpucsv here because we need to just dump the data
1026 + // minus the empty rows
1027 + // because fputcsv needs to tokenize
1028 + // we can standardize the CSV enclosure here and replace all ' with "
1029 + // we can assume that if there are an even number of ' they should be changed to "
1030 + // but that will screw up words like fo'c'sle
1031 + // so here let's just assume ' will NOT be used for enclosures
1032 + fwrite( $handle, $row );
1033 + fwrite( $handle, PHP_EOL );
1034 + }
1035 + }
1036 + $source = new Visualizer_Source_Csv( $tmpfile );
1037 + fclose( $handle );
1038 + break;
1039 + case 'table':
1040 + // Import from Chart
1041 + // fall-through.
1042 + case 'excel':
1043 + // data coming in from the excel editor.
1044 + $source = apply_filters( 'visualizer_pro_handle_chart_data', $data, '' );
1045 + break;
1046 + }
1047 + return $source;
1048 + }
1049 +
1050 + /**
1051 + * Parses the data uploaded as an HTML table.
1052 + *
1053 + * @since 3.2.0
1054 + *
1055 + * @access private
1056 + */
1057 + private function handleTabularData() {
1058 + $csv = array();
1059 + // the datatable mentions the headers twice, so lets remove the duplicates.
1060 + $headers = array_unique( array_filter( $_POST['header'] ) );
1061 + $types = $_POST['type'];
1062 +
1063 + // capture all the indexes that correspond to excluded columns.
1064 + $exclude = array();
1065 + $index = 0;
1066 + foreach ( $types as $type ) {
1067 + if ( empty( $type ) ) {
1068 + $exclude[] = $index;
1069 + }
1070 + $index++;
1071 + }
1072 +
1073 + // when N headers are being renamed, the number of headers increases by N
1074 + // because of the way datatable duplicates header information
1075 + // so unset the headers that have been renamed.
1076 + if ( count( $headers ) !== count( $types ) ) {
1077 + $to = count( $headers );
1078 + for ( $i = count( $types ); $i < $to; $i++ ) {
1079 + unset( $headers[ $i + 1 ] );
1080 + }
1081 + }
1082 +
1083 + $columns = array();
1084 + for ( $i = 0; $i < count( $headers ); $i++ ) {
1085 + if ( ! isset( $_POST[ 'data' . $i ] ) ) {
1086 + continue;
1087 + }
1088 + $columns[ $i ] = $_POST[ 'data' . $i ];
1089 + }
1090 +
1091 + $csv[] = $headers;
1092 + $csv[] = $types;
1093 + for ( $j = 0; $j < count( $columns[0] ); $j++ ) {
1094 + $row = array();
1095 + for ( $i = 0; $i < count( $headers ); $i++ ) {
1096 + $row[] = sanitize_text_field( $columns[ $i ][ $j ] );
1097 + }
1098 + $csv[] = $row;
1099 + }
1100 +
1101 + $tmpfile = tempnam( get_temp_dir(), Visualizer_Plugin::NAME );
1102 + $handle = fopen( $tmpfile, 'w' );
1103 +
1104 + if ( $csv ) {
1105 + $index = 0;
1106 + foreach ( $csv as $row ) {
1107 + // remove all the cells corresponding to the excluded headers.
1108 + foreach ( $exclude as $j ) {
1109 + unset( $row[ $j ] );
1110 + }
1111 + fputcsv( $handle, $row );
1112 + }
1113 + }
1114 + $source = new Visualizer_Source_Csv( $tmpfile );
1115 + fclose( $handle );
1116 + return $source;
1117 + }
1118 +
1119 + /**
555 1120 * Parses uploaded CSV file and saves new data for the chart.
556 1121 *
557 1122 * @since 1.0.0
558 1123 *
@@ -574,9 +1139,9 @@
574 1139
575 1140 // check chart, if chart exists
576 1141 // do not use filter_input as it does not work for phpunit test cases, use filter_var instead
577 1142 $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
578 - if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type != Visualizer_Plugin::CPT_VISUALIZER ) {
1143 + if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ) {
579 1144 if ( ! $can_die ) {
580 1145 return;
581 1146 }
582 1147 status_header( 400 );
@@ -582,8 +1147,10 @@
582 1147 status_header( 400 );
583 1148 exit;
584 1149 }
585 1150
1151 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Uploading data for chart %d with POST = %s and GET = %s', $chart_id, print_r( $_POST, true ), print_r( $_GET, true ) ), 'debug', __FILE__, __LINE__ );
1152 +
586 1153 if ( ! isset( $_POST['vz-import-time'] ) ) {
587 1154 apply_filters( 'visualizer_pro_remove_schedule', $chart_id );
588 1155 }
589 1156
@@ -588,32 +1155,65 @@
588 1155 }
589 1156
590 1157 if ( ! isset( $_POST['chart_data_src'] ) || Visualizer_Plugin::CF_SOURCE_FILTER !== $_POST['chart_data_src'] ) {
591 1158 // delete the filters in case this chart is being uploaded from other data sources
592 - delete_post_meta( $chart_id, 'visualizer-filter-config' );
1159 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_FILTER_CONFIG );
1160 + delete_post_meta( $chart_id, '__transient-' . Visualizer_Plugin::CF_FILTER_CONFIG );
1161 + delete_post_meta( $chart_id, '__transient-' . Visualizer_Plugin::CF_DB_QUERY );
593 1162
594 1163 // delete "import from db" specific parameters.
595 1164 delete_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY );
596 1165 delete_post_meta( $chart_id, Visualizer_Plugin::CF_DB_SCHEDULE );
1166 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_REMOTE_DB_PARAMS );
597 1167 }
598 1168
1169 + // delete json related data.
1170 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_URL );
1171 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_ROOT );
1172 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_PAGING );
1173 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_HEADERS );
1174 +
1175 + // delete last error
1176 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR );
1177 +
1178 + // delete editor related data.
1179 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR );
1180 +
1181 + // delete this so that a JSON import can be later edited manually without a problem.
1182 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE );
1183 +
599 1184 $source = null;
600 1185 $render = new Visualizer_Render_Page_Update();
601 - if ( isset( $_POST['remote_data'] ) && filter_var( $_POST['remote_data'], FILTER_VALIDATE_URL ) ) {
602 - $source = new Visualizer_Source_Csv_Remote( $_POST['remote_data'] );
1186 +
1187 + $remote_data = false;
1188 + if ( isset( $_POST['remote_data'] ) && function_exists( 'wp_http_validate_url' ) ) {
1189 + $remote_data = wp_http_validate_url( $_POST['remote_data'] );
1190 + }
1191 + if ( false !== $remote_data ) {
1192 + $source = new Visualizer_Source_Csv_Remote( $remote_data );
603 1193 if ( isset( $_POST['vz-import-time'] ) ) {
604 - apply_filters( 'visualizer_pro_chart_schedule', $chart_id, $_POST['remote_data'], $_POST['vz-import-time'] );
1194 + apply_filters( 'visualizer_pro_chart_schedule', $chart_id, $remote_data, $_POST['vz-import-time'] );
605 1195 }
1196 + // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
606 1197 } elseif ( isset( $_FILES['local_data'] ) && $_FILES['local_data']['error'] == 0 ) {
607 1198 $source = new Visualizer_Source_Csv( $_FILES['local_data']['tmp_name'] );
608 1199 } elseif ( isset( $_POST['chart_data'] ) && strlen( $_POST['chart_data'] ) > 0 ) {
609 - $source = apply_filters( 'visualizer_pro_handle_chart_data', $_POST['chart_data'], '' );
1200 + $source = $this->handleCSVasString( $_POST['chart_data'], $_POST['editor-type'] );
1201 + update_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR, $_POST['editor-type'] );
1202 + } elseif ( isset( $_POST['table_data'] ) && 'yes' === $_POST['table_data'] ) {
1203 + $source = $this->handleTabularData();
1204 + update_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR, $_POST['editor-type'] );
610 1205 } else {
611 - $render->message = esc_html__( 'CSV file with chart data was not uploaded. Please, try again.', 'visualizer' );
1206 + 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__ );
1207 + $render->message = esc_html__( 'CSV file with chart data was not uploaded. Please try again.', 'visualizer' );
1208 + update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, esc_html__( 'CSV file with chart data was not uploaded. Please try again.', 'visualizer' ) );
612 1209 }
1210 +
1211 + 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__ );
1212 +
613 1213 if ( $source ) {
614 1214 if ( $source->fetch() ) {
615 - $content = $source->getData();
1215 + $content = $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) );
616 1216 $populate = true;
617 1217 if ( is_string( $content ) && is_array( unserialize( $content ) ) ) {
618 1218 $json = unserialize( $content );
619 1219 // if source exists, so should data. if source exists but data is blank, do not populate the chart.
@@ -619,8 +1219,9 @@
619 1219 // if source exists, so should data. if source exists but data is blank, do not populate the chart.
620 1220 // if we populate the data even if it is empty, the chart will show "Table has no columns".
621 1221 if ( array_key_exists( 'source', $json ) && ! empty( $json['source'] ) && ( ! array_key_exists( 'data', $json ) || empty( $json['data'] ) ) ) {
622 1222 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__ );
1223 + update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, sprintf( 'Not populating chart data as source exists (%s) but data is empty!', $json['source'] ) );
623 1224 $populate = false;
624 1225 }
625 1226 }
626 1227 if ( $populate ) {
@@ -626,17 +1227,43 @@
626 1227 if ( $populate ) {
627 1228 $chart->post_content = $content;
628 1229 }
629 1230 wp_update_post( $chart->to_array() );
1231 +
630 1232 update_post_meta( $chart->ID, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
631 1233 update_post_meta( $chart->ID, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
632 1234 update_post_meta( $chart->ID, Visualizer_Plugin::CF_DEFAULT_DATA, 0 );
1235 +
1236 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Updated post for chart %d', $chart_id ), 'debug', __FILE__, __LINE__ );
1237 +
1238 + Visualizer_Module_Utility::set_defaults( $chart, null );
1239 +
1240 + $settings = get_post_meta( $chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
1241 + if ( isset( $settings['series'] ) && ! ( count( $settings['series'] ) - count( $source->getSeries() ) > 1 ) ) {
1242 + $diff_total_series = abs( count( $settings['series'] ) - count( $source->getSeries() ) );
1243 + if ( $diff_total_series ) {
1244 + foreach ( range( 1, $diff_total_series ) as $k => $diff_series ) {
1245 + $settings['series'][] = end( $settings['series'] );
1246 + }
1247 + update_post_meta( $chart->ID, Visualizer_Plugin::CF_SETTINGS, $settings );
1248 + }
1249 + }
1250 +
633 1251 $render->id = $chart->ID;
634 - $render->data = json_encode( $source->getRawData() );
1252 + $render->data = json_encode( $source->getRawData( get_post_meta( $chart->ID, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ) );
635 1253 $render->series = json_encode( $source->getSeries() );
1254 + $render->settings = json_encode( $settings );
636 1255 } else {
637 - $render->message = esc_html__( 'CSV file is broken or invalid. Please, try again.', 'visualizer' );
1256 + $error = $source->get_error();
1257 + if ( empty( $error ) ) {
1258 + $error = esc_html__( 'CSV file is broken or invalid. Please try again.', 'visualizer' );
1259 + }
1260 + $render->message = $error;
1261 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( '%s for chart %d.', $error, $chart_id ), 'error', __FILE__, __LINE__ );
1262 + update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, $error );
638 1263 }
1264 + } else {
1265 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Unknown internal error for chart %d.', $chart_id ), 'error', __FILE__, __LINE__ );
639 1266 }
640 1267 $render->render();
641 1268 if ( ! $can_die ) {
642 1269 return;
@@ -652,27 +1279,18 @@
652 1279 * @access public
653 1280 */
654 1281 public function cloneChart() {
655 1282 $chart_id = $success = false;
656 - $nonce = wp_verify_nonce( filter_input( INPUT_GET, 'nonce' ), Visualizer_Plugin::ACTION_CLONE_CHART );
1283 + $nonce = isset( $_GET['nonce'] ) && wp_verify_nonce( $_GET['nonce'], Visualizer_Plugin::ACTION_CLONE_CHART );
657 1284 $capable = current_user_can( 'edit_posts' );
658 1285 if ( $nonce && $capable ) {
659 - $chart_id = filter_input(
660 - INPUT_GET,
661 - 'chart',
662 - FILTER_VALIDATE_INT,
663 - array(
664 - 'options' => array(
665 - 'min_range' => 1,
666 - ),
667 - )
668 - );
1286 + $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
669 1287 if ( $chart_id ) {
670 1288 $chart = get_post( $chart_id );
671 - $success = $chart && $chart->post_type == Visualizer_Plugin::CPT_VISUALIZER;
1289 + $success = $chart && $chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
672 1290 }
673 1291 }
674 - $redirect = wp_get_referer();
1292 + $redirect = remove_query_arg( 'vaction', wp_get_referer() );
675 1293 if ( $success ) {
676 1294 $new_chart_id = wp_insert_post(
677 1295 array(
678 1296 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
@@ -681,23 +1299,35 @@
681 1299 'post_status' => $chart->post_status,
682 1300 'post_content' => $chart->post_content,
683 1301 )
684 1302 );
685 - if ( $new_chart_id && ! is_wp_error( $new_chart_id ) ) {
686 - add_post_meta( $new_chart_id, Visualizer_Plugin::CF_CHART_TYPE, get_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_TYPE, true ) );
687 - add_post_meta( $new_chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, get_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, true ) );
688 - add_post_meta( $new_chart_id, Visualizer_Plugin::CF_SOURCE, get_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, true ) );
689 - add_post_meta( $new_chart_id, Visualizer_Plugin::CF_SERIES, get_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, true ) );
690 - add_post_meta( $new_chart_id, Visualizer_Plugin::CF_SETTINGS, get_post_meta( $chart_id, Visualizer_Plugin::CF_SETTINGS, true ) );
691 - $redirect = add_query_arg(
692 - array(
693 - 'page' => 'visualizer',
694 - 'type' => filter_input( INPUT_GET, 'type' ),
1303 + if ( is_wp_error( $new_chart_id ) ) {
1304 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Error while cloning chart %d = %s', $chart_id, print_r( $new_chart_id, true ) ), 'error', __FILE__, __LINE__ );
1305 + } else {
1306 + $post_meta = get_post_meta( $chart_id );
1307 + foreach ( $post_meta as $key => $value ) {
1308 + if ( strpos( $key, 'visualizer-' ) !== false ) {
1309 + add_post_meta( $new_chart_id, $key, maybe_unserialize( $value[0] ) );
1310 + }
1311 + }
1312 + $redirect = esc_url(
1313 + add_query_arg(
1314 + array(
1315 + 'page' => 'visualizer',
1316 + 'type' => filter_input( INPUT_GET, 'type' ),
1317 + 'vaction' => false,
1318 + ),
1319 + admin_url( 'admin.php' )
695 1320 ),
696 - admin_url( 'upload.php' )
1321 + null,
1322 + 'db'
697 1323 );
698 1324 }
699 1325 }
1326 +
1327 + if ( defined( 'WP_TESTS_DOMAIN' ) ) {
1328 + wp_die();
1329 + }
700 1330 wp_redirect( $redirect );
701 1331 exit;
702 1332 }
703 1333
@@ -743,9 +1373,12 @@
743 1373 $data = $this->_getChartArray();
744 1374 $render = new Visualizer_Render_Page_Data();
745 1375 $render->chart = $this->_chart;
746 1376 $render->type = $data['type'];
747 - unset( $data['settings']['width'], $data['settings']['height'] );
1377 +
1378 + if ( $data && $data['settings'] ) {
1379 + unset( $data['settings']['width'], $data['settings']['height'], $data['settings']['chartArea'] );
1380 + }
748 1381 wp_enqueue_style( 'visualizer-frame' );
749 1382 wp_enqueue_script( 'visualizer-render' );
750 1383 wp_localize_script(
751 1384 'visualizer-render',
@@ -751,9 +1384,9 @@
751 1384 'visualizer-render',
752 1385 'visualizer',
753 1386 array(
754 1387 'l10n' => array(
755 - 'invalid_source' => esc_html__( 'You have entered invalid URL. Please, insert proper URL.', 'visualizer' ),
1388 + 'invalid_source' => esc_html__( 'You have entered an invalid URL. Please provide a valid URL.', 'visualizer' ),
756 1389 'loading' => esc_html__( 'Loading...', 'visualizer' ),
757 1390 ),
758 1391 'charts' => array(
759 1392 'canvas' => $data,
@@ -759,13 +1392,16 @@
759 1392 'canvas' => $data,
760 1393 ),
761 1394 )
762 1395 );
763 - // Added by Ash/Upwork
764 - if ( VISUALIZER_PRO ) {
1396 +
1397 + do_action( 'visualizer_enqueue_scripts_and_styles', $data, $this->_chart->ID );
1398 +
1399 + if ( Visualizer_Module::is_pro() && Visualizer_Module::is_pro_older_than( '1.9.0' ) ) {
765 1400 global $Visualizer_Pro;
766 - $Visualizer_Pro->_enqueueScriptsAndStyles( $data );
1401 + $Visualizer_Pro->_enqueueScriptsAndStyles( $data, $this->_chart->ID );
767 1402 }
1403 +
768 1404 // Added by Ash/Upwork
769 1405 $this->_addAction( 'admin_head', 'renderFlattrScript' );
770 1406 wp_iframe( array( $render, 'render' ) );
771 1407 }
@@ -778,13 +1414,14 @@
778 1414 public function getQueryData() {
779 1415 check_ajax_referer( Visualizer_Plugin::ACTION_FETCH_DB_DATA . Visualizer_Plugin::VERSION, 'security' );
780 1416
781 1417 $params = wp_parse_args( $_POST['params'] );
782 - $source = new Visualizer_Source_Query( stripslashes( $params['query'] ) );
1418 + $chart_id = filter_var( $params['chart_id'], FILTER_VALIDATE_INT );
1419 +
1420 + $source = new Visualizer_Source_Query( stripslashes( $params['query'] ), $chart_id, $params );
783 1421 $html = $source->fetch( true );
784 - $error = '';
785 - if ( empty( $html ) ) {
786 - $error = $source->get_error();
1422 + $error = $source->get_error();
1423 + if ( ! empty( $error ) ) {
787 1424 wp_send_json_error( array( 'msg' => $error ) );
788 1425 }
789 1426 wp_send_json_success( array( 'table' => $html ) );
790 1427 }
@@ -807,21 +1444,42 @@
807 1444 ),
808 1445 )
809 1446 );
810 1447
1448 + $hours = filter_input(
1449 + INPUT_POST,
1450 + 'refresh',
1451 + FILTER_VALIDATE_FLOAT,
1452 + array(
1453 + 'options' => array(
1454 + 'min_range' => -1,
1455 + 'max_range' => apply_filters( 'visualizer_is_business', false ) ? PHP_INT_MAX : -1,
1456 + ),
1457 + )
1458 + );
1459 +
1460 + if ( ! is_numeric( $hours ) ) {
1461 + $hours = -1;
1462 + }
1463 +
811 1464 $render = new Visualizer_Render_Page_Update();
812 1465 if ( $chart_id ) {
813 1466 $params = wp_parse_args( $_POST['params'] );
814 - $source = new Visualizer_Source_Query( stripslashes( $params['query'] ) );
1467 + $source = new Visualizer_Source_Query( stripslashes( $params['query'] ), $chart_id, $params );
815 1468 $source->fetch( false );
816 1469 $error = $source->get_error();
817 1470 if ( empty( $error ) ) {
818 - $hours = $_POST['refresh'];
819 1471 update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY, stripslashes( $params['query'] ) );
820 1472 update_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
821 1473 update_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
822 1474 update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_SCHEDULE, $hours );
823 1475 update_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, 0 );
1476 + if ( isset( $params['db_type'] ) && $params['db_type'] !== Visualizer_Plugin::WP_DB_NAME ) {
1477 + $remote_db_params = $params;
1478 + unset( $remote_db_params['query'] );
1479 + unset( $remote_db_params['chart_id'] );
1480 + update_post_meta( $chart_id, Visualizer_Plugin::CF_REMOTE_DB_PARAMS, $remote_db_params );
1481 + }
824 1482
825 1483 $schedules = get_option( Visualizer_Plugin::CF_DB_SCHEDULE, array() );
826 1484 $schedules[ $chart_id ] = time() + $hours * HOUR_IN_SECONDS;
827 1485 update_option( Visualizer_Plugin::CF_DB_SCHEDULE, $schedules );
@@ -828,13 +1486,14 @@
828 1486
829 1487 wp_update_post(
830 1488 array(
831 1489 'ID' => $chart_id,
832 - 'post_content' => $source->getData(),
1490 + 'post_content' => $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ),
833 1491 )
834 1492 );
835 - $render->data = json_encode( $source->getRawData() );
1493 + $render->data = json_encode( $source->getRawData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ) );
836 1494 $render->series = json_encode( $source->getSeries() );
1495 + $render->id = $chart_id;
837 1496 } else {
838 1497 $render->message = $error;
839 1498 }
840 1499 }
@@ -841,6 +1500,98 @@
841 1500 $render->render();
842 1501 if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
843 1502 defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
844 1503 }
1504 + }
1505 +
1506 +
1507 + /**
1508 + * Saves the filter query and the schedule.
1509 + *
1510 + * @access public
1511 + */
1512 + public function saveFilter() {
1513 + check_ajax_referer( Visualizer_Plugin::ACTION_SAVE_FILTER_QUERY . Visualizer_Plugin::VERSION, 'security' );
1514 +
1515 + $chart_id = filter_input(
1516 + INPUT_GET,
1517 + 'chart',
1518 + FILTER_VALIDATE_INT,
1519 + array(
1520 + 'options' => array(
1521 + 'min_range' => 1,
1522 + ),
1523 + )
1524 + );
1525 +
1526 + $hours = filter_input(
1527 + INPUT_POST,
1528 + 'refresh',
1529 + FILTER_VALIDATE_INT,
1530 + array(
1531 + 'options' => array(
1532 + 'min_range' => -1,
1533 + 'max_range' => apply_filters( 'visualizer_is_business', false ) ? PHP_INT_MAX : -1,
1534 + ),
1535 + )
1536 + );
1537 +
1538 + if ( 0 !== $hours && empty( $hours ) ) {
1539 + $hours = -1;
1540 + }
1541 +
1542 + do_action( 'visualizer_save_filter', $chart_id, $hours );
1543 +
1544 + if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
1545 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1546 + }
1547 + }
1548 +
1549 + /**
1550 + * Save chart image.
1551 + *
1552 + * @param string $base64_img Chart image.
1553 + * @param int $chart_id Chart ID.
1554 + * @param bool $save_attachment Save attachment.
1555 + * @return attachment ID
1556 + */
1557 + public function save_chart_image( $base64_img, $chart_id, $save_attachment = true ) {
1558 + // Delete old chart image.
1559 + $old_attachment_id = get_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_IMAGE, true );
1560 + if ( $old_attachment_id ) {
1561 + wp_delete_attachment( $old_attachment_id, true );
1562 + }
1563 +
1564 + if ( ! $save_attachment ) {
1565 + return 0;
1566 + }
1567 +
1568 + // Upload dir.
1569 + $upload_dir = wp_upload_dir();
1570 + $upload_path = str_replace( '/', DIRECTORY_SEPARATOR, $upload_dir['path'] ) . DIRECTORY_SEPARATOR;
1571 +
1572 + $img = str_replace( 'data:image/png;base64,', '', $base64_img );
1573 + $img = str_replace( ' ', '+', $img );
1574 + $decoded = base64_decode( $img );
1575 + $filename = 'visualization-' . $chart_id . '.png';
1576 + $file_type = 'image/png';
1577 + $hashed_filename = $filename;
1578 +
1579 + // Save the image in the uploads directory.
1580 + require_once ABSPATH . '/wp-admin/includes/file.php';
1581 + \WP_Filesystem();
1582 + global $wp_filesystem;
1583 + $upload_file = $wp_filesystem->put_contents( $upload_path . $hashed_filename, $decoded );
1584 +
1585 + // Insert new chart image.
1586 + $attachment = array(
1587 + 'post_mime_type' => $file_type,
1588 + 'post_title' => preg_replace( '/\.[^.]+$/', '', basename( $hashed_filename ) ),
1589 + 'post_content' => '',
1590 + 'post_status' => 'inherit',
1591 + 'guid' => $upload_dir['url'] . '/' . basename( $hashed_filename ),
1592 + );
1593 +
1594 + $attach_id = wp_insert_attachment( $attachment, $upload_dir['path'] . '/' . $hashed_filename );
1595 + return $attach_id;
845 1596 }
846 1597 }