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