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