PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.10.8
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.10.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 3.10.4 All 148 releases
← All changes | classes/Visualizer/Module/Chart.php +876 -114 3.1.03.10.8 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,119 @@
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 ( ! empty( $_POST ) ) {
536 + $_POST = map_deep( $_POST, 'wp_strip_all_tags' );
537 + }
538 + if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ) {
539 + if ( empty( $_GET['lang'] ) || empty( $_GET['parent_chart_id'] ) ) {
540 + $this->deleteOldCharts();
541 + $default_type = isset( $_GET['type'] ) && ! empty( $_GET['type'] ) ? $_GET['type'] : 'line';
542 + $chart_status = Visualizer_Module_Admin::checkChartStatus( $default_type );
543 + if ( ! $chart_status ) {
544 + $default_type = 'line';
545 + }
546 + $source = new Visualizer_Source_Csv( VISUALIZER_ABSPATH . DIRECTORY_SEPARATOR . 'samples' . DIRECTORY_SEPARATOR . $default_type . '.csv' );
547 + $source->fetch();
548 + $chart_id = wp_insert_post(
269 549 array(
270 - 'focusTarget' => 'datum',
550 + 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
551 + 'post_title' => 'Visualization',
552 + 'post_author' => get_current_user_id(),
553 + 'post_status' => 'auto-draft',
554 + 'post_content' => $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ),
271 555 )
272 556 );
557 + if ( $chart_id && ! is_wp_error( $chart_id ) ) {
558 + add_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_TYPE, $default_type );
559 + add_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, 1 );
560 + add_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
561 + add_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
562 + add_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_LIBRARY, '' );
563 + add_post_meta(
564 + $chart_id,
565 + Visualizer_Plugin::CF_SETTINGS,
566 + array(
567 + 'focusTarget' => 'datum',
568 + )
569 + );
570 +
571 + do_action( 'visualizer_pro_new_chart_defaults', $chart_id );
572 + }
573 + } else {
574 + if ( current_user_can( 'edit_posts' ) ) {
575 + $parent_chart_id = isset( $_GET['parent_chart_id'] ) ? filter_var( $_GET['parent_chart_id'], FILTER_VALIDATE_INT ) : '';
576 + $success = false;
577 + if ( $parent_chart_id ) {
578 + $parent_chart = get_post( $parent_chart_id );
579 + $success = $parent_chart && $parent_chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
580 + }
581 + if ( $success ) {
582 + $new_chart_id = wp_insert_post(
583 + array(
584 + 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
585 + 'post_title' => 'Visualization',
586 + 'post_author' => get_current_user_id(),
587 + 'post_status' => $parent_chart->post_status,
588 + 'post_content' => $parent_chart->post_content,
589 + )
590 + );
591 +
592 + if ( is_wp_error( $new_chart_id ) ) {
593 + 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__ );
594 + } else {
595 + $post_meta = get_post_meta( $parent_chart_id );
596 + $chart_id = $new_chart_id;
597 + foreach ( $post_meta as $key => $value ) {
598 + if ( strpos( $key, 'visualizer-' ) !== false ) {
599 + add_post_meta( $new_chart_id, $key, maybe_unserialize( $value[0] ) );
600 + }
601 + }
602 + }
603 + }
604 + }
273 605 do_action( 'visualizer_pro_new_chart_defaults', $chart_id );
274 606 }
275 - wp_redirect( add_query_arg( 'chart', (int) $chart_id ) );
276 - defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
607 + wp_redirect( esc_url_raw( add_query_arg( 'chart', (int) $chart_id ) ) );
608 +
609 + if ( defined( 'WP_TESTS_DOMAIN' ) ) {
610 + wp_die();
611 + }
612 + exit();
277 613 }
278 614
279 - $this->load_chart_type( $chart_id );
615 + $_POST['save_chart_image'] = isset( $_POST['save_chart_image'] ) && 'yes' === $_POST['save_chart_image'] ? true : false;
616 + $_POST['lazy_load_chart'] = isset( $_POST['lazy_load_chart'] ) && 'yes' === $_POST['lazy_load_chart'] ? true : false;
280 617
618 + if ( isset( $_POST['chart-img'] ) && ! empty( $_POST['chart-img'] ) ) {
619 + $attachment_id = $this->save_chart_image( $_POST['chart-img'], $chart_id, $_POST['save_chart_image'] );
620 + if ( $attachment_id ) {
621 + update_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_IMAGE, $attachment_id );
622 + }
623 + }
624 + $lib = $this->load_chart_type( $chart_id );
625 +
626 + // the alpha color picker (RGBA) is not supported by google.
627 + $color_picker_dep = 'wp-color-picker';
628 + if ( in_array( $lib, array( 'chartjs', 'datatables' ), true ) && ! wp_script_is( 'wp-color-picker-alpha', 'registered' ) ) {
629 + wp_register_script( 'wp-color-picker-alpha', VISUALIZER_ABSURL . 'js/lib/wp-color-picker-alpha.min.js', array( 'wp-color-picker' ), Visualizer_Plugin::VERSION );
630 + $color_picker_dep = 'wp-color-picker-alpha';
631 + }
632 +
281 633 // enqueue and register scripts and styles
282 634 wp_register_script( 'visualizer-chosen', VISUALIZER_ABSURL . 'js/lib/chosen.jquery.min.js', array( 'jquery' ), Visualizer_Plugin::VERSION );
283 635 wp_register_style( 'visualizer-chosen', VISUALIZER_ABSURL . 'css/lib/chosen.min.css', array(), Visualizer_Plugin::VERSION );
284 636
285 637 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 );
638 + wp_register_script( 'visualizer-frame', VISUALIZER_ABSURL . 'js/frame.js', array( 'visualizer-chosen', 'jquery-ui-accordion', 'jquery-ui-tabs' ), Visualizer_Plugin::VERSION, true );
287 639 wp_register_script( 'visualizer-customization', $this->get_user_customization_js(), array(), null, true );
288 640 wp_register_script(
289 641 'visualizer-render',
290 642 VISUALIZER_ABSURL . 'js/render-facade.js',
@@ -295,19 +647,23 @@
295 647 wp_register_script(
296 648 'visualizer-preview',
297 649 VISUALIZER_ABSURL . 'js/preview.js',
298 650 array(
299 - 'wp-color-picker',
651 + $color_picker_dep,
300 652 'visualizer-render',
301 653 ),
302 654 Visualizer_Plugin::VERSION,
303 655 true
304 656 );
305 - // added by Ash/Upwork
306 - if ( VISUALIZER_PRO ) {
657 + wp_register_script( 'visualizer-editor-simple', VISUALIZER_ABSURL . 'js/simple-editor.js', array( 'jquery' ), Visualizer_Plugin::VERSION, true );
658 +
659 + do_action( 'visualizer_add_scripts' );
660 +
661 + if ( Visualizer_Module::is_pro() && Visualizer_Module::is_pro_older_than( '1.9.0' ) ) {
307 662 global $Visualizer_Pro;
308 663 $Visualizer_Pro->_addScriptsAndStyles();
309 664 }
665 +
310 666 // dispatch pages
311 667 $this->_chart = get_post( $chart_id );
312 668 $tab = isset( $_GET['tab'] ) && ! empty( $_GET['tab'] ) ? $_GET['tab'] : 'visualizer';
313 669
@@ -319,8 +675,9 @@
319 675 if ( isset( $_POST['cancel'] ) && 1 === intval( $_POST['cancel'] ) ) {
320 676 // if the cancel button is clicked.
321 677 $this->undoRevisions( $chart_id, true );
322 678 } elseif ( isset( $_POST['save'] ) && 1 === intval( $_POST['save'] ) ) {
679 + $this->handlePermissions();
323 680 // if the save button is clicked.
324 681 $this->undoRevisions( $chart_id, false );
325 682 } else {
326 683 // if the edit button is clicked.
@@ -326,8 +683,16 @@
326 683 // if the edit button is clicked.
327 684 $this->_chart = $this->handleExistingRevisions( $chart_id, $this->_chart );
328 685 }
329 686
687 + // Clear existing chart cache.
688 + if ( isset( $_POST['save'] ) && 1 === intval( $_POST['save'] ) ) {
689 + $cache_key = Visualizer_Plugin::CF_CHART_CACHE . '_' . $chart_id;
690 + if ( get_transient( $cache_key ) ) {
691 + delete_transient( $cache_key );
692 + }
693 + }
694 +
330 695 switch ( $tab ) {
331 696 case 'settings':
332 697 $this->_handleDataAndSettingsPage();
333 698 break;
@@ -335,9 +700,9 @@
335 700 case 'visualizer': // fall through.
336 701 $this->_handleTypesPage();
337 702 break;
338 703 default:
339 - do_action( 'visualizer_pro_handle_tab', $tab, $this->_chart );
704 + // this should never happen.
340 705 break;
341 706 }
342 707 defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
343 708 }
@@ -344,25 +709,26 @@
344 709
345 710 /**
346 711 * Load code editor assets.
347 712 */
348 - private function loadCodeEditorAssets() {
713 + private function loadCodeEditorAssets( $chart_id ) {
349 714 global $wp_version;
350 715
351 - if ( ! VISUALIZER_PRO ) {
716 + $wp_scripts = wp_scripts();
717 +
718 + // data tables assets.
719 + wp_register_script( 'visualizer-datatables', VISUALIZER_ABSURL . 'js/lib/datatables.min.js', array( 'jquery-ui-core' ), Visualizer_Plugin::VERSION );
720 + wp_register_style( 'visualizer-datatables', VISUALIZER_ABSURL . 'css/lib/datatables.min.css', array(), Visualizer_Plugin::VERSION );
721 + 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 );
722 + wp_enqueue_script( 'visualizer-datatables' );
723 + wp_enqueue_style( 'visualizer-jquery-ui' );
724 +
725 + if ( ! Visualizer_Module::is_pro() ) {
352 726 return;
353 727 }
354 728
355 - $table_col_mapping = Visualizer_Source_Query_Params::get_all_db_tables_column_mapping();
729 + $table_col_mapping = Visualizer_Source_Query_Params::get_all_db_tables_column_mapping( $chart_id );
356 730
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 731 if ( version_compare( $wp_version, '4.9.0', '<' ) ) {
366 732 // code mirror assets.
367 733 wp_register_script( 'visualizer-codemirror-core', '//codemirror.net/lib/codemirror.js', array( 'jquery' ), Visualizer_Plugin::VERSION );
368 734 wp_register_script( 'visualizer-codemirror-placeholder', '//codemirror.net/addon/display/placeholder.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
@@ -396,8 +762,25 @@
396 762 return $table_col_mapping;
397 763 }
398 764
399 765 /**
766 + * Handle permissions from the new conslidated settings sidebar.
767 + */
768 + private function handlePermissions() {
769 + if ( ! Visualizer_Module::is_pro() ) {
770 + return;
771 + }
772 +
773 + // we will not support old free and new pro.
774 + // handling new free and old pro.
775 + if ( has_action( 'visualizer_pro_handle_tab' ) ) {
776 + do_action( 'visualizer_pro_handle_tab', 'permissions', $this->_chart );
777 + } else {
778 + do_action( 'visualizer_handle_permissions', $this->_chart );
779 + }
780 + }
781 +
782 + /**
400 783 * Handle data and settings page
401 784 */
402 785 private function _handleDataAndSettingsPage() {
403 786 if ( isset( $_POST['map_api_key'] ) ) {
@@ -402,30 +785,53 @@
402 785 private function _handleDataAndSettingsPage() {
403 786 if ( isset( $_POST['map_api_key'] ) ) {
404 787 update_option( 'visualizer-map-api-key', $_POST['map_api_key'] );
405 788 }
406 - if ( $_SERVER['REQUEST_METHOD'] == 'POST' && isset( $_GET['nonce'] ) && wp_verify_nonce( $_GET['nonce'] ) ) {
407 - if ( $this->_chart->post_status == 'auto-draft' ) {
789 +
790 + if ( $_SERVER['REQUEST_METHOD'] === 'POST' && isset( $_GET['nonce'] ) && wp_verify_nonce( $_GET['nonce'] ) ) {
791 + if ( $this->_chart->post_status === 'auto-draft' ) {
408 792 $this->_chart->post_status = 'publish';
409 793
410 794 // 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 795 // we do not want any difference in data so disable revisions temporarily.
412 - add_filter( 'wp_revisions_to_keep', '__return_false' );
796 + $this->disableRevisionsTemporarily();
797 +
413 798 wp_update_post( $this->_chart->to_array() );
414 799 }
415 800 // save meta data only when it is NOT being canceled.
416 801 if ( ! ( isset( $_POST['cancel'] ) && 1 === intval( $_POST['cancel'] ) ) ) {
417 802 update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, $_POST );
803 +
804 + // we will keep a parameter called 'internal_title' that will be set to the given title or, if empty, the chart ID
805 + // this will help in searching with the chart id.
806 + $settings = get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
807 + $title = null;
808 + if ( isset( $settings['title'] ) && ! empty( $settings['title'] ) ) {
809 + $title = $settings['title'];
810 + if ( is_array( $title ) ) {
811 + $title = $settings['title']['text'];
812 + }
813 + }
814 + if ( empty( $title ) ) {
815 + $title = $this->_chart->ID;
816 + }
817 + $settings['internal_title'] = $title;
818 + $settings_label = isset( $settings['pieResidueSliceLabel'] ) ? $settings['pieResidueSliceLabel'] : '';
819 + if ( empty( $settings_label ) ) {
820 + $settings['pieResidueSliceLabel'] = esc_html__( 'Other', 'visualizer' );
821 + } else {
822 + $settings['pieResidueSliceLabel'] = $settings_label;
823 + }
824 + update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, $settings );
418 825 }
419 826 $render = new Visualizer_Render_Page_Send();
420 827 $render->text = sprintf( '[visualizer id="%d"]', $this->_chart->ID );
421 828 wp_iframe( array( $render, 'render' ) );
422 -
423 829 return;
424 830 }
425 831 $data = $this->_getChartArray();
426 832 $sidebar = '';
427 - $sidebar_class = 'Visualizer_Render_Sidebar_Type_' . ucfirst( $data['type'] );
833 + $sidebar_class = $this->load_chart_class_name( $this->_chart->ID );
428 834 if ( class_exists( $sidebar_class, true ) ) {
429 835 $sidebar = new $sidebar_class( $data['settings'] );
430 836 $sidebar->__series = $data['series'];
431 837 $sidebar->__data = $data['data'];
@@ -430,14 +836,14 @@
430 836 $sidebar->__series = $data['series'];
431 837 $sidebar->__data = $data['data'];
432 838 } else {
433 839 $sidebar = apply_filters( 'visualizer_pro_chart_type_sidebar', '', $data );
434 - if ( $sidebar != '' ) {
840 + if ( $sidebar !== '' ) {
435 841 $sidebar->__series = $data['series'];
436 842 $sidebar->__data = $data['data'];
437 843 }
438 844 }
439 - unset( $data['settings']['width'], $data['settings']['height'] );
845 + unset( $data['settings']['width'], $data['settings']['height'], $data['settings']['chartArea'] );
440 846 wp_enqueue_style( 'wp-color-picker' );
441 847 wp_enqueue_style( 'visualizer-frame' );
442 848 wp_enqueue_script( 'visualizer-preview' );
443 849 wp_enqueue_script( 'visualizer-chosen' );
@@ -442,17 +848,37 @@
442 848 wp_enqueue_script( 'visualizer-preview' );
443 849 wp_enqueue_script( 'visualizer-chosen' );
444 850 wp_enqueue_script( 'visualizer-render' );
445 851
446 - $table_col_mapping = $this->loadCodeEditorAssets();
852 + if ( Visualizer_Module::can_show_feature( 'simple-editor' ) ) {
853 + wp_enqueue_script( 'visualizer-editor-simple' );
854 + wp_localize_script(
855 + 'visualizer-editor-simple',
856 + 'visualizer1',
857 + array(
858 + 'ajax' => array(
859 + 'url' => admin_url( 'admin-ajax.php' ),
860 + 'nonces' => array(
861 + ),
862 + 'actions' => array(
863 + ),
864 + ),
865 + )
866 + );
867 + }
447 868
869 + $table_col_mapping = $this->loadCodeEditorAssets( $this->_chart->ID );
870 +
448 871 wp_localize_script(
449 872 'visualizer-render',
450 873 'visualizer',
451 874 array(
452 875 'l10n' => array(
453 - 'invalid_source' => esc_html__( 'You have entered invalid URL. Please, insert proper URL.', 'visualizer' ),
876 + 'invalid_source' => esc_html__( 'You have entered an invalid URL. Please provide a valid URL.', 'visualizer' ),
454 877 'loading' => esc_html__( 'Loading...', 'visualizer' ),
878 + 'json_error' => esc_html__( 'An error occured in fetching data.', 'visualizer' ),
879 + 'select_columns' => esc_html__( 'Please select a few columns to include in the chart.', 'visualizer' ),
880 + '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 881 ),
456 882 'charts' => array(
457 883 'canvas' => $data,
458 884 'id' => $this->_chart->ID,
@@ -463,19 +889,29 @@
463 889 'url' => admin_url( 'admin-ajax.php' ),
464 890 'nonces' => array(
465 891 'permissions' => wp_create_nonce( Visualizer_Plugin::ACTION_FETCH_PERMISSIONS_DATA ),
466 892 'db_get_data' => wp_create_nonce( Visualizer_Plugin::ACTION_FETCH_DB_DATA . Visualizer_Plugin::VERSION ),
893 + 'json_get_roots' => wp_create_nonce( Visualizer_Plugin::ACTION_JSON_GET_ROOTS . Visualizer_Plugin::VERSION ),
894 + 'json_get_data' => wp_create_nonce( Visualizer_Plugin::ACTION_JSON_GET_DATA . Visualizer_Plugin::VERSION ),
895 + 'json_set_schedule' => wp_create_nonce( Visualizer_Plugin::ACTION_JSON_SET_SCHEDULE . Visualizer_Plugin::VERSION ),
467 896 ),
468 897 'actions' => array(
469 898 'permissions' => Visualizer_Plugin::ACTION_FETCH_PERMISSIONS_DATA,
470 899 'db_get_data' => Visualizer_Plugin::ACTION_FETCH_DB_DATA,
900 + 'json_get_roots' => Visualizer_Plugin::ACTION_JSON_GET_ROOTS,
901 + 'json_get_data' => Visualizer_Plugin::ACTION_JSON_GET_DATA,
902 + 'json_set_schedule' => Visualizer_Plugin::ACTION_JSON_SET_SCHEDULE,
471 903 ),
472 904 ),
473 905 'db_query' => array(
474 906 'tables' => $table_col_mapping,
475 907 ),
476 - 'is_pro' => VISUALIZER_PRO,
908 + 'is_pro' => Visualizer_Module::is_pro(),
477 909 'page_type' => 'chart',
910 + 'json_tag_separator' => Visualizer_Source_Json::TAG_SEPARATOR,
911 + 'json_tag_separator_view' => Visualizer_Source_Json::TAG_SEPARATOR_VIEW,
912 + 'is_front' => false,
913 + 'rest_base' => get_rest_url( null, 'wc/v3/reports/' ),
478 914 )
479 915 );
480 916
481 917 $render = new Visualizer_Render_Page_Data();
@@ -483,21 +919,25 @@
483 919 $render->type = $data['type'];
484 920 $render->custom_css = $data['css'];
485 921 $render->sidebar = $sidebar;
486 922 if ( filter_input( INPUT_GET, 'library', FILTER_VALIDATE_BOOLEAN ) ) {
487 - $render->button = filter_input( INPUT_GET, 'action' ) == Visualizer_Plugin::ACTION_EDIT_CHART
923 + $render->button = filter_input( INPUT_GET, 'action' ) === Visualizer_Plugin::ACTION_EDIT_CHART
488 924 ? esc_html__( 'Save Chart', 'visualizer' )
489 925 : esc_html__( 'Create Chart', 'visualizer' );
490 - if ( filter_input( INPUT_GET, 'action' ) == Visualizer_Plugin::ACTION_EDIT_CHART ) {
926 + if ( filter_input( INPUT_GET, 'action' ) === Visualizer_Plugin::ACTION_EDIT_CHART ) {
491 927 $render->cancel_button = esc_html__( 'Cancel', 'visualizer' );
492 928 }
493 929 } else {
494 930 $render->button = esc_attr__( 'Insert Chart', 'visualizer' );
495 931 }
496 - if ( VISUALIZER_PRO ) {
932 +
933 + do_action( 'visualizer_enqueue_scripts_and_styles', $data, $this->_chart->ID );
934 +
935 + if ( Visualizer_Module::is_pro() && Visualizer_Module::is_pro_older_than( '1.9.0' ) ) {
497 936 global $Visualizer_Pro;
498 - $Visualizer_Pro->_enqueueScriptsAndStyles( $data );
937 + $Visualizer_Pro->_enqueueScriptsAndStyles( $data, $this->_chart->ID );
499 938 }
939 +
500 940 $this->_addAction( 'admin_head', 'renderFlattrScript' );
501 941 wp_iframe( array( $render, 'render' ) );
502 942 }
503 943
@@ -509,24 +949,35 @@
509 949 * @access private
510 950 */
511 951 private function _handleTypesPage() {
512 952 // process post request
513 - if ( $_SERVER['REQUEST_METHOD'] == 'POST' && wp_verify_nonce( filter_input( INPUT_POST, 'nonce' ) ) ) {
953 + if ( $_SERVER['REQUEST_METHOD'] === 'POST' && wp_verify_nonce( filter_input( INPUT_POST, 'nonce' ) ) ) {
514 954 $type = filter_input( INPUT_POST, 'type' );
515 - if ( in_array( $type, Visualizer_Plugin::getChartTypes() ) ) {
955 + $library = filter_input( INPUT_POST, 'chart-library' );
956 + if ( Visualizer_Module_Admin::checkChartStatus( $type ) ) {
957 + if ( empty( $library ) ) {
958 + // library cannot be empty.
959 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, 'Chart library empty while creating the chart! Aborting...', 'error', __FILE__, __LINE__ );
960 + return;
961 + }
962 +
516 963 // save new chart type
517 964 update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_CHART_TYPE, $type );
965 + update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_CHART_LIBRARY, $library );
518 966 // if the chart has default data, update it with appropriate default data for new type
519 967 if ( filter_var( get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_DEFAULT_DATA, true ), FILTER_VALIDATE_BOOLEAN ) ) {
520 968 $source = new Visualizer_Source_Csv( VISUALIZER_ABSPATH . DIRECTORY_SEPARATOR . 'samples' . DIRECTORY_SEPARATOR . $type . '.csv' );
521 969 $source->fetch();
522 - $this->_chart->post_content = $source->getData();
970 + $this->_chart->post_content = $source->getData( get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) );
523 971 wp_update_post( $this->_chart->to_array() );
524 972 update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
525 973 }
974 +
975 + Visualizer_Module_Utility::set_defaults( $this->_chart );
976 +
526 977 // redirect to next tab
527 978 // changed by Ash/Upwork
528 - wp_redirect( add_query_arg( 'tab', 'settings' ) );
979 + wp_redirect( esc_url_raw( add_query_arg( 'tab', 'settings' ) ) );
529 980
530 981 return;
531 982 }
532 983 }
@@ -531,9 +982,9 @@
531 982 }
532 983 }
533 984 $render = new Visualizer_Render_Page_Types();
534 985 $render->type = get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_CHART_TYPE, true );
535 - $render->types = Visualizer_Module_Admin::_getChartTypesLocalized();
986 + $render->types = Visualizer_Module_Admin::_getChartTypesLocalized( false, false, false, 'types' );
536 987 $render->chart = $this->_chart;
537 988 wp_enqueue_style( 'visualizer-frame' );
538 989 wp_enqueue_script( 'visualizer-frame' );
539 990 wp_iframe( array( $render, 'render' ) );
@@ -549,10 +1000,131 @@
549 1000 */
550 1001 public function renderFlattrScript() {
551 1002 echo '';
552 1003 }
553 - // changed by Ash/Upwork
1004 +
554 1005 /**
1006 + * Processes the CSV that is sent in the request as a string.
1007 + *
1008 + * @since 3.2.0
1009 + */
1010 + private function handleCSVasString( $data, $editor_type ) {
1011 + $source = null;
1012 +
1013 + switch ( $editor_type ) {
1014 + case 'text':
1015 + // data coming in from the text editor.
1016 + $tmpfile = tempnam( get_temp_dir(), Visualizer_Plugin::NAME );
1017 + $handle = fopen( $tmpfile, 'w' );
1018 + $values = preg_split( '/[\n\r]+/', stripslashes( trim( $data ) ) );
1019 + if ( $values ) {
1020 + foreach ( $values as $row ) {
1021 + if ( empty( $row ) ) {
1022 + continue;
1023 + }
1024 + $row = explode( ',', $row );
1025 + $row = array_map(
1026 + function( $r ) {
1027 + return '' === $r ? ' ' : $r;
1028 + },
1029 + $row
1030 + );
1031 + $row = implode( ',', $row );
1032 + // don't use fpucsv here because we need to just dump the data
1033 + // minus the empty rows
1034 + // because fputcsv needs to tokenize
1035 + // we can standardize the CSV enclosure here and replace all ' with "
1036 + // we can assume that if there are an even number of ' they should be changed to "
1037 + // but that will screw up words like fo'c'sle
1038 + // so here let's just assume ' will NOT be used for enclosures
1039 + fwrite( $handle, $row );
1040 + fwrite( $handle, PHP_EOL );
1041 + }
1042 + }
1043 + $source = new Visualizer_Source_Csv( $tmpfile );
1044 + fclose( $handle );
1045 + break;
1046 + case 'table':
1047 + // Import from Chart
1048 + // fall-through.
1049 + case 'excel':
1050 + // data coming in from the excel editor.
1051 + $source = apply_filters( 'visualizer_pro_handle_chart_data', $data, '' );
1052 + break;
1053 + }
1054 + return $source;
1055 + }
1056 +
1057 + /**
1058 + * Parses the data uploaded as an HTML table.
1059 + *
1060 + * @since 3.2.0
1061 + *
1062 + * @access private
1063 + */
1064 + private function handleTabularData() {
1065 + $csv = array();
1066 + // the datatable mentions the headers twice, so lets remove the duplicates.
1067 + $headers = array_unique( array_filter( $_POST['header'] ) );
1068 + $types = $_POST['type'];
1069 +
1070 + // capture all the indexes that correspond to excluded columns.
1071 + $exclude = array();
1072 + $index = 0;
1073 + foreach ( $types as $type ) {
1074 + if ( empty( $type ) ) {
1075 + $exclude[] = $index;
1076 + }
1077 + $index++;
1078 + }
1079 +
1080 + // when N headers are being renamed, the number of headers increases by N
1081 + // because of the way datatable duplicates header information
1082 + // so unset the headers that have been renamed.
1083 + if ( count( $headers ) !== count( $types ) ) {
1084 + $to = count( $headers );
1085 + for ( $i = count( $types ); $i < $to; $i++ ) {
1086 + unset( $headers[ $i + 1 ] );
1087 + }
1088 + }
1089 +
1090 + $columns = array();
1091 + for ( $i = 0; $i < count( $headers ); $i++ ) {
1092 + if ( ! isset( $_POST[ 'data' . $i ] ) ) {
1093 + continue;
1094 + }
1095 + $columns[ $i ] = $_POST[ 'data' . $i ];
1096 + }
1097 +
1098 + $csv[] = $headers;
1099 + $csv[] = $types;
1100 + for ( $j = 0; $j < count( $columns[0] ); $j++ ) {
1101 + $row = array();
1102 + for ( $i = 0; $i < count( $headers ); $i++ ) {
1103 + $row[] = sanitize_text_field( $columns[ $i ][ $j ] );
1104 + }
1105 + $csv[] = $row;
1106 + }
1107 +
1108 + $tmpfile = tempnam( get_temp_dir(), Visualizer_Plugin::NAME );
1109 + $handle = fopen( $tmpfile, 'w' );
1110 +
1111 + if ( $csv ) {
1112 + $index = 0;
1113 + foreach ( $csv as $row ) {
1114 + // remove all the cells corresponding to the excluded headers.
1115 + foreach ( $exclude as $j ) {
1116 + unset( $row[ $j ] );
1117 + }
1118 + fputcsv( $handle, $row );
1119 + }
1120 + }
1121 + $source = new Visualizer_Source_Csv( $tmpfile );
1122 + fclose( $handle );
1123 + return $source;
1124 + }
1125 +
1126 + /**
555 1127 * Parses uploaded CSV file and saves new data for the chart.
556 1128 *
557 1129 * @since 1.0.0
558 1130 *
@@ -574,9 +1146,9 @@
574 1146
575 1147 // check chart, if chart exists
576 1148 // do not use filter_input as it does not work for phpunit test cases, use filter_var instead
577 1149 $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 ) {
1150 + if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ) {
579 1151 if ( ! $can_die ) {
580 1152 return;
581 1153 }
582 1154 status_header( 400 );
@@ -582,8 +1154,10 @@
582 1154 status_header( 400 );
583 1155 exit;
584 1156 }
585 1157
1158 + 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__ );
1159 +
586 1160 if ( ! isset( $_POST['vz-import-time'] ) ) {
587 1161 apply_filters( 'visualizer_pro_remove_schedule', $chart_id );
588 1162 }
589 1163
@@ -588,32 +1162,65 @@
588 1162 }
589 1163
590 1164 if ( ! isset( $_POST['chart_data_src'] ) || Visualizer_Plugin::CF_SOURCE_FILTER !== $_POST['chart_data_src'] ) {
591 1165 // delete the filters in case this chart is being uploaded from other data sources
592 - delete_post_meta( $chart_id, 'visualizer-filter-config' );
1166 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_FILTER_CONFIG );
1167 + delete_post_meta( $chart_id, '__transient-' . Visualizer_Plugin::CF_FILTER_CONFIG );
1168 + delete_post_meta( $chart_id, '__transient-' . Visualizer_Plugin::CF_DB_QUERY );
593 1169
594 1170 // delete "import from db" specific parameters.
595 1171 delete_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY );
596 1172 delete_post_meta( $chart_id, Visualizer_Plugin::CF_DB_SCHEDULE );
1173 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_REMOTE_DB_PARAMS );
597 1174 }
598 1175
1176 + // delete json related data.
1177 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_URL );
1178 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_ROOT );
1179 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_PAGING );
1180 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_HEADERS );
1181 +
1182 + // delete last error
1183 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR );
1184 +
1185 + // delete editor related data.
1186 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR );
1187 +
1188 + // delete this so that a JSON import can be later edited manually without a problem.
1189 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE );
1190 +
599 1191 $source = null;
600 1192 $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'] );
1193 +
1194 + $remote_data = false;
1195 + if ( isset( $_POST['remote_data'] ) && function_exists( 'wp_http_validate_url' ) ) {
1196 + $remote_data = wp_http_validate_url( $_POST['remote_data'] );
1197 + }
1198 + if ( false !== $remote_data ) {
1199 + $source = new Visualizer_Source_Csv_Remote( $remote_data );
603 1200 if ( isset( $_POST['vz-import-time'] ) ) {
604 - apply_filters( 'visualizer_pro_chart_schedule', $chart_id, $_POST['remote_data'], $_POST['vz-import-time'] );
1201 + apply_filters( 'visualizer_pro_chart_schedule', $chart_id, $remote_data, $_POST['vz-import-time'] );
605 1202 }
1203 + // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
606 1204 } elseif ( isset( $_FILES['local_data'] ) && $_FILES['local_data']['error'] == 0 ) {
607 1205 $source = new Visualizer_Source_Csv( $_FILES['local_data']['tmp_name'] );
608 1206 } elseif ( isset( $_POST['chart_data'] ) && strlen( $_POST['chart_data'] ) > 0 ) {
609 - $source = apply_filters( 'visualizer_pro_handle_chart_data', $_POST['chart_data'], '' );
1207 + $source = $this->handleCSVasString( $_POST['chart_data'], $_POST['editor-type'] );
1208 + update_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR, $_POST['editor-type'] );
1209 + } elseif ( isset( $_POST['table_data'] ) && 'yes' === $_POST['table_data'] ) {
1210 + $source = $this->handleTabularData();
1211 + update_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR, $_POST['editor-type'] );
610 1212 } else {
611 - $render->message = esc_html__( 'CSV file with chart data was not uploaded. Please, try again.', 'visualizer' );
1213 + 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__ );
1214 + $render->message = esc_html__( 'CSV file with chart data was not uploaded. Please try again.', 'visualizer' );
1215 + update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, esc_html__( 'CSV file with chart data was not uploaded. Please try again.', 'visualizer' ) );
612 1216 }
1217 +
1218 + 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__ );
1219 +
613 1220 if ( $source ) {
614 1221 if ( $source->fetch() ) {
615 - $content = $source->getData();
1222 + $content = $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) );
616 1223 $populate = true;
617 1224 if ( is_string( $content ) && is_array( unserialize( $content ) ) ) {
618 1225 $json = unserialize( $content );
619 1226 // if source exists, so should data. if source exists but data is blank, do not populate the chart.
@@ -619,8 +1226,9 @@
619 1226 // if source exists, so should data. if source exists but data is blank, do not populate the chart.
620 1227 // if we populate the data even if it is empty, the chart will show "Table has no columns".
621 1228 if ( array_key_exists( 'source', $json ) && ! empty( $json['source'] ) && ( ! array_key_exists( 'data', $json ) || empty( $json['data'] ) ) ) {
622 1229 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__ );
1230 + 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 1231 $populate = false;
624 1232 }
625 1233 }
626 1234 if ( $populate ) {
@@ -626,17 +1234,43 @@
626 1234 if ( $populate ) {
627 1235 $chart->post_content = $content;
628 1236 }
629 1237 wp_update_post( $chart->to_array() );
1238 +
630 1239 update_post_meta( $chart->ID, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
631 1240 update_post_meta( $chart->ID, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
632 1241 update_post_meta( $chart->ID, Visualizer_Plugin::CF_DEFAULT_DATA, 0 );
1242 +
1243 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Updated post for chart %d', $chart_id ), 'debug', __FILE__, __LINE__ );
1244 +
1245 + Visualizer_Module_Utility::set_defaults( $chart, null );
1246 +
1247 + $settings = get_post_meta( $chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
1248 + if ( isset( $settings['series'] ) && ! ( count( $settings['series'] ) - count( $source->getSeries() ) > 1 ) ) {
1249 + $diff_total_series = abs( count( $settings['series'] ) - count( $source->getSeries() ) );
1250 + if ( $diff_total_series ) {
1251 + foreach ( range( 1, $diff_total_series ) as $k => $diff_series ) {
1252 + $settings['series'][] = end( $settings['series'] );
1253 + }
1254 + update_post_meta( $chart->ID, Visualizer_Plugin::CF_SETTINGS, $settings );
1255 + }
1256 + }
1257 +
633 1258 $render->id = $chart->ID;
634 - $render->data = json_encode( $source->getRawData() );
1259 + $render->data = json_encode( $source->getRawData( get_post_meta( $chart->ID, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ) );
635 1260 $render->series = json_encode( $source->getSeries() );
1261 + $render->settings = json_encode( $settings );
636 1262 } else {
637 - $render->message = esc_html__( 'CSV file is broken or invalid. Please, try again.', 'visualizer' );
1263 + $error = $source->get_error();
1264 + if ( empty( $error ) ) {
1265 + $error = esc_html__( 'CSV file is broken or invalid. Please try again.', 'visualizer' );
1266 + }
1267 + $render->message = $error;
1268 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( '%s for chart %d.', $error, $chart_id ), 'error', __FILE__, __LINE__ );
1269 + update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, $error );
638 1270 }
1271 + } else {
1272 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Unknown internal error for chart %d.', $chart_id ), 'error', __FILE__, __LINE__ );
639 1273 }
640 1274 $render->render();
641 1275 if ( ! $can_die ) {
642 1276 return;
@@ -652,27 +1286,18 @@
652 1286 * @access public
653 1287 */
654 1288 public function cloneChart() {
655 1289 $chart_id = $success = false;
656 - $nonce = wp_verify_nonce( filter_input( INPUT_GET, 'nonce' ), Visualizer_Plugin::ACTION_CLONE_CHART );
1290 + $nonce = isset( $_GET['nonce'] ) && wp_verify_nonce( $_GET['nonce'], Visualizer_Plugin::ACTION_CLONE_CHART );
657 1291 $capable = current_user_can( 'edit_posts' );
658 1292 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 - );
1293 + $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
669 1294 if ( $chart_id ) {
670 1295 $chart = get_post( $chart_id );
671 - $success = $chart && $chart->post_type == Visualizer_Plugin::CPT_VISUALIZER;
1296 + $success = $chart && $chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
672 1297 }
673 1298 }
674 - $redirect = wp_get_referer();
1299 + $redirect = remove_query_arg( 'vaction', wp_get_referer() );
675 1300 if ( $success ) {
676 1301 $new_chart_id = wp_insert_post(
677 1302 array(
678 1303 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
@@ -681,23 +1306,35 @@
681 1306 'post_status' => $chart->post_status,
682 1307 'post_content' => $chart->post_content,
683 1308 )
684 1309 );
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' ),
1310 + if ( is_wp_error( $new_chart_id ) ) {
1311 + 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__ );
1312 + } else {
1313 + $post_meta = get_post_meta( $chart_id );
1314 + foreach ( $post_meta as $key => $value ) {
1315 + if ( strpos( $key, 'visualizer-' ) !== false ) {
1316 + add_post_meta( $new_chart_id, $key, maybe_unserialize( $value[0] ) );
1317 + }
1318 + }
1319 + $redirect = esc_url(
1320 + add_query_arg(
1321 + array(
1322 + 'page' => 'visualizer',
1323 + 'type' => filter_input( INPUT_GET, 'type' ),
1324 + 'vaction' => false,
1325 + ),
1326 + admin_url( 'admin.php' )
695 1327 ),
696 - admin_url( 'upload.php' )
1328 + null,
1329 + 'db'
697 1330 );
698 1331 }
699 1332 }
1333 +
1334 + if ( defined( 'WP_TESTS_DOMAIN' ) ) {
1335 + wp_die();
1336 + }
700 1337 wp_redirect( $redirect );
701 1338 exit;
702 1339 }
703 1340
@@ -743,9 +1380,12 @@
743 1380 $data = $this->_getChartArray();
744 1381 $render = new Visualizer_Render_Page_Data();
745 1382 $render->chart = $this->_chart;
746 1383 $render->type = $data['type'];
747 - unset( $data['settings']['width'], $data['settings']['height'] );
1384 +
1385 + if ( $data && $data['settings'] ) {
1386 + unset( $data['settings']['width'], $data['settings']['height'], $data['settings']['chartArea'] );
1387 + }
748 1388 wp_enqueue_style( 'visualizer-frame' );
749 1389 wp_enqueue_script( 'visualizer-render' );
750 1390 wp_localize_script(
751 1391 'visualizer-render',
@@ -751,9 +1391,9 @@
751 1391 'visualizer-render',
752 1392 'visualizer',
753 1393 array(
754 1394 'l10n' => array(
755 - 'invalid_source' => esc_html__( 'You have entered invalid URL. Please, insert proper URL.', 'visualizer' ),
1395 + 'invalid_source' => esc_html__( 'You have entered an invalid URL. Please provide a valid URL.', 'visualizer' ),
756 1396 'loading' => esc_html__( 'Loading...', 'visualizer' ),
757 1397 ),
758 1398 'charts' => array(
759 1399 'canvas' => $data,
@@ -759,13 +1399,16 @@
759 1399 'canvas' => $data,
760 1400 ),
761 1401 )
762 1402 );
763 - // Added by Ash/Upwork
764 - if ( VISUALIZER_PRO ) {
1403 +
1404 + do_action( 'visualizer_enqueue_scripts_and_styles', $data, $this->_chart->ID );
1405 +
1406 + if ( Visualizer_Module::is_pro() && Visualizer_Module::is_pro_older_than( '1.9.0' ) ) {
765 1407 global $Visualizer_Pro;
766 - $Visualizer_Pro->_enqueueScriptsAndStyles( $data );
1408 + $Visualizer_Pro->_enqueueScriptsAndStyles( $data, $this->_chart->ID );
767 1409 }
1410 +
768 1411 // Added by Ash/Upwork
769 1412 $this->_addAction( 'admin_head', 'renderFlattrScript' );
770 1413 wp_iframe( array( $render, 'render' ) );
771 1414 }
@@ -778,13 +1421,14 @@
778 1421 public function getQueryData() {
779 1422 check_ajax_referer( Visualizer_Plugin::ACTION_FETCH_DB_DATA . Visualizer_Plugin::VERSION, 'security' );
780 1423
781 1424 $params = wp_parse_args( $_POST['params'] );
782 - $source = new Visualizer_Source_Query( stripslashes( $params['query'] ) );
1425 + $chart_id = filter_var( $params['chart_id'], FILTER_VALIDATE_INT );
1426 +
1427 + $source = new Visualizer_Source_Query( stripslashes( $params['query'] ), $chart_id, $params );
783 1428 $html = $source->fetch( true );
784 - $error = '';
785 - if ( empty( $html ) ) {
786 - $error = $source->get_error();
1429 + $error = $source->get_error();
1430 + if ( ! empty( $error ) ) {
787 1431 wp_send_json_error( array( 'msg' => $error ) );
788 1432 }
789 1433 wp_send_json_success( array( 'table' => $html ) );
790 1434 }
@@ -807,21 +1451,42 @@
807 1451 ),
808 1452 )
809 1453 );
810 1454
1455 + $hours = filter_input(
1456 + INPUT_POST,
1457 + 'refresh',
1458 + FILTER_VALIDATE_FLOAT,
1459 + array(
1460 + 'options' => array(
1461 + 'min_range' => -1,
1462 + 'max_range' => apply_filters( 'visualizer_is_business', false ) ? PHP_INT_MAX : -1,
1463 + ),
1464 + )
1465 + );
1466 +
1467 + if ( ! is_numeric( $hours ) ) {
1468 + $hours = -1;
1469 + }
1470 +
811 1471 $render = new Visualizer_Render_Page_Update();
812 1472 if ( $chart_id ) {
813 1473 $params = wp_parse_args( $_POST['params'] );
814 - $source = new Visualizer_Source_Query( stripslashes( $params['query'] ) );
1474 + $source = new Visualizer_Source_Query( stripslashes( $params['query'] ), $chart_id, $params );
815 1475 $source->fetch( false );
816 1476 $error = $source->get_error();
817 1477 if ( empty( $error ) ) {
818 - $hours = $_POST['refresh'];
819 1478 update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY, stripslashes( $params['query'] ) );
820 1479 update_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
821 1480 update_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
822 1481 update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_SCHEDULE, $hours );
823 1482 update_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, 0 );
1483 + if ( isset( $params['db_type'] ) && $params['db_type'] !== Visualizer_Plugin::WP_DB_NAME ) {
1484 + $remote_db_params = $params;
1485 + unset( $remote_db_params['query'] );
1486 + unset( $remote_db_params['chart_id'] );
1487 + update_post_meta( $chart_id, Visualizer_Plugin::CF_REMOTE_DB_PARAMS, $remote_db_params );
1488 + }
824 1489
825 1490 $schedules = get_option( Visualizer_Plugin::CF_DB_SCHEDULE, array() );
826 1491 $schedules[ $chart_id ] = time() + $hours * HOUR_IN_SECONDS;
827 1492 update_option( Visualizer_Plugin::CF_DB_SCHEDULE, $schedules );
@@ -828,13 +1493,14 @@
828 1493
829 1494 wp_update_post(
830 1495 array(
831 1496 'ID' => $chart_id,
832 - 'post_content' => $source->getData(),
1497 + 'post_content' => $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ),
833 1498 )
834 1499 );
835 - $render->data = json_encode( $source->getRawData() );
1500 + $render->data = json_encode( $source->getRawData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ) );
836 1501 $render->series = json_encode( $source->getSeries() );
1502 + $render->id = $chart_id;
837 1503 } else {
838 1504 $render->message = $error;
839 1505 }
840 1506 }
@@ -841,6 +1507,102 @@
841 1507 $render->render();
842 1508 if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
843 1509 defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
844 1510 }
1511 + }
1512 +
1513 +
1514 + /**
1515 + * Saves the filter query and the schedule.
1516 + *
1517 + * @access public
1518 + */
1519 + public function saveFilter() {
1520 + check_ajax_referer( Visualizer_Plugin::ACTION_SAVE_FILTER_QUERY . Visualizer_Plugin::VERSION, 'security' );
1521 +
1522 + $chart_id = filter_input(
1523 + INPUT_GET,
1524 + 'chart',
1525 + FILTER_VALIDATE_INT,
1526 + array(
1527 + 'options' => array(
1528 + 'min_range' => 1,
1529 + ),
1530 + )
1531 + );
1532 +
1533 + $hours = filter_input(
1534 + INPUT_POST,
1535 + 'refresh',
1536 + FILTER_VALIDATE_INT,
1537 + array(
1538 + 'options' => array(
1539 + 'min_range' => -1,
1540 + 'max_range' => apply_filters( 'visualizer_is_business', false ) ? PHP_INT_MAX : -1,
1541 + ),
1542 + )
1543 + );
1544 +
1545 + if ( 0 !== $hours && empty( $hours ) ) {
1546 + $hours = -1;
1547 + }
1548 +
1549 + do_action( 'visualizer_save_filter', $chart_id, $hours );
1550 +
1551 + if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
1552 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1553 + }
1554 + }
1555 +
1556 + /**
1557 + * Save chart image.
1558 + *
1559 + * @param string $base64_img Chart image.
1560 + * @param int $chart_id Chart ID.
1561 + * @param bool $save_attachment Save attachment.
1562 + * @return attachment ID
1563 + */
1564 + public function save_chart_image( $base64_img, $chart_id, $save_attachment = true ) {
1565 + // Delete old chart image.
1566 + $old_attachment_id = get_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_IMAGE, true );
1567 + if ( $old_attachment_id ) {
1568 + wp_delete_attachment( $old_attachment_id, true );
1569 + }
1570 +
1571 + if ( ! $save_attachment ) {
1572 + return 0;
1573 + }
1574 +
1575 + // Upload dir.
1576 + $upload_dir = wp_upload_dir();
1577 + $upload_path = str_replace( '/', DIRECTORY_SEPARATOR, $upload_dir['path'] ) . DIRECTORY_SEPARATOR;
1578 +
1579 + $img = str_replace( 'data:image/png;base64,', '', $base64_img );
1580 + $img = str_replace( ' ', '+', $img );
1581 + $decoded = base64_decode( $img );
1582 + $filename = 'visualization-' . $chart_id . '.png';
1583 + $file_type = 'image/png';
1584 + $hashed_filename = $filename;
1585 +
1586 + // Save the image in the uploads directory.
1587 + require_once ABSPATH . '/wp-admin/includes/file.php';
1588 + \WP_Filesystem();
1589 + global $wp_filesystem;
1590 + if ( ! is_a( $wp_filesystem, 'WP_Filesystem_Base' ) ) {
1591 + $creds = request_filesystem_credentials( site_url() );
1592 + wp_filesystem( $creds );
1593 + }
1594 + $upload_file = $wp_filesystem->put_contents( $upload_path . $hashed_filename, $decoded );
1595 +
1596 + // Insert new chart image.
1597 + $attachment = array(
1598 + 'post_mime_type' => $file_type,
1599 + 'post_title' => preg_replace( '/\.[^.]+$/', '', basename( $hashed_filename ) ),
1600 + 'post_content' => '',
1601 + 'post_status' => 'inherit',
1602 + 'guid' => $upload_dir['url'] . '/' . basename( $hashed_filename ),
1603 + );
1604 +
1605 + $attach_id = wp_insert_attachment( $attachment, $upload_dir['path'] . '/' . $hashed_filename );
1606 + return $attach_id;
845 1607 }
846 1608 }