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 +1087 -105 3.0.83.10.8 View file →
@@ -57,13 +57,256 @@
57 57 $this->_addAjaxAction( Visualizer_Plugin::ACTION_EDIT_CHART, 'renderChartPages' );
58 58 $this->_addAjaxAction( Visualizer_Plugin::ACTION_UPLOAD_DATA, 'uploadData' );
59 59 $this->_addAjaxAction( Visualizer_Plugin::ACTION_CLONE_CHART, 'cloneChart' );
60 60 $this->_addAjaxAction( Visualizer_Plugin::ACTION_EXPORT_DATA, 'exportData' );
61 +
62 + $this->_addAjaxAction( Visualizer_Plugin::ACTION_FETCH_DB_DATA, 'getQueryData' );
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 +
61 74 }
62 75
63 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 + /**
64 305 * Fetches charts from database.
65 306 *
307 + * This method is also called from the media pop-up (classic editor: create a post and add chart from insert content).
308 + *
66 309 * @since 1.0.0
67 310 *
68 311 * @access public
69 312 */
@@ -71,9 +314,12 @@
71 314 $query_args = array(
72 315 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
73 316 'posts_per_page' => 9,
74 317 'paged' => filter_input(
75 - INPUT_GET, 'page', FILTER_VALIDATE_INT, array(
318 + INPUT_GET,
319 + 'page',
320 + FILTER_VALIDATE_INT,
321 + array(
76 322 'options' => array(
77 323 'min_range' => 1,
78 324 'default' => 1,
79 325 ),
@@ -85,9 +331,9 @@
85 331 // 'filter' is from the modal from the add media button.
86 332 $filter = filter_input( INPUT_GET, 'filter', FILTER_SANITIZE_STRING );
87 333 }
88 334
89 - if ( $filter && in_array( $filter, Visualizer_Plugin::getChartTypes() ) ) {
335 + if ( $filter && in_array( $filter, Visualizer_Plugin::getChartTypes(), true ) ) {
90 336 $query_args['meta_query'] = array(
91 337 array(
92 338 'key' => Visualizer_Plugin::CF_CHART_TYPE,
93 339 'value' => $filter,
@@ -100,8 +346,18 @@
100 346 while ( $query->have_posts() ) {
101 347 $chart = $query->next_post();
102 348 $chart_data = $this->_getChartArray( $chart );
103 349 $chart_data['id'] = $chart->ID;
350 + $chart_data['library'] = $this->load_chart_type( $chart->ID );
351 + $css = '';
352 + $settings = $chart_data['settings'];
353 + $arguments = $this->get_inline_custom_css( 'visualizer-chart-' . $chart->ID, $settings );
354 + if ( ! empty( $arguments ) ) {
355 + $css = $arguments[0];
356 + $settings = $arguments[1];
357 + }
358 + $chart_data['settings'] = $settings;
359 + $chart_data['css'] = $css;
104 360 $charts[] = $chart_data;
105 361 }
106 362 self::_sendResponse(
107 363 array(
@@ -128,15 +384,34 @@
128 384 $chart = $this->_chart;
129 385 }
130 386 $type = get_post_meta( $chart->ID, Visualizer_Plugin::CF_CHART_TYPE, true );
131 387 $series = apply_filters( Visualizer_Plugin::FILTER_GET_CHART_SERIES, get_post_meta( $chart->ID, Visualizer_Plugin::CF_SERIES, true ), $chart->ID, $type );
132 - $data = apply_filters( Visualizer_Plugin::FILTER_GET_CHART_DATA, unserialize( $chart->post_content ), $chart->ID, $type );
388 + $data = self::get_chart_data( $chart, $type );
389 + $library = $this->load_chart_type( $chart->ID );
133 390
391 + $settings = get_post_meta( $chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
392 + $settings = apply_filters( Visualizer_Plugin::FILTER_GET_CHART_SETTINGS, $settings, $chart->ID, $type );
393 + if ( ! empty( $atts['settings'] ) ) {
394 + $settings = apply_filters( $atts['settings'], $settings, $chart->ID, $type );
395 + }
396 +
397 + $css = '';
398 + $arguments = $this->get_inline_custom_css( 'visualizer-' . $chart->ID, $settings );
399 + if ( ! empty( $arguments ) ) {
400 + $css = $arguments[0];
401 + $settings = $arguments[1];
402 + }
403 +
404 + $date_formats = Visualizer_Source::get_date_formats_if_exists( $series, $data );
405 +
134 406 return array(
135 407 'type' => $type,
136 408 'series' => $series,
137 - 'settings' => get_post_meta( $chart->ID, Visualizer_Plugin::CF_SETTINGS, true ),
409 + 'settings' => $settings,
138 410 'data' => $data,
411 + 'library' => $library,
412 + 'css' => $css,
413 + 'date_formats' => $date_formats,
139 414 );
140 415 }
141 416
142 417 /**
@@ -163,9 +438,9 @@
163 438 *
164 439 * @access public
165 440 */
166 441 public function deleteChart() {
167 - $is_post = $_SERVER['REQUEST_METHOD'] == 'POST';
442 + $is_post = $_SERVER['REQUEST_METHOD'] === 'POST';
168 443 $input_method = $is_post ? INPUT_POST : INPUT_GET;
169 444 $chart_id = $success = false;
170 445 $nonce = wp_verify_nonce( filter_input( $input_method, 'nonce' ) );
171 446 $capable = current_user_can( 'delete_posts' );
@@ -170,9 +445,12 @@
170 445 $nonce = wp_verify_nonce( filter_input( $input_method, 'nonce' ) );
171 446 $capable = current_user_can( 'delete_posts' );
172 447 if ( $nonce && $capable ) {
173 448 $chart_id = filter_input(
174 - $input_method, 'chart', FILTER_VALIDATE_INT, array(
449 + $input_method,
450 + 'chart',
451 + FILTER_VALIDATE_INT,
452 + array(
175 453 'options' => array(
176 454 'min_range' => 1,
177 455 ),
178 456 )
@@ -178,13 +456,26 @@
178 456 )
179 457 );
180 458 if ( $chart_id ) {
181 459 $chart = get_post( $chart_id );
182 - $success = $chart && $chart->post_type == Visualizer_Plugin::CPT_VISUALIZER;
460 + $success = $chart && $chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
183 461 }
184 462 }
185 463 if ( $success ) {
186 - 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 + }
187 478 }
188 479 if ( $is_post ) {
189 480 self::_sendResponse(
190 481 array(
@@ -191,13 +482,41 @@
191 482 'success' => $success,
192 483 )
193 484 );
194 485 }
195 - wp_redirect( wp_get_referer() );
486 + wp_redirect( remove_query_arg( 'vaction', wp_get_referer() ) );
196 487 exit;
197 488 }
198 489
199 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 + /**
200 519 * Renders appropriate page for chart builder. Creates new auto draft chart
201 520 * if no chart has been specified.
202 521 *
203 522 * @since 1.0.0
@@ -205,64 +524,146 @@
205 524 * @access public
206 525 */
207 526 public function renderChartPages() {
208 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' );
209 533 // check chart, if chart not exists, will create new one and redirects to the same page with proper chart id
210 534 $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
211 - if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type != Visualizer_Plugin::CPT_VISUALIZER ) {
212 - $default_type = isset( $_GET['type'] ) && ! empty( $_GET['type'] ) ? $_GET['type'] : 'line';
213 - $source = new Visualizer_Source_Csv( VISUALIZER_ABSPATH . DIRECTORY_SEPARATOR . 'samples' . DIRECTORY_SEPARATOR . $default_type . '.csv' );
214 - $source->fetch();
215 - $chart_id = wp_insert_post(
216 - array(
217 - 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
218 - 'post_title' => 'Visualization',
219 - 'post_author' => get_current_user_id(),
220 - 'post_status' => 'auto-draft',
221 - 'post_content' => $source->getData(),
222 - )
223 - );
224 - if ( $chart_id && ! is_wp_error( $chart_id ) ) {
225 - add_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_TYPE, $default_type );
226 - add_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, 1 );
227 - add_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
228 - add_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
229 - add_post_meta(
230 - $chart_id, Visualizer_Plugin::CF_SETTINGS, array(
231 - 'focusTarget' => 'datum',
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(
549 + array(
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 ) ),
232 555 )
233 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 + }
234 605 do_action( 'visualizer_pro_new_chart_defaults', $chart_id );
235 606 }
236 - wp_redirect( add_query_arg( 'chart', (int) $chart_id ) );
237 - 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();
238 613 }
614 +
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;
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 +
239 633 // enqueue and register scripts and styles
240 634 wp_register_script( 'visualizer-chosen', VISUALIZER_ABSURL . 'js/lib/chosen.jquery.min.js', array( 'jquery' ), Visualizer_Plugin::VERSION );
241 635 wp_register_style( 'visualizer-chosen', VISUALIZER_ABSURL . 'css/lib/chosen.min.css', array(), Visualizer_Plugin::VERSION );
242 636
243 637 wp_register_style( 'visualizer-frame', VISUALIZER_ABSURL . 'css/frame.css', array( 'visualizer-chosen' ), Visualizer_Plugin::VERSION );
244 - wp_register_script( 'visualizer-frame', VISUALIZER_ABSURL . 'js/frame.js', array( 'visualizer-chosen' ), Visualizer_Plugin::VERSION, true );
245 - wp_register_script( 'google-jsapi-new', '//www.gstatic.com/charts/loader.js', array(), null, true );
246 - wp_register_script( 'google-jsapi-old', '//www.google.com/jsapi', array( 'google-jsapi-new' ), null, 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 );
639 + wp_register_script( 'visualizer-customization', $this->get_user_customization_js(), array(), null, true );
247 640 wp_register_script(
248 - 'visualizer-render', VISUALIZER_ABSURL . 'js/render.js', array(
249 - 'google-jsapi-old',
250 - 'google-jsapi-new',
251 - 'visualizer-frame',
252 - ), Visualizer_Plugin::VERSION, true
641 + 'visualizer-render',
642 + VISUALIZER_ABSURL . 'js/render-facade.js',
643 + apply_filters( 'visualizer_assets_render', array( 'visualizer-frame', 'visualizer-customization' ), false ),
644 + Visualizer_Plugin::VERSION,
645 + true
253 646 );
254 647 wp_register_script(
255 - 'visualizer-preview', VISUALIZER_ABSURL . 'js/preview.js', array(
256 - 'wp-color-picker',
648 + 'visualizer-preview',
649 + VISUALIZER_ABSURL . 'js/preview.js',
650 + array(
651 + $color_picker_dep,
257 652 'visualizer-render',
258 - ), Visualizer_Plugin::VERSION, true
653 + ),
654 + Visualizer_Plugin::VERSION,
655 + true
259 656 );
260 - // added by Ash/Upwork
261 - 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' ) ) {
262 662 global $Visualizer_Pro;
263 663 $Visualizer_Pro->_addScriptsAndStyles();
264 664 }
665 +
265 666 // dispatch pages
266 667 $this->_chart = get_post( $chart_id );
267 668 $tab = isset( $_GET['tab'] ) && ! empty( $_GET['tab'] ) ? $_GET['tab'] : 'visualizer';
268 669
@@ -274,8 +675,9 @@
274 675 if ( isset( $_POST['cancel'] ) && 1 === intval( $_POST['cancel'] ) ) {
275 676 // if the cancel button is clicked.
276 677 $this->undoRevisions( $chart_id, true );
277 678 } elseif ( isset( $_POST['save'] ) && 1 === intval( $_POST['save'] ) ) {
679 + $this->handlePermissions();
278 680 // if the save button is clicked.
279 681 $this->undoRevisions( $chart_id, false );
280 682 } else {
281 683 // if the edit button is clicked.
@@ -281,8 +683,16 @@
281 683 // if the edit button is clicked.
282 684 $this->_chart = $this->handleExistingRevisions( $chart_id, $this->_chart );
283 685 }
284 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 +
285 695 switch ( $tab ) {
286 696 case 'settings':
287 697 $this->_handleDataAndSettingsPage();
288 698 break;
@@ -290,9 +700,9 @@
290 700 case 'visualizer': // fall through.
291 701 $this->_handleTypesPage();
292 702 break;
293 703 default:
294 - do_action( 'visualizer_pro_handle_tab', $tab, $this->_chart );
704 + // this should never happen.
295 705 break;
296 706 }
297 707 defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
298 708 }
@@ -297,8 +707,80 @@
297 707 defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
298 708 }
299 709
300 710 /**
711 + * Load code editor assets.
712 + */
713 + private function loadCodeEditorAssets( $chart_id ) {
714 + global $wp_version;
715 +
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() ) {
726 + return;
727 + }
728 +
729 + $table_col_mapping = Visualizer_Source_Query_Params::get_all_db_tables_column_mapping( $chart_id );
730 +
731 + if ( version_compare( $wp_version, '4.9.0', '<' ) ) {
732 + // code mirror assets.
733 + wp_register_script( 'visualizer-codemirror-core', '//codemirror.net/lib/codemirror.js', array( 'jquery' ), Visualizer_Plugin::VERSION );
734 + wp_register_script( 'visualizer-codemirror-placeholder', '//codemirror.net/addon/display/placeholder.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
735 + wp_register_script( 'visualizer-codemirror-matchbrackets', '//codemirror.net/addon/edit/matchbrackets.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
736 + wp_register_script( 'visualizer-codemirror-closebrackets', '//codemirror.net/addon/edit/closebrackets.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
737 + wp_register_script( 'visualizer-codemirror-sql', '//codemirror.net/mode/sql/sql.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
738 + wp_register_script( 'visualizer-codemirror-sql-hint', '//codemirror.net/addon/hint/sql-hint.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
739 + wp_register_script( 'visualizer-codemirror-hint', '//codemirror.net/addon/hint/show-hint.js', array( 'visualizer-codemirror-sql', 'visualizer-codemirror-sql-hint', 'visualizer-codemirror-placeholder', 'visualizer-codemirror-matchbrackets', 'visualizer-codemirror-closebrackets' ), Visualizer_Plugin::VERSION );
740 + wp_register_style( 'visualizer-codemirror-core', '//codemirror.net/lib/codemirror.css', array(), Visualizer_Plugin::VERSION );
741 + wp_register_style( 'visualizer-codemirror-hint', '//codemirror.net/addon/hint/show-hint.css', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
742 +
743 + wp_enqueue_script( 'visualizer-codemirror-hint' );
744 + wp_enqueue_style( 'visualizer-codemirror-hint' );
745 + } else {
746 + wp_enqueue_code_editor(
747 + array(
748 + 'type' => 'sql',
749 + 'codemirror' => array(
750 + 'autofocus' => true,
751 + 'lineWrapping' => true,
752 + 'dragDrop' => false,
753 + 'matchBrackets' => true,
754 + 'autoCloseBrackets' => true,
755 + 'extraKeys' => array( 'Ctrl-Space' => 'autocomplete' ),
756 + 'hintOptions' => array( 'tables' => $table_col_mapping ),
757 + ),
758 + )
759 + );
760 + }
761 +
762 + return $table_col_mapping;
763 + }
764 +
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 + /**
301 783 * Handle data and settings page
302 784 */
303 785 private function _handleDataAndSettingsPage() {
304 786 if ( isset( $_POST['map_api_key'] ) ) {
@@ -303,23 +785,53 @@
303 785 private function _handleDataAndSettingsPage() {
304 786 if ( isset( $_POST['map_api_key'] ) ) {
305 787 update_option( 'visualizer-map-api-key', $_POST['map_api_key'] );
306 788 }
307 - if ( $_SERVER['REQUEST_METHOD'] == 'POST' && isset( $_GET['nonce'] ) && wp_verify_nonce( $_GET['nonce'] ) ) {
308 - 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' ) {
309 792 $this->_chart->post_status = 'publish';
793 +
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.
795 + // we do not want any difference in data so disable revisions temporarily.
796 + $this->disableRevisionsTemporarily();
797 +
310 798 wp_update_post( $this->_chart->to_array() );
311 799 }
312 - update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, $_POST );
800 + // save meta data only when it is NOT being canceled.
801 + if ( ! ( isset( $_POST['cancel'] ) && 1 === intval( $_POST['cancel'] ) ) ) {
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 );
825 + }
313 826 $render = new Visualizer_Render_Page_Send();
314 827 $render->text = sprintf( '[visualizer id="%d"]', $this->_chart->ID );
315 828 wp_iframe( array( $render, 'render' ) );
316 -
317 829 return;
318 830 }
319 831 $data = $this->_getChartArray();
320 832 $sidebar = '';
321 - $sidebar_class = 'Visualizer_Render_Sidebar_Type_' . ucfirst( $data['type'] );
833 + $sidebar_class = $this->load_chart_class_name( $this->_chart->ID );
322 834 if ( class_exists( $sidebar_class, true ) ) {
323 835 $sidebar = new $sidebar_class( $data['settings'] );
324 836 $sidebar->__series = $data['series'];
325 837 $sidebar->__data = $data['data'];
@@ -324,59 +836,108 @@
324 836 $sidebar->__series = $data['series'];
325 837 $sidebar->__data = $data['data'];
326 838 } else {
327 839 $sidebar = apply_filters( 'visualizer_pro_chart_type_sidebar', '', $data );
328 - if ( $sidebar != '' ) {
840 + if ( $sidebar !== '' ) {
329 841 $sidebar->__series = $data['series'];
330 842 $sidebar->__data = $data['data'];
331 843 }
332 844 }
333 - unset( $data['settings']['width'], $data['settings']['height'] );
334 - wp_enqueue_style( 'visualizer-frame' );
845 + unset( $data['settings']['width'], $data['settings']['height'], $data['settings']['chartArea'] );
335 846 wp_enqueue_style( 'wp-color-picker' );
336 847 wp_enqueue_style( 'visualizer-frame' );
337 848 wp_enqueue_script( 'visualizer-preview' );
849 + wp_enqueue_script( 'visualizer-chosen' );
338 850 wp_enqueue_script( 'visualizer-render' );
851 +
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 + }
868 +
869 + $table_col_mapping = $this->loadCodeEditorAssets( $this->_chart->ID );
870 +
339 871 wp_localize_script(
340 - 'visualizer-render', 'visualizer', array(
872 + 'visualizer-render',
873 + 'visualizer',
874 + array(
341 875 'l10n' => array(
342 - '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' ),
343 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' ),
344 881 ),
345 882 'charts' => array(
346 883 'canvas' => $data,
884 + 'id' => $this->_chart->ID,
347 885 ),
348 886 'language' => $this->get_language(),
349 887 'map_api_key' => get_option( 'visualizer-map-api-key' ),
350 - 'ajax' => array(
888 + 'ajax' => array(
351 889 'url' => admin_url( 'admin-ajax.php' ),
352 890 'nonces' => array(
353 891 'permissions' => wp_create_nonce( Visualizer_Plugin::ACTION_FETCH_PERMISSIONS_DATA ),
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 ),
354 896 ),
355 897 'actions' => array(
356 898 'permissions' => Visualizer_Plugin::ACTION_FETCH_PERMISSIONS_DATA,
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,
357 903 ),
358 904 ),
905 + 'db_query' => array(
906 + 'tables' => $table_col_mapping,
907 + ),
908 + 'is_pro' => Visualizer_Module::is_pro(),
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/' ),
359 914 )
360 915 );
916 +
361 917 $render = new Visualizer_Render_Page_Data();
362 918 $render->chart = $this->_chart;
363 919 $render->type = $data['type'];
920 + $render->custom_css = $data['css'];
364 921 $render->sidebar = $sidebar;
365 922 if ( filter_input( INPUT_GET, 'library', FILTER_VALIDATE_BOOLEAN ) ) {
366 - $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
367 924 ? esc_html__( 'Save Chart', 'visualizer' )
368 925 : esc_html__( 'Create Chart', 'visualizer' );
369 - if ( filter_input( INPUT_GET, 'action' ) == Visualizer_Plugin::ACTION_EDIT_CHART ) {
926 + if ( filter_input( INPUT_GET, 'action' ) === Visualizer_Plugin::ACTION_EDIT_CHART ) {
370 927 $render->cancel_button = esc_html__( 'Cancel', 'visualizer' );
371 928 }
372 929 } else {
373 930 $render->button = esc_attr__( 'Insert Chart', 'visualizer' );
374 931 }
375 - 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' ) ) {
376 936 global $Visualizer_Pro;
377 - $Visualizer_Pro->_enqueueScriptsAndStyles( $data );
937 + $Visualizer_Pro->_enqueueScriptsAndStyles( $data, $this->_chart->ID );
378 938 }
939 +
379 940 $this->_addAction( 'admin_head', 'renderFlattrScript' );
380 941 wp_iframe( array( $render, 'render' ) );
381 942 }
382 943
@@ -388,24 +949,35 @@
388 949 * @access private
389 950 */
390 951 private function _handleTypesPage() {
391 952 // process post request
392 - 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' ) ) ) {
393 954 $type = filter_input( INPUT_POST, 'type' );
394 - 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 +
395 963 // save new chart type
396 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 );
397 966 // if the chart has default data, update it with appropriate default data for new type
398 967 if ( filter_var( get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_DEFAULT_DATA, true ), FILTER_VALIDATE_BOOLEAN ) ) {
399 968 $source = new Visualizer_Source_Csv( VISUALIZER_ABSPATH . DIRECTORY_SEPARATOR . 'samples' . DIRECTORY_SEPARATOR . $type . '.csv' );
400 969 $source->fetch();
401 - $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 ) );
402 971 wp_update_post( $this->_chart->to_array() );
403 972 update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
404 973 }
974 +
975 + Visualizer_Module_Utility::set_defaults( $this->_chart );
976 +
405 977 // redirect to next tab
406 978 // changed by Ash/Upwork
407 - wp_redirect( add_query_arg( 'tab', 'settings' ) );
979 + wp_redirect( esc_url_raw( add_query_arg( 'tab', 'settings' ) ) );
408 980
409 981 return;
410 982 }
411 983 }
@@ -410,9 +982,9 @@
410 982 }
411 983 }
412 984 $render = new Visualizer_Render_Page_Types();
413 985 $render->type = get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_CHART_TYPE, true );
414 - $render->types = Visualizer_Module_Admin::_getChartTypesLocalized();
986 + $render->types = Visualizer_Module_Admin::_getChartTypesLocalized( false, false, false, 'types' );
415 987 $render->chart = $this->_chart;
416 988 wp_enqueue_style( 'visualizer-frame' );
417 989 wp_enqueue_script( 'visualizer-frame' );
418 990 wp_iframe( array( $render, 'render' ) );
@@ -428,10 +1000,131 @@
428 1000 */
429 1001 public function renderFlattrScript() {
430 1002 echo '';
431 1003 }
432 - // changed by Ash/Upwork
1004 +
433 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 + /**
434 1127 * Parses uploaded CSV file and saves new data for the chart.
435 1128 *
436 1129 * @since 1.0.0
437 1130 *
@@ -453,9 +1146,9 @@
453 1146
454 1147 // check chart, if chart exists
455 1148 // do not use filter_input as it does not work for phpunit test cases, use filter_var instead
456 1149 $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
457 - 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 ) {
458 1151 if ( ! $can_die ) {
459 1152 return;
460 1153 }
461 1154 status_header( 400 );
@@ -461,8 +1154,10 @@
461 1154 status_header( 400 );
462 1155 exit;
463 1156 }
464 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 +
465 1160 if ( ! isset( $_POST['vz-import-time'] ) ) {
466 1161 apply_filters( 'visualizer_pro_remove_schedule', $chart_id );
467 1162 }
468 1163
@@ -467,37 +1162,115 @@
467 1162 }
468 1163
469 1164 if ( ! isset( $_POST['chart_data_src'] ) || Visualizer_Plugin::CF_SOURCE_FILTER !== $_POST['chart_data_src'] ) {
470 1165 // delete the filters in case this chart is being uploaded from other data sources
471 - 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 );
1169 +
1170 + // delete "import from db" specific parameters.
1171 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY );
1172 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_DB_SCHEDULE );
1173 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_REMOTE_DB_PARAMS );
472 1174 }
473 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 +
474 1191 $source = null;
475 1192 $render = new Visualizer_Render_Page_Update();
476 - if ( isset( $_POST['remote_data'] ) && filter_var( $_POST['remote_data'], FILTER_VALIDATE_URL ) ) {
477 - $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 );
478 1200 if ( isset( $_POST['vz-import-time'] ) ) {
479 - 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'] );
480 1202 }
1203 + // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
481 1204 } elseif ( isset( $_FILES['local_data'] ) && $_FILES['local_data']['error'] == 0 ) {
482 1205 $source = new Visualizer_Source_Csv( $_FILES['local_data']['tmp_name'] );
483 1206 } elseif ( isset( $_POST['chart_data'] ) && strlen( $_POST['chart_data'] ) > 0 ) {
484 - $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'] );
485 1212 } else {
486 - $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' ) );
487 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 +
488 1220 if ( $source ) {
489 1221 if ( $source->fetch() ) {
490 - $chart->post_content = $source->getData();
1222 + $content = $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) );
1223 + $populate = true;
1224 + if ( is_string( $content ) && is_array( unserialize( $content ) ) ) {
1225 + $json = unserialize( $content );
1226 + // if source exists, so should data. if source exists but data is blank, do not populate the chart.
1227 + // if we populate the data even if it is empty, the chart will show "Table has no columns".
1228 + if ( array_key_exists( 'source', $json ) && ! empty( $json['source'] ) && ( ! array_key_exists( 'data', $json ) || empty( $json['data'] ) ) ) {
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'] ) );
1231 + $populate = false;
1232 + }
1233 + }
1234 + if ( $populate ) {
1235 + $chart->post_content = $content;
1236 + }
491 1237 wp_update_post( $chart->to_array() );
1238 +
492 1239 update_post_meta( $chart->ID, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
493 1240 update_post_meta( $chart->ID, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
494 1241 update_post_meta( $chart->ID, Visualizer_Plugin::CF_DEFAULT_DATA, 0 );
495 - $render->data = json_encode( $source->getRawData() );
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 +
1258 + $render->id = $chart->ID;
1259 + $render->data = json_encode( $source->getRawData( get_post_meta( $chart->ID, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ) );
496 1260 $render->series = json_encode( $source->getSeries() );
1261 + $render->settings = json_encode( $settings );
497 1262 } else {
498 - $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 );
499 1270 }
1271 + } else {
1272 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Unknown internal error for chart %d.', $chart_id ), 'error', __FILE__, __LINE__ );
500 1273 }
501 1274 $render->render();
502 1275 if ( ! $can_die ) {
503 1276 return;
@@ -513,24 +1286,18 @@
513 1286 * @access public
514 1287 */
515 1288 public function cloneChart() {
516 1289 $chart_id = $success = false;
517 - $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 );
518 1291 $capable = current_user_can( 'edit_posts' );
519 1292 if ( $nonce && $capable ) {
520 - $chart_id = filter_input(
521 - INPUT_GET, 'chart', FILTER_VALIDATE_INT, array(
522 - 'options' => array(
523 - 'min_range' => 1,
524 - ),
525 - )
526 - );
1293 + $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
527 1294 if ( $chart_id ) {
528 1295 $chart = get_post( $chart_id );
529 - $success = $chart && $chart->post_type == Visualizer_Plugin::CPT_VISUALIZER;
1296 + $success = $chart && $chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
530 1297 }
531 1298 }
532 - $redirect = wp_get_referer();
1299 + $redirect = remove_query_arg( 'vaction', wp_get_referer() );
533 1300 if ( $success ) {
534 1301 $new_chart_id = wp_insert_post(
535 1302 array(
536 1303 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
@@ -539,22 +1306,35 @@
539 1306 'post_status' => $chart->post_status,
540 1307 'post_content' => $chart->post_content,
541 1308 )
542 1309 );
543 - if ( $new_chart_id && ! is_wp_error( $new_chart_id ) ) {
544 - add_post_meta( $new_chart_id, Visualizer_Plugin::CF_CHART_TYPE, get_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_TYPE, true ) );
545 - add_post_meta( $new_chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, get_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, true ) );
546 - add_post_meta( $new_chart_id, Visualizer_Plugin::CF_SOURCE, get_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, true ) );
547 - add_post_meta( $new_chart_id, Visualizer_Plugin::CF_SERIES, get_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, true ) );
548 - add_post_meta( $new_chart_id, Visualizer_Plugin::CF_SETTINGS, get_post_meta( $chart_id, Visualizer_Plugin::CF_SETTINGS, true ) );
549 - $redirect = add_query_arg(
550 - array(
551 - 'page' => 'visualizer',
552 - 'type' => filter_input( INPUT_GET, 'type' ),
553 - ), admin_url( 'upload.php' )
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' )
1327 + ),
1328 + null,
1329 + 'db'
554 1330 );
555 1331 }
556 1332 }
1333 +
1334 + if ( defined( 'WP_TESTS_DOMAIN' ) ) {
1335 + wp_die();
1336 + }
557 1337 wp_redirect( $redirect );
558 1338 exit;
559 1339 }
560 1340
@@ -569,9 +1349,11 @@
569 1349 check_ajax_referer( Visualizer_Plugin::ACTION_EXPORT_DATA . Visualizer_Plugin::VERSION, 'security' );
570 1350 $capable = current_user_can( 'edit_posts' );
571 1351 if ( $capable ) {
572 1352 $chart_id = isset( $_GET['chart'] ) ? filter_var(
573 - $_GET['chart'], FILTER_VALIDATE_INT, array(
1353 + $_GET['chart'],
1354 + FILTER_VALIDATE_INT,
1355 + array(
574 1356 'options' => array(
575 1357 'min_range' => 1,
576 1358 ),
577 1359 )
@@ -598,15 +1380,20 @@
598 1380 $data = $this->_getChartArray();
599 1381 $render = new Visualizer_Render_Page_Data();
600 1382 $render->chart = $this->_chart;
601 1383 $render->type = $data['type'];
602 - 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 + }
603 1388 wp_enqueue_style( 'visualizer-frame' );
604 1389 wp_enqueue_script( 'visualizer-render' );
605 1390 wp_localize_script(
606 - 'visualizer-render', 'visualizer', array(
1391 + 'visualizer-render',
1392 + 'visualizer',
1393 + array(
607 1394 'l10n' => array(
608 - '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' ),
609 1396 'loading' => esc_html__( 'Loading...', 'visualizer' ),
610 1397 ),
611 1398 'charts' => array(
612 1399 'canvas' => $data,
@@ -612,15 +1399,210 @@
612 1399 'canvas' => $data,
613 1400 ),
614 1401 )
615 1402 );
616 - // Added by Ash/Upwork
617 - 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' ) ) {
618 1407 global $Visualizer_Pro;
619 - $Visualizer_Pro->_enqueueScriptsAndStyles( $data );
1408 + $Visualizer_Pro->_enqueueScriptsAndStyles( $data, $this->_chart->ID );
620 1409 }
1410 +
621 1411 // Added by Ash/Upwork
622 1412 $this->_addAction( 'admin_head', 'renderFlattrScript' );
623 1413 wp_iframe( array( $render, 'render' ) );
624 1414 }
625 1415
1416 + /**
1417 + * Returns the data for the query.
1418 + *
1419 + * @access public
1420 + */
1421 + public function getQueryData() {
1422 + check_ajax_referer( Visualizer_Plugin::ACTION_FETCH_DB_DATA . Visualizer_Plugin::VERSION, 'security' );
1423 +
1424 + $params = wp_parse_args( $_POST['params'] );
1425 + $chart_id = filter_var( $params['chart_id'], FILTER_VALIDATE_INT );
1426 +
1427 + $source = new Visualizer_Source_Query( stripslashes( $params['query'] ), $chart_id, $params );
1428 + $html = $source->fetch( true );
1429 + $error = $source->get_error();
1430 + if ( ! empty( $error ) ) {
1431 + wp_send_json_error( array( 'msg' => $error ) );
1432 + }
1433 + wp_send_json_success( array( 'table' => $html ) );
1434 + }
1435 +
1436 + /**
1437 + * Saves the query and the schedule.
1438 + *
1439 + * @access public
1440 + */
1441 + public function saveQuery() {
1442 + check_ajax_referer( Visualizer_Plugin::ACTION_SAVE_DB_QUERY . Visualizer_Plugin::VERSION, 'security' );
1443 +
1444 + $chart_id = filter_input(
1445 + INPUT_GET,
1446 + 'chart',
1447 + FILTER_VALIDATE_INT,
1448 + array(
1449 + 'options' => array(
1450 + 'min_range' => 1,
1451 + ),
1452 + )
1453 + );
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 +
1471 + $render = new Visualizer_Render_Page_Update();
1472 + if ( $chart_id ) {
1473 + $params = wp_parse_args( $_POST['params'] );
1474 + $source = new Visualizer_Source_Query( stripslashes( $params['query'] ), $chart_id, $params );
1475 + $source->fetch( false );
1476 + $error = $source->get_error();
1477 + if ( empty( $error ) ) {
1478 + update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY, stripslashes( $params['query'] ) );
1479 + update_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
1480 + update_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
1481 + update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_SCHEDULE, $hours );
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 + }
1489 +
1490 + $schedules = get_option( Visualizer_Plugin::CF_DB_SCHEDULE, array() );
1491 + $schedules[ $chart_id ] = time() + $hours * HOUR_IN_SECONDS;
1492 + update_option( Visualizer_Plugin::CF_DB_SCHEDULE, $schedules );
1493 +
1494 + wp_update_post(
1495 + array(
1496 + 'ID' => $chart_id,
1497 + 'post_content' => $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ),
1498 + )
1499 + );
1500 + $render->data = json_encode( $source->getRawData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ) );
1501 + $render->series = json_encode( $source->getSeries() );
1502 + $render->id = $chart_id;
1503 + } else {
1504 + $render->message = $error;
1505 + }
1506 + }
1507 + $render->render();
1508 + if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
1509 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
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;
1607 + }
626 1608 }