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