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