PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.11.7
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.11.7
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 +846 -121 3.1.33.11.7 View file →
@@ -60,12 +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 +
64 70 $this->_addAjaxAction( Visualizer_Plugin::ACTION_SAVE_FILTER_QUERY, 'saveFilter' );
71 +
72 + $this->_addFilter( 'visualizer_get_sidebar', 'getSidebar', 10, 2 );
73 +
65 74 }
66 75
67 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 + /**
68 305 * Fetches charts from database.
69 306 *
70 307 * This method is also called from the media pop-up (classic editor: create a post and add chart from insert content).
71 308 *
@@ -94,9 +331,9 @@
94 331 // 'filter' is from the modal from the add media button.
95 332 $filter = filter_input( INPUT_GET, 'filter', FILTER_SANITIZE_STRING );
96 333 }
97 334
98 - if ( $filter && in_array( $filter, Visualizer_Plugin::getChartTypes() ) ) {
335 + if ( $filter && in_array( $filter, Visualizer_Plugin::getChartTypes(), true ) ) {
99 336 $query_args['meta_query'] = array(
100 337 array(
101 338 'key' => Visualizer_Plugin::CF_CHART_TYPE,
102 339 'value' => $filter,
@@ -147,9 +384,9 @@
147 384 $chart = $this->_chart;
148 385 }
149 386 $type = get_post_meta( $chart->ID, Visualizer_Plugin::CF_CHART_TYPE, true );
150 387 $series = apply_filters( Visualizer_Plugin::FILTER_GET_CHART_SERIES, get_post_meta( $chart->ID, Visualizer_Plugin::CF_SERIES, true ), $chart->ID, $type );
151 - $data = apply_filters( Visualizer_Plugin::FILTER_GET_CHART_DATA, unserialize( $chart->post_content ), $chart->ID, $type );
388 + $data = self::get_chart_data( $chart, $type );
152 389 $library = $this->load_chart_type( $chart->ID );
153 390
154 391 $settings = get_post_meta( $chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
155 392 $settings = apply_filters( Visualizer_Plugin::FILTER_GET_CHART_SETTINGS, $settings, $chart->ID, $type );
@@ -201,9 +438,9 @@
201 438 *
202 439 * @access public
203 440 */
204 441 public function deleteChart() {
205 - $is_post = $_SERVER['REQUEST_METHOD'] == 'POST';
442 + $is_post = $_SERVER['REQUEST_METHOD'] === 'POST';
206 443 $input_method = $is_post ? INPUT_POST : INPUT_GET;
207 444 $chart_id = $success = false;
208 445 $nonce = wp_verify_nonce( filter_input( $input_method, 'nonce' ) );
209 446 $capable = current_user_can( 'delete_posts' );
@@ -219,13 +456,26 @@
219 456 )
220 457 );
221 458 if ( $chart_id ) {
222 459 $chart = get_post( $chart_id );
223 - $success = $chart && $chart->post_type == Visualizer_Plugin::CPT_VISUALIZER;
460 + $success = $chart && $chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
224 461 }
225 462 }
226 463 if ( $success ) {
227 - 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 + }
228 478 }
229 479 if ( $is_post ) {
230 480 self::_sendResponse(
231 481 array(
@@ -232,9 +482,9 @@
232 482 'success' => $success,
233 483 )
234 484 );
235 485 }
236 - wp_redirect( wp_get_referer() );
486 + wp_redirect( remove_query_arg( 'vaction', wp_get_referer() ) );
237 487 exit;
238 488 }
239 489
240 490 /**
@@ -274,50 +524,119 @@
274 524 * @access public
275 525 */
276 526 public function renderChartPages() {
277 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' );
278 533 // check chart, if chart not exists, will create new one and redirects to the same page with proper chart id
279 534 $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
280 - if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type != Visualizer_Plugin::CPT_VISUALIZER ) {
281 - $this->deleteOldCharts();
282 - $default_type = isset( $_GET['type'] ) && ! empty( $_GET['type'] ) ? $_GET['type'] : 'line';
283 - $source = new Visualizer_Source_Csv( VISUALIZER_ABSPATH . DIRECTORY_SEPARATOR . 'samples' . DIRECTORY_SEPARATOR . $default_type . '.csv' );
284 - $source->fetch();
285 - $chart_id = wp_insert_post(
286 - array(
287 - 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
288 - 'post_title' => 'Visualization',
289 - 'post_author' => get_current_user_id(),
290 - 'post_status' => 'auto-draft',
291 - 'post_content' => $source->getData(),
292 - )
293 - );
294 - if ( $chart_id && ! is_wp_error( $chart_id ) ) {
295 - add_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_TYPE, $default_type );
296 - add_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, 1 );
297 - add_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
298 - add_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
299 - add_post_meta(
300 - $chart_id,
301 - 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(
302 549 array(
303 - '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 ) ),
304 555 )
305 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 + }
306 605 do_action( 'visualizer_pro_new_chart_defaults', $chart_id );
307 606 }
308 - wp_redirect( add_query_arg( 'chart', (int) $chart_id ) );
309 - 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();
310 613 }
311 614
312 - $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;
313 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 +
314 633 // enqueue and register scripts and styles
315 634 wp_register_script( 'visualizer-chosen', VISUALIZER_ABSURL . 'js/lib/chosen.jquery.min.js', array( 'jquery' ), Visualizer_Plugin::VERSION );
316 635 wp_register_style( 'visualizer-chosen', VISUALIZER_ABSURL . 'css/lib/chosen.min.css', array(), Visualizer_Plugin::VERSION );
317 636
318 637 wp_register_style( 'visualizer-frame', VISUALIZER_ABSURL . 'css/frame.css', array( 'visualizer-chosen' ), Visualizer_Plugin::VERSION );
319 - 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 );
320 639 wp_register_script( 'visualizer-customization', $this->get_user_customization_js(), array(), null, true );
321 640 wp_register_script(
322 641 'visualizer-render',
323 642 VISUALIZER_ABSURL . 'js/render-facade.js',
@@ -328,19 +647,23 @@
328 647 wp_register_script(
329 648 'visualizer-preview',
330 649 VISUALIZER_ABSURL . 'js/preview.js',
331 650 array(
332 - 'wp-color-picker',
651 + $color_picker_dep,
333 652 'visualizer-render',
334 653 ),
335 654 Visualizer_Plugin::VERSION,
336 655 true
337 656 );
338 - // added by Ash/Upwork
339 - 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' ) ) {
340 662 global $Visualizer_Pro;
341 663 $Visualizer_Pro->_addScriptsAndStyles();
342 664 }
665 +
343 666 // dispatch pages
344 667 $this->_chart = get_post( $chart_id );
345 668 $tab = isset( $_GET['tab'] ) && ! empty( $_GET['tab'] ) ? $_GET['tab'] : 'visualizer';
346 669
@@ -352,8 +675,9 @@
352 675 if ( isset( $_POST['cancel'] ) && 1 === intval( $_POST['cancel'] ) ) {
353 676 // if the cancel button is clicked.
354 677 $this->undoRevisions( $chart_id, true );
355 678 } elseif ( isset( $_POST['save'] ) && 1 === intval( $_POST['save'] ) ) {
679 + $this->handlePermissions();
356 680 // if the save button is clicked.
357 681 $this->undoRevisions( $chart_id, false );
358 682 } else {
359 683 // if the edit button is clicked.
@@ -359,8 +683,16 @@
359 683 // if the edit button is clicked.
360 684 $this->_chart = $this->handleExistingRevisions( $chart_id, $this->_chart );
361 685 }
362 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 +
363 695 switch ( $tab ) {
364 696 case 'settings':
365 697 $this->_handleDataAndSettingsPage();
366 698 break;
@@ -368,9 +700,9 @@
368 700 case 'visualizer': // fall through.
369 701 $this->_handleTypesPage();
370 702 break;
371 703 default:
372 - do_action( 'visualizer_pro_handle_tab', $tab, $this->_chart );
704 + // this should never happen.
373 705 break;
374 706 }
375 707 defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
376 708 }
@@ -377,25 +709,26 @@
377 709
378 710 /**
379 711 * Load code editor assets.
380 712 */
381 - private function loadCodeEditorAssets() {
713 + private function loadCodeEditorAssets( $chart_id ) {
382 714 global $wp_version;
383 715
384 - 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() ) {
385 726 return;
386 727 }
387 728
388 - $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 );
389 730
390 - // data tables assets.
391 - wp_register_script( 'visualizer-datatables', '//cdn.datatables.net/1.10.16/js/jquery.dataTables.min.js', array( 'jquery-ui-core' ), Visualizer_Plugin::VERSION );
392 - wp_register_style( 'visualizer-datatables', '//cdn.datatables.net/1.10.16/css/jquery.dataTables.min.css', array(), Visualizer_Plugin::VERSION );
393 - wp_register_style( 'visualizer-datatables-ui', '//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css', array( 'visualizer-datatables' ), Visualizer_Plugin::VERSION );
394 -
395 - wp_enqueue_script( 'visualizer-datatables' );
396 - wp_enqueue_style( 'visualizer-datatables-ui' );
397 -
398 731 if ( version_compare( $wp_version, '4.9.0', '<' ) ) {
399 732 // code mirror assets.
400 733 wp_register_script( 'visualizer-codemirror-core', '//codemirror.net/lib/codemirror.js', array( 'jquery' ), Visualizer_Plugin::VERSION );
401 734 wp_register_script( 'visualizer-codemirror-placeholder', '//codemirror.net/addon/display/placeholder.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
@@ -418,9 +751,9 @@
418 751 'lineWrapping' => true,
419 752 'dragDrop' => false,
420 753 'matchBrackets' => true,
421 754 'autoCloseBrackets' => true,
422 - 'extraKeys' => array( 'Ctrl-Space' => 'autocomplete' ),
755 + 'extraKeys' => array( 'Shift-Space' => 'autocomplete' ),
423 756 'hintOptions' => array( 'tables' => $table_col_mapping ),
424 757 ),
425 758 )
426 759 );
@@ -429,8 +762,25 @@
429 762 return $table_col_mapping;
430 763 }
431 764
432 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 + /**
433 783 * Handle data and settings page
434 784 */
435 785 private function _handleDataAndSettingsPage() {
436 786 if ( isset( $_POST['map_api_key'] ) ) {
@@ -435,30 +785,56 @@
435 785 private function _handleDataAndSettingsPage() {
436 786 if ( isset( $_POST['map_api_key'] ) ) {
437 787 update_option( 'visualizer-map-api-key', $_POST['map_api_key'] );
438 788 }
439 - if ( $_SERVER['REQUEST_METHOD'] == 'POST' && isset( $_GET['nonce'] ) && wp_verify_nonce( $_GET['nonce'] ) ) {
440 - if ( $this->_chart->post_status == 'auto-draft' ) {
789 +
790 + if ( $_SERVER['REQUEST_METHOD'] === 'POST' && isset( $_GET['nonce'] ) && wp_verify_nonce( $_GET['nonce'] ) ) {
791 + $is_canceled = isset( $_POST['cancel'] ) && 1 === intval( $_POST['cancel'] );
792 + $is_newly_created = $this->_chart->post_status === 'auto-draft';
793 +
794 + if ( $is_newly_created && ! $is_canceled ) {
441 795 $this->_chart->post_status = 'publish';
442 796
443 797 // 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.
444 798 // we do not want any difference in data so disable revisions temporarily.
445 - add_filter( 'wp_revisions_to_keep', '__return_false' );
799 + $this->disableRevisionsTemporarily();
800 +
446 801 wp_update_post( $this->_chart->to_array() );
447 802 }
448 803 // save meta data only when it is NOT being canceled.
449 - if ( ! ( isset( $_POST['cancel'] ) && 1 === intval( $_POST['cancel'] ) ) ) {
804 + if ( ! $is_canceled ) {
450 805 update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, $_POST );
806 +
807 + // we will keep a parameter called 'internal_title' that will be set to the given title or, if empty, the chart ID
808 + // this will help in searching with the chart id.
809 + $settings = get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
810 + $title = null;
811 + if ( isset( $settings['title'] ) && ! empty( $settings['title'] ) ) {
812 + $title = $settings['title'];
813 + if ( is_array( $title ) ) {
814 + $title = $settings['title']['text'];
815 + }
816 + }
817 + if ( empty( $title ) ) {
818 + $title = $this->_chart->ID;
819 + }
820 + $settings['internal_title'] = $title;
821 + $settings_label = isset( $settings['pieResidueSliceLabel'] ) ? $settings['pieResidueSliceLabel'] : '';
822 + if ( empty( $settings_label ) ) {
823 + $settings['pieResidueSliceLabel'] = esc_html__( 'Other', 'visualizer' );
824 + } else {
825 + $settings['pieResidueSliceLabel'] = $settings_label;
826 + }
827 + update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, $settings );
451 828 }
452 829 $render = new Visualizer_Render_Page_Send();
453 830 $render->text = sprintf( '[visualizer id="%d"]', $this->_chart->ID );
454 831 wp_iframe( array( $render, 'render' ) );
455 -
456 832 return;
457 833 }
458 834 $data = $this->_getChartArray();
459 835 $sidebar = '';
460 - $sidebar_class = 'Visualizer_Render_Sidebar_Type_' . ucfirst( $data['type'] );
836 + $sidebar_class = $this->load_chart_class_name( $this->_chart->ID );
461 837 if ( class_exists( $sidebar_class, true ) ) {
462 838 $sidebar = new $sidebar_class( $data['settings'] );
463 839 $sidebar->__series = $data['series'];
464 840 $sidebar->__data = $data['data'];
@@ -463,14 +839,14 @@
463 839 $sidebar->__series = $data['series'];
464 840 $sidebar->__data = $data['data'];
465 841 } else {
466 842 $sidebar = apply_filters( 'visualizer_pro_chart_type_sidebar', '', $data );
467 - if ( $sidebar != '' ) {
843 + if ( $sidebar !== '' ) {
468 844 $sidebar->__series = $data['series'];
469 845 $sidebar->__data = $data['data'];
470 846 }
471 847 }
472 - unset( $data['settings']['width'], $data['settings']['height'] );
848 + unset( $data['settings']['width'], $data['settings']['height'], $data['settings']['chartArea'] );
473 849 wp_enqueue_style( 'wp-color-picker' );
474 850 wp_enqueue_style( 'visualizer-frame' );
475 851 wp_enqueue_script( 'visualizer-preview' );
476 852 wp_enqueue_script( 'visualizer-chosen' );
@@ -475,17 +851,38 @@
475 851 wp_enqueue_script( 'visualizer-preview' );
476 852 wp_enqueue_script( 'visualizer-chosen' );
477 853 wp_enqueue_script( 'visualizer-render' );
478 854
479 - $table_col_mapping = $this->loadCodeEditorAssets();
855 + if ( Visualizer_Module::can_show_feature( 'simple-editor' ) ) {
856 + wp_enqueue_script( 'visualizer-editor-simple' );
857 + wp_localize_script(
858 + 'visualizer-editor-simple',
859 + 'visualizer1',
860 + array(
861 + 'ajax' => array(
862 + 'url' => admin_url( 'admin-ajax.php' ),
863 + 'nonces' => array(
864 + ),
865 + 'actions' => array(
866 + ),
867 + ),
868 + )
869 + );
870 + }
480 871
872 + $table_col_mapping = $this->loadCodeEditorAssets( $this->_chart->ID );
873 +
481 874 wp_localize_script(
482 875 'visualizer-render',
483 876 'visualizer',
484 877 array(
485 878 'l10n' => array(
486 - 'invalid_source' => esc_html__( 'You have entered invalid URL. Please, insert proper URL.', 'visualizer' ),
487 - 'loading' => esc_html__( 'Loading...', 'visualizer' ),
879 + 'invalid_source' => esc_html__( 'You have entered an invalid URL. Please provide a valid URL.', 'visualizer' ),
880 + 'loading' => esc_html__( 'Loading...', 'visualizer' ),
881 + 'json_error' => esc_html__( 'An error occured in fetching data.', 'visualizer' ),
882 + 'select_columns' => esc_html__( 'Please select a few columns to include in the chart.', 'visualizer' ),
883 + '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' ),
884 + 'copied' => __( 'The data has been copied to your clipboard. Hit Ctrl-V/Cmd-V in your spreadsheet editor to paste the data.', 'visualizer' ),
488 885 ),
489 886 'charts' => array(
490 887 'canvas' => $data,
491 888 'id' => $this->_chart->ID,
@@ -496,19 +893,29 @@
496 893 'url' => admin_url( 'admin-ajax.php' ),
497 894 'nonces' => array(
498 895 'permissions' => wp_create_nonce( Visualizer_Plugin::ACTION_FETCH_PERMISSIONS_DATA ),
499 896 'db_get_data' => wp_create_nonce( Visualizer_Plugin::ACTION_FETCH_DB_DATA . Visualizer_Plugin::VERSION ),
897 + 'json_get_roots' => wp_create_nonce( Visualizer_Plugin::ACTION_JSON_GET_ROOTS . Visualizer_Plugin::VERSION ),
898 + 'json_get_data' => wp_create_nonce( Visualizer_Plugin::ACTION_JSON_GET_DATA . Visualizer_Plugin::VERSION ),
899 + 'json_set_schedule' => wp_create_nonce( Visualizer_Plugin::ACTION_JSON_SET_SCHEDULE . Visualizer_Plugin::VERSION ),
500 900 ),
501 901 'actions' => array(
502 902 'permissions' => Visualizer_Plugin::ACTION_FETCH_PERMISSIONS_DATA,
503 903 'db_get_data' => Visualizer_Plugin::ACTION_FETCH_DB_DATA,
904 + 'json_get_roots' => Visualizer_Plugin::ACTION_JSON_GET_ROOTS,
905 + 'json_get_data' => Visualizer_Plugin::ACTION_JSON_GET_DATA,
906 + 'json_set_schedule' => Visualizer_Plugin::ACTION_JSON_SET_SCHEDULE,
504 907 ),
505 908 ),
506 909 'db_query' => array(
507 910 'tables' => $table_col_mapping,
508 911 ),
509 - 'is_pro' => VISUALIZER_PRO,
912 + 'is_pro' => Visualizer_Module::is_pro(),
510 913 'page_type' => 'chart',
914 + 'json_tag_separator' => Visualizer_Source_Json::TAG_SEPARATOR,
915 + 'json_tag_separator_view' => Visualizer_Source_Json::TAG_SEPARATOR_VIEW,
916 + 'is_front' => false,
917 + 'rest_base' => get_rest_url( null, 'wc/v3/reports/' ),
511 918 )
512 919 );
513 920
514 921 $render = new Visualizer_Render_Page_Data();
@@ -516,21 +923,24 @@
516 923 $render->type = $data['type'];
517 924 $render->custom_css = $data['css'];
518 925 $render->sidebar = $sidebar;
519 926 if ( filter_input( INPUT_GET, 'library', FILTER_VALIDATE_BOOLEAN ) ) {
520 - $render->button = filter_input( INPUT_GET, 'action' ) == Visualizer_Plugin::ACTION_EDIT_CHART
927 + $render->button = filter_input( INPUT_GET, 'action' ) === Visualizer_Plugin::ACTION_EDIT_CHART
521 928 ? esc_html__( 'Save Chart', 'visualizer' )
522 929 : esc_html__( 'Create Chart', 'visualizer' );
523 - if ( filter_input( INPUT_GET, 'action' ) == Visualizer_Plugin::ACTION_EDIT_CHART ) {
524 - $render->cancel_button = esc_html__( 'Cancel', 'visualizer' );
525 - }
930 +
931 + $render->cancel_button = esc_html__( 'Cancel', 'visualizer' );
526 932 } else {
527 933 $render->button = esc_attr__( 'Insert Chart', 'visualizer' );
528 934 }
529 - if ( VISUALIZER_PRO ) {
935 +
936 + do_action( 'visualizer_enqueue_scripts_and_styles', $data, $this->_chart->ID );
937 +
938 + if ( Visualizer_Module::is_pro() && Visualizer_Module::is_pro_older_than( '1.9.0' ) ) {
530 939 global $Visualizer_Pro;
531 - $Visualizer_Pro->_enqueueScriptsAndStyles( $data );
940 + $Visualizer_Pro->_enqueueScriptsAndStyles( $data, $this->_chart->ID );
532 941 }
942 +
533 943 $this->_addAction( 'admin_head', 'renderFlattrScript' );
534 944 wp_iframe( array( $render, 'render' ) );
535 945 }
536 946
@@ -542,24 +952,35 @@
542 952 * @access private
543 953 */
544 954 private function _handleTypesPage() {
545 955 // process post request
546 - if ( $_SERVER['REQUEST_METHOD'] == 'POST' && wp_verify_nonce( filter_input( INPUT_POST, 'nonce' ) ) ) {
956 + if ( $_SERVER['REQUEST_METHOD'] === 'POST' && wp_verify_nonce( filter_input( INPUT_POST, 'nonce' ) ) ) {
547 957 $type = filter_input( INPUT_POST, 'type' );
548 - if ( in_array( $type, Visualizer_Plugin::getChartTypes() ) ) {
958 + $library = filter_input( INPUT_POST, 'chart-library' );
959 + if ( Visualizer_Module_Admin::checkChartStatus( $type ) ) {
960 + if ( empty( $library ) ) {
961 + // library cannot be empty.
962 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, 'Chart library empty while creating the chart! Aborting...', 'error', __FILE__, __LINE__ );
963 + return;
964 + }
965 +
549 966 // save new chart type
550 967 update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_CHART_TYPE, $type );
968 + update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_CHART_LIBRARY, $library );
551 969 // if the chart has default data, update it with appropriate default data for new type
552 970 if ( filter_var( get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_DEFAULT_DATA, true ), FILTER_VALIDATE_BOOLEAN ) ) {
553 971 $source = new Visualizer_Source_Csv( VISUALIZER_ABSPATH . DIRECTORY_SEPARATOR . 'samples' . DIRECTORY_SEPARATOR . $type . '.csv' );
554 972 $source->fetch();
555 - $this->_chart->post_content = $source->getData();
973 + $this->_chart->post_content = $source->getData( get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) );
556 974 wp_update_post( $this->_chart->to_array() );
557 975 update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
558 976 }
977 +
978 + Visualizer_Module_Utility::set_defaults( $this->_chart );
979 +
559 980 // redirect to next tab
560 981 // changed by Ash/Upwork
561 - wp_redirect( add_query_arg( 'tab', 'settings' ) );
982 + wp_redirect( esc_url_raw( add_query_arg( 'tab', 'settings' ) ) );
562 983
563 984 return;
564 985 }
565 986 }
@@ -564,9 +985,9 @@
564 985 }
565 986 }
566 987 $render = new Visualizer_Render_Page_Types();
567 988 $render->type = get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_CHART_TYPE, true );
568 - $render->types = Visualizer_Module_Admin::_getChartTypesLocalized();
989 + $render->types = Visualizer_Module_Admin::_getChartTypesLocalized( false, false, false, 'types' );
569 990 $render->chart = $this->_chart;
570 991 wp_enqueue_style( 'visualizer-frame' );
571 992 wp_enqueue_script( 'visualizer-frame' );
572 993 wp_iframe( array( $render, 'render' ) );
@@ -582,10 +1003,131 @@
582 1003 */
583 1004 public function renderFlattrScript() {
584 1005 echo '';
585 1006 }
586 - // changed by Ash/Upwork
1007 +
587 1008 /**
1009 + * Processes the CSV that is sent in the request as a string.
1010 + *
1011 + * @since 3.2.0
1012 + */
1013 + private function handleCSVasString( $data, $editor_type ) {
1014 + $source = null;
1015 +
1016 + switch ( $editor_type ) {
1017 + case 'text':
1018 + // data coming in from the text editor.
1019 + $tmpfile = tempnam( get_temp_dir(), Visualizer_Plugin::NAME );
1020 + $handle = fopen( $tmpfile, 'w' );
1021 + $values = preg_split( '/[\n\r]+/', stripslashes( trim( $data ) ) );
1022 + if ( $values ) {
1023 + foreach ( $values as $row ) {
1024 + if ( empty( $row ) ) {
1025 + continue;
1026 + }
1027 + $row = explode( ',', $row );
1028 + $row = array_map(
1029 + function( $r ) {
1030 + return '' === $r ? ' ' : $r;
1031 + },
1032 + $row
1033 + );
1034 + $row = implode( ',', $row );
1035 + // don't use fpucsv here because we need to just dump the data
1036 + // minus the empty rows
1037 + // because fputcsv needs to tokenize
1038 + // we can standardize the CSV enclosure here and replace all ' with "
1039 + // we can assume that if there are an even number of ' they should be changed to "
1040 + // but that will screw up words like fo'c'sle
1041 + // so here let's just assume ' will NOT be used for enclosures
1042 + fwrite( $handle, $row );
1043 + fwrite( $handle, PHP_EOL );
1044 + }
1045 + }
1046 + $source = new Visualizer_Source_Csv( $tmpfile );
1047 + fclose( $handle );
1048 + break;
1049 + case 'table':
1050 + // Import from Chart
1051 + // fall-through.
1052 + case 'excel':
1053 + // data coming in from the excel editor.
1054 + $source = apply_filters( 'visualizer_pro_handle_chart_data', $data, '' );
1055 + break;
1056 + }
1057 + return $source;
1058 + }
1059 +
1060 + /**
1061 + * Parses the data uploaded as an HTML table.
1062 + *
1063 + * @since 3.2.0
1064 + *
1065 + * @access private
1066 + */
1067 + private function handleTabularData() {
1068 + $csv = array();
1069 + // the datatable mentions the headers twice, so lets remove the duplicates.
1070 + $headers = array_unique( array_filter( $_POST['header'] ) );
1071 + $types = $_POST['type'];
1072 +
1073 + // capture all the indexes that correspond to excluded columns.
1074 + $exclude = array();
1075 + $index = 0;
1076 + foreach ( $types as $type ) {
1077 + if ( empty( $type ) ) {
1078 + $exclude[] = $index;
1079 + }
1080 + $index++;
1081 + }
1082 +
1083 + // when N headers are being renamed, the number of headers increases by N
1084 + // because of the way datatable duplicates header information
1085 + // so unset the headers that have been renamed.
1086 + if ( count( $headers ) !== count( $types ) ) {
1087 + $to = count( $headers );
1088 + for ( $i = count( $types ); $i < $to; $i++ ) {
1089 + unset( $headers[ $i + 1 ] );
1090 + }
1091 + }
1092 +
1093 + $columns = array();
1094 + for ( $i = 0; $i < count( $headers ); $i++ ) {
1095 + if ( ! isset( $_POST[ 'data' . $i ] ) ) {
1096 + continue;
1097 + }
1098 + $columns[ $i ] = $_POST[ 'data' . $i ];
1099 + }
1100 +
1101 + $csv[] = $headers;
1102 + $csv[] = $types;
1103 + for ( $j = 0; $j < count( $columns[0] ); $j++ ) {
1104 + $row = array();
1105 + for ( $i = 0; $i < count( $headers ); $i++ ) {
1106 + $row[] = sanitize_text_field( $columns[ $i ][ $j ] );
1107 + }
1108 + $csv[] = $row;
1109 + }
1110 +
1111 + $tmpfile = tempnam( get_temp_dir(), Visualizer_Plugin::NAME );
1112 + $handle = fopen( $tmpfile, 'w' );
1113 +
1114 + if ( $csv ) {
1115 + $index = 0;
1116 + foreach ( $csv as $row ) {
1117 + // remove all the cells corresponding to the excluded headers.
1118 + foreach ( $exclude as $j ) {
1119 + unset( $row[ $j ] );
1120 + }
1121 + fputcsv( $handle, $row );
1122 + }
1123 + }
1124 + $source = new Visualizer_Source_Csv( $tmpfile );
1125 + fclose( $handle );
1126 + return $source;
1127 + }
1128 +
1129 + /**
588 1130 * Parses uploaded CSV file and saves new data for the chart.
589 1131 *
590 1132 * @since 1.0.0
591 1133 *
@@ -607,9 +1149,9 @@
607 1149
608 1150 // check chart, if chart exists
609 1151 // do not use filter_input as it does not work for phpunit test cases, use filter_var instead
610 1152 $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
611 - if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type != Visualizer_Plugin::CPT_VISUALIZER ) {
1153 + if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ) {
612 1154 if ( ! $can_die ) {
613 1155 return;
614 1156 }
615 1157 status_header( 400 );
@@ -615,8 +1157,10 @@
615 1157 status_header( 400 );
616 1158 exit;
617 1159 }
618 1160
1161 + 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__ );
1162 +
619 1163 if ( ! isset( $_POST['vz-import-time'] ) ) {
620 1164 apply_filters( 'visualizer_pro_remove_schedule', $chart_id );
621 1165 }
622 1166
@@ -628,27 +1172,58 @@
628 1172
629 1173 // delete "import from db" specific parameters.
630 1174 delete_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY );
631 1175 delete_post_meta( $chart_id, Visualizer_Plugin::CF_DB_SCHEDULE );
1176 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_REMOTE_DB_PARAMS );
632 1177 }
633 1178
1179 + // delete json related data.
1180 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_URL );
1181 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_ROOT );
1182 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_PAGING );
1183 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_HEADERS );
1184 +
1185 + // delete last error
1186 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR );
1187 +
1188 + // delete editor related data.
1189 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR );
1190 +
1191 + // delete this so that a JSON import can be later edited manually without a problem.
1192 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE );
1193 +
634 1194 $source = null;
635 1195 $render = new Visualizer_Render_Page_Update();
636 - if ( isset( $_POST['remote_data'] ) && filter_var( $_POST['remote_data'], FILTER_VALIDATE_URL ) ) {
637 - $source = new Visualizer_Source_Csv_Remote( $_POST['remote_data'] );
1196 +
1197 + $remote_data = false;
1198 + if ( isset( $_POST['remote_data'] ) && function_exists( 'wp_http_validate_url' ) ) {
1199 + $remote_data = wp_http_validate_url( $_POST['remote_data'] );
1200 + }
1201 + if ( false !== $remote_data ) {
1202 + $source = new Visualizer_Source_Csv_Remote( $remote_data );
638 1203 if ( isset( $_POST['vz-import-time'] ) ) {
639 - apply_filters( 'visualizer_pro_chart_schedule', $chart_id, $_POST['remote_data'], $_POST['vz-import-time'] );
1204 + apply_filters( 'visualizer_pro_chart_schedule', $chart_id, $remote_data, $_POST['vz-import-time'] );
640 1205 }
1206 + // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
641 1207 } elseif ( isset( $_FILES['local_data'] ) && $_FILES['local_data']['error'] == 0 ) {
642 1208 $source = new Visualizer_Source_Csv( $_FILES['local_data']['tmp_name'] );
643 1209 } elseif ( isset( $_POST['chart_data'] ) && strlen( $_POST['chart_data'] ) > 0 ) {
644 - $source = apply_filters( 'visualizer_pro_handle_chart_data', $_POST['chart_data'], '', $chart_id, $_POST );
1210 + $source = $this->handleCSVasString( $_POST['chart_data'], $_POST['editor-type'] );
1211 + update_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR, $_POST['editor-type'] );
1212 + } elseif ( isset( $_POST['table_data'] ) && 'yes' === $_POST['table_data'] ) {
1213 + $source = $this->handleTabularData();
1214 + update_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR, $_POST['editor-type'] );
645 1215 } else {
646 - $render->message = esc_html__( 'CSV file with chart data was not uploaded. Please, try again.', 'visualizer' );
1216 + 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__ );
1217 + $render->message = esc_html__( 'CSV file with chart data was not uploaded. Please try again.', 'visualizer' );
1218 + update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, esc_html__( 'CSV file with chart data was not uploaded. Please try again.', 'visualizer' ) );
647 1219 }
1220 +
1221 + 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__ );
1222 +
648 1223 if ( $source ) {
649 1224 if ( $source->fetch() ) {
650 - $content = $source->getData();
1225 + $content = $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) );
651 1226 $populate = true;
652 1227 if ( is_string( $content ) && is_array( unserialize( $content ) ) ) {
653 1228 $json = unserialize( $content );
654 1229 // if source exists, so should data. if source exists but data is blank, do not populate the chart.
@@ -654,8 +1229,9 @@
654 1229 // if source exists, so should data. if source exists but data is blank, do not populate the chart.
655 1230 // if we populate the data even if it is empty, the chart will show "Table has no columns".
656 1231 if ( array_key_exists( 'source', $json ) && ! empty( $json['source'] ) && ( ! array_key_exists( 'data', $json ) || empty( $json['data'] ) ) ) {
657 1232 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__ );
1233 + update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, sprintf( 'Not populating chart data as source exists (%s) but data is empty!', $json['source'] ) );
658 1234 $populate = false;
659 1235 }
660 1236 }
661 1237 if ( $populate ) {
@@ -661,17 +1237,43 @@
661 1237 if ( $populate ) {
662 1238 $chart->post_content = $content;
663 1239 }
664 1240 wp_update_post( $chart->to_array() );
1241 +
665 1242 update_post_meta( $chart->ID, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
666 1243 update_post_meta( $chart->ID, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
667 1244 update_post_meta( $chart->ID, Visualizer_Plugin::CF_DEFAULT_DATA, 0 );
1245 +
1246 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Updated post for chart %d', $chart_id ), 'debug', __FILE__, __LINE__ );
1247 +
1248 + Visualizer_Module_Utility::set_defaults( $chart, null );
1249 +
1250 + $settings = get_post_meta( $chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
1251 + if ( isset( $settings['series'] ) && ! ( count( $settings['series'] ) - count( $source->getSeries() ) > 1 ) ) {
1252 + $diff_total_series = abs( count( $settings['series'] ) - count( $source->getSeries() ) );
1253 + if ( $diff_total_series ) {
1254 + foreach ( range( 1, $diff_total_series ) as $k => $diff_series ) {
1255 + $settings['series'][] = end( $settings['series'] );
1256 + }
1257 + update_post_meta( $chart->ID, Visualizer_Plugin::CF_SETTINGS, $settings );
1258 + }
1259 + }
1260 +
668 1261 $render->id = $chart->ID;
669 - $render->data = json_encode( $source->getRawData() );
1262 + $render->data = json_encode( $source->getRawData( get_post_meta( $chart->ID, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ) );
670 1263 $render->series = json_encode( $source->getSeries() );
1264 + $render->settings = json_encode( $settings );
671 1265 } else {
672 - $render->message = esc_html__( 'CSV file is broken or invalid. Please, try again.', 'visualizer' );
1266 + $error = $source->get_error();
1267 + if ( empty( $error ) ) {
1268 + $error = esc_html__( 'CSV file is broken or invalid. Please try again.', 'visualizer' );
1269 + }
1270 + $render->message = $error;
1271 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( '%s for chart %d.', $error, $chart_id ), 'error', __FILE__, __LINE__ );
1272 + update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, $error );
673 1273 }
1274 + } else {
1275 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Unknown internal error for chart %d.', $chart_id ), 'error', __FILE__, __LINE__ );
674 1276 }
675 1277 $render->render();
676 1278 if ( ! $can_die ) {
677 1279 return;
@@ -687,27 +1289,18 @@
687 1289 * @access public
688 1290 */
689 1291 public function cloneChart() {
690 1292 $chart_id = $success = false;
691 - $nonce = wp_verify_nonce( filter_input( INPUT_GET, 'nonce' ), Visualizer_Plugin::ACTION_CLONE_CHART );
1293 + $nonce = isset( $_GET['nonce'] ) && wp_verify_nonce( $_GET['nonce'], Visualizer_Plugin::ACTION_CLONE_CHART );
692 1294 $capable = current_user_can( 'edit_posts' );
693 1295 if ( $nonce && $capable ) {
694 - $chart_id = filter_input(
695 - INPUT_GET,
696 - 'chart',
697 - FILTER_VALIDATE_INT,
698 - array(
699 - 'options' => array(
700 - 'min_range' => 1,
701 - ),
702 - )
703 - );
1296 + $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
704 1297 if ( $chart_id ) {
705 1298 $chart = get_post( $chart_id );
706 - $success = $chart && $chart->post_type == Visualizer_Plugin::CPT_VISUALIZER;
1299 + $success = $chart && $chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
707 1300 }
708 1301 }
709 - $redirect = wp_get_referer();
1302 + $redirect = remove_query_arg( 'vaction', wp_get_referer() );
710 1303 if ( $success ) {
711 1304 $new_chart_id = wp_insert_post(
712 1305 array(
713 1306 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
@@ -716,23 +1309,35 @@
716 1309 'post_status' => $chart->post_status,
717 1310 'post_content' => $chart->post_content,
718 1311 )
719 1312 );
720 - if ( $new_chart_id && ! is_wp_error( $new_chart_id ) ) {
721 - add_post_meta( $new_chart_id, Visualizer_Plugin::CF_CHART_TYPE, get_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_TYPE, true ) );
722 - add_post_meta( $new_chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, get_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, true ) );
723 - add_post_meta( $new_chart_id, Visualizer_Plugin::CF_SOURCE, get_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, true ) );
724 - add_post_meta( $new_chart_id, Visualizer_Plugin::CF_SERIES, get_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, true ) );
725 - add_post_meta( $new_chart_id, Visualizer_Plugin::CF_SETTINGS, get_post_meta( $chart_id, Visualizer_Plugin::CF_SETTINGS, true ) );
726 - $redirect = add_query_arg(
727 - array(
728 - 'page' => 'visualizer',
729 - 'type' => filter_input( INPUT_GET, 'type' ),
1313 + if ( is_wp_error( $new_chart_id ) ) {
1314 + 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__ );
1315 + } else {
1316 + $post_meta = get_post_meta( $chart_id );
1317 + foreach ( $post_meta as $key => $value ) {
1318 + if ( strpos( $key, 'visualizer-' ) !== false ) {
1319 + add_post_meta( $new_chart_id, $key, maybe_unserialize( $value[0] ) );
1320 + }
1321 + }
1322 + $redirect = esc_url(
1323 + add_query_arg(
1324 + array(
1325 + 'page' => 'visualizer',
1326 + 'type' => filter_input( INPUT_GET, 'type' ),
1327 + 'vaction' => false,
1328 + ),
1329 + admin_url( 'admin.php' )
730 1330 ),
731 - admin_url( 'upload.php' )
1331 + null,
1332 + 'db'
732 1333 );
733 1334 }
734 1335 }
1336 +
1337 + if ( defined( 'WP_TESTS_DOMAIN' ) ) {
1338 + wp_die();
1339 + }
735 1340 wp_redirect( $redirect );
736 1341 exit;
737 1342 }
738 1343
@@ -778,9 +1383,12 @@
778 1383 $data = $this->_getChartArray();
779 1384 $render = new Visualizer_Render_Page_Data();
780 1385 $render->chart = $this->_chart;
781 1386 $render->type = $data['type'];
782 - unset( $data['settings']['width'], $data['settings']['height'] );
1387 +
1388 + if ( $data && $data['settings'] ) {
1389 + unset( $data['settings']['width'], $data['settings']['height'], $data['settings']['chartArea'] );
1390 + }
783 1391 wp_enqueue_style( 'visualizer-frame' );
784 1392 wp_enqueue_script( 'visualizer-render' );
785 1393 wp_localize_script(
786 1394 'visualizer-render',
@@ -786,9 +1394,9 @@
786 1394 'visualizer-render',
787 1395 'visualizer',
788 1396 array(
789 1397 'l10n' => array(
790 - 'invalid_source' => esc_html__( 'You have entered invalid URL. Please, insert proper URL.', 'visualizer' ),
1398 + 'invalid_source' => esc_html__( 'You have entered an invalid URL. Please provide a valid URL.', 'visualizer' ),
791 1399 'loading' => esc_html__( 'Loading...', 'visualizer' ),
792 1400 ),
793 1401 'charts' => array(
794 1402 'canvas' => $data,
@@ -794,13 +1402,16 @@
794 1402 'canvas' => $data,
795 1403 ),
796 1404 )
797 1405 );
798 - // Added by Ash/Upwork
799 - if ( VISUALIZER_PRO ) {
1406 +
1407 + do_action( 'visualizer_enqueue_scripts_and_styles', $data, $this->_chart->ID );
1408 +
1409 + if ( Visualizer_Module::is_pro() && Visualizer_Module::is_pro_older_than( '1.9.0' ) ) {
800 1410 global $Visualizer_Pro;
801 - $Visualizer_Pro->_enqueueScriptsAndStyles( $data );
1411 + $Visualizer_Pro->_enqueueScriptsAndStyles( $data, $this->_chart->ID );
802 1412 }
1413 +
803 1414 // Added by Ash/Upwork
804 1415 $this->_addAction( 'admin_head', 'renderFlattrScript' );
805 1416 wp_iframe( array( $render, 'render' ) );
806 1417 }
@@ -812,14 +1423,27 @@
812 1423 */
813 1424 public function getQueryData() {
814 1425 check_ajax_referer( Visualizer_Plugin::ACTION_FETCH_DB_DATA . Visualizer_Plugin::VERSION, 'security' );
815 1426
1427 + if ( ! current_user_can( 'administrator' ) ) {
1428 + wp_send_json_error( array( 'msg' => __( 'Action not allowed for this user.', 'visualizer' ) ) );
1429 + }
1430 + if ( ! is_super_admin() ) {
1431 + wp_send_json_error( array( 'msg' => __( 'Action not allowed for this user.', 'visualizer' ) ) );
1432 + }
1433 +
1434 + if ( ! Visualizer_Module::is_pro() ) {
1435 + wp_send_json_error( array( 'msg' => __( 'Feature is not available.', 'visualizer' ) ) );
1436 + }
1437 +
816 1438 $params = wp_parse_args( $_POST['params'] );
817 - $source = new Visualizer_Source_Query( stripslashes( $params['query'] ) );
1439 + $chart_id = filter_var( $params['chart_id'], FILTER_VALIDATE_INT );
1440 + $query = trim( $params['query'], ';' );
1441 +
1442 + $source = new Visualizer_Source_Query( stripslashes( $query ), $chart_id, $params );
818 1443 $html = $source->fetch( true );
819 - $error = '';
820 - if ( empty( $html ) ) {
821 - $error = $source->get_error();
1444 + $error = $source->get_error();
1445 + if ( ! empty( $error ) ) {
822 1446 wp_send_json_error( array( 'msg' => $error ) );
823 1447 }
824 1448 wp_send_json_success( array( 'table' => $html ) );
825 1449 }
@@ -831,8 +1455,19 @@
831 1455 */
832 1456 public function saveQuery() {
833 1457 check_ajax_referer( Visualizer_Plugin::ACTION_SAVE_DB_QUERY . Visualizer_Plugin::VERSION, 'security' );
834 1458
1459 + if ( ! current_user_can( 'administrator' ) ) {
1460 + wp_send_json_error( array( 'msg' => __( 'Action not allowed for this user.', 'visualizer' ) ) );
1461 + }
1462 + if ( ! is_super_admin() ) {
1463 + wp_send_json_error( array( 'msg' => __( 'Action not allowed for this user.', 'visualizer' ) ) );
1464 + }
1465 +
1466 + if ( ! Visualizer_Module::is_pro() ) {
1467 + wp_send_json_error( array( 'msg' => __( 'Feature is not available.', 'visualizer' ) ) );
1468 + }
1469 +
835 1470 $chart_id = filter_input(
836 1471 INPUT_GET,
837 1472 'chart',
838 1473 FILTER_VALIDATE_INT,
@@ -842,21 +1477,43 @@
842 1477 ),
843 1478 )
844 1479 );
845 1480
1481 + $hours = filter_input(
1482 + INPUT_POST,
1483 + 'refresh',
1484 + FILTER_VALIDATE_FLOAT,
1485 + array(
1486 + 'options' => array(
1487 + 'min_range' => -1,
1488 + 'max_range' => apply_filters( 'visualizer_is_business', false ) ? PHP_INT_MAX : -1,
1489 + ),
1490 + )
1491 + );
1492 +
1493 + if ( ! is_numeric( $hours ) ) {
1494 + $hours = -1;
1495 + }
1496 +
846 1497 $render = new Visualizer_Render_Page_Update();
847 1498 if ( $chart_id ) {
848 1499 $params = wp_parse_args( $_POST['params'] );
849 - $source = new Visualizer_Source_Query( stripslashes( $params['query'] ) );
1500 + $query = trim( $params['query'], ';' );
1501 + $source = new Visualizer_Source_Query( stripslashes( $query ), $chart_id, $params );
850 1502 $source->fetch( false );
851 1503 $error = $source->get_error();
852 1504 if ( empty( $error ) ) {
853 - $hours = $_POST['refresh'];
854 - update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY, stripslashes( $params['query'] ) );
1505 + update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY, stripslashes( $query ) );
855 1506 update_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
856 1507 update_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
857 1508 update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_SCHEDULE, $hours );
858 1509 update_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, 0 );
1510 + if ( isset( $params['db_type'] ) && $params['db_type'] !== Visualizer_Plugin::WP_DB_NAME ) {
1511 + $remote_db_params = $params;
1512 + unset( $remote_db_params['query'] );
1513 + unset( $remote_db_params['chart_id'] );
1514 + update_post_meta( $chart_id, Visualizer_Plugin::CF_REMOTE_DB_PARAMS, $remote_db_params );
1515 + }
859 1516
860 1517 $schedules = get_option( Visualizer_Plugin::CF_DB_SCHEDULE, array() );
861 1518 $schedules[ $chart_id ] = time() + $hours * HOUR_IN_SECONDS;
862 1519 update_option( Visualizer_Plugin::CF_DB_SCHEDULE, $schedules );
@@ -863,13 +1520,14 @@
863 1520
864 1521 wp_update_post(
865 1522 array(
866 1523 'ID' => $chart_id,
867 - 'post_content' => $source->getData(),
1524 + 'post_content' => $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ),
868 1525 )
869 1526 );
870 - $render->data = json_encode( $source->getRawData() );
1527 + $render->data = json_encode( $source->getRawData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ) );
871 1528 $render->series = json_encode( $source->getSeries() );
1529 + $render->id = $chart_id;
872 1530 } else {
873 1531 $render->message = $error;
874 1532 }
875 1533 }
@@ -898,13 +1556,80 @@
898 1556 ),
899 1557 )
900 1558 );
901 1559
902 - $hours = $_POST['refresh'];
1560 + $hours = filter_input(
1561 + INPUT_POST,
1562 + 'refresh',
1563 + FILTER_VALIDATE_INT,
1564 + array(
1565 + 'options' => array(
1566 + 'min_range' => -1,
1567 + 'max_range' => apply_filters( 'visualizer_is_business', false ) ? PHP_INT_MAX : -1,
1568 + ),
1569 + )
1570 + );
903 1571
1572 + if ( 0 !== $hours && empty( $hours ) ) {
1573 + $hours = -1;
1574 + }
1575 +
904 1576 do_action( 'visualizer_save_filter', $chart_id, $hours );
905 1577
906 1578 if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
907 1579 defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
908 1580 }
1581 + }
1582 +
1583 + /**
1584 + * Save chart image.
1585 + *
1586 + * @param string $base64_img Chart image.
1587 + * @param int $chart_id Chart ID.
1588 + * @param bool $save_attachment Save attachment.
1589 + * @return attachment ID
1590 + */
1591 + public function save_chart_image( $base64_img, $chart_id, $save_attachment = true ) {
1592 + // Delete old chart image.
1593 + $old_attachment_id = get_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_IMAGE, true );
1594 + if ( $old_attachment_id ) {
1595 + wp_delete_attachment( $old_attachment_id, true );
1596 + }
1597 +
1598 + if ( ! $save_attachment ) {
1599 + return 0;
1600 + }
1601 +
1602 + // Upload dir.
1603 + $upload_dir = wp_upload_dir();
1604 + $upload_path = str_replace( '/', DIRECTORY_SEPARATOR, $upload_dir['path'] ) . DIRECTORY_SEPARATOR;
1605 +
1606 + $img = str_replace( 'data:image/png;base64,', '', $base64_img );
1607 + $img = str_replace( ' ', '+', $img );
1608 + $decoded = base64_decode( $img );
1609 + $filename = 'visualization-' . $chart_id . '.png';
1610 + $file_type = 'image/png';
1611 + $hashed_filename = $filename;
1612 +
1613 + // Save the image in the uploads directory.
1614 + require_once ABSPATH . '/wp-admin/includes/file.php';
1615 + \WP_Filesystem();
1616 + global $wp_filesystem;
1617 + if ( ! is_a( $wp_filesystem, 'WP_Filesystem_Base' ) ) {
1618 + $creds = request_filesystem_credentials( site_url() );
1619 + wp_filesystem( $creds );
1620 + }
1621 + $upload_file = $wp_filesystem->put_contents( $upload_path . $hashed_filename, $decoded );
1622 +
1623 + // Insert new chart image.
1624 + $attachment = array(
1625 + 'post_mime_type' => $file_type,
1626 + 'post_title' => preg_replace( '/\.[^.]+$/', '', basename( $hashed_filename ) ),
1627 + 'post_content' => '',
1628 + 'post_status' => 'inherit',
1629 + 'guid' => $upload_dir['url'] . '/' . basename( $hashed_filename ),
1630 + );
1631 +
1632 + $attach_id = wp_insert_attachment( $attachment, $upload_dir['path'] . '/' . $hashed_filename );
1633 + return $attach_id;
909 1634 }
910 1635 }