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