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