PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.7.0
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.7.0
4.0.8 4.0.7 4.0.6 4.0.5 4.0.4 4.0.3 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.1 3.1.2 3.1.3 3.10.0 3.10.1 3.10.10 3.10.11 3.10.12 3.10.13 3.10.14 3.10.15 3.10.2 3.10.3 All 149 releases
← All changes | classes/Visualizer/Module/Chart.php +730 -83 3.1.03.7.0 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 ) {
@@ -629,14 +1139,30 @@
629 1139 wp_update_post( $chart->to_array() );
630 1140 update_post_meta( $chart->ID, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
631 1141 update_post_meta( $chart->ID, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
632 1142 update_post_meta( $chart->ID, Visualizer_Plugin::CF_DEFAULT_DATA, 0 );
1143 +
1144 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Updated post for chart %d', $chart_id ), 'debug', __FILE__, __LINE__ );
1145 +
1146 + Visualizer_Module_Utility::set_defaults( $chart, null );
1147 +
1148 + $settings = get_post_meta( $chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
1149 +
633 1150 $render->id = $chart->ID;
634 - $render->data = json_encode( $source->getRawData() );
1151 + $render->data = json_encode( $source->getRawData( get_post_meta( $chart->ID, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ) );
635 1152 $render->series = json_encode( $source->getSeries() );
1153 + $render->settings = json_encode( $settings );
636 1154 } else {
637 - $render->message = esc_html__( 'CSV file is broken or invalid. Please, try again.', 'visualizer' );
1155 + $error = $source->get_error();
1156 + if ( empty( $error ) ) {
1157 + $error = esc_html__( 'CSV file is broken or invalid. Please try again.', 'visualizer' );
1158 + }
1159 + $render->message = $error;
1160 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( '%s for chart %d.', $error, $chart_id ), 'error', __FILE__, __LINE__ );
1161 + update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, $error );
638 1162 }
1163 + } else {
1164 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Unknown internal error for chart %d.', $chart_id ), 'error', __FILE__, __LINE__ );
639 1165 }
640 1166 $render->render();
641 1167 if ( ! $can_die ) {
642 1168 return;
@@ -652,27 +1178,18 @@
652 1178 * @access public
653 1179 */
654 1180 public function cloneChart() {
655 1181 $chart_id = $success = false;
656 - $nonce = wp_verify_nonce( filter_input( INPUT_GET, 'nonce' ), Visualizer_Plugin::ACTION_CLONE_CHART );
1182 + $nonce = isset( $_GET['nonce'] ) && wp_verify_nonce( $_GET['nonce'], Visualizer_Plugin::ACTION_CLONE_CHART );
657 1183 $capable = current_user_can( 'edit_posts' );
658 1184 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 - );
1185 + $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
669 1186 if ( $chart_id ) {
670 1187 $chart = get_post( $chart_id );
671 - $success = $chart && $chart->post_type == Visualizer_Plugin::CPT_VISUALIZER;
1188 + $success = $chart && $chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
672 1189 }
673 1190 }
674 - $redirect = wp_get_referer();
1191 + $redirect = remove_query_arg( 'vaction', wp_get_referer() );
675 1192 if ( $success ) {
676 1193 $new_chart_id = wp_insert_post(
677 1194 array(
678 1195 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
@@ -681,23 +1198,32 @@
681 1198 'post_status' => $chart->post_status,
682 1199 'post_content' => $chart->post_content,
683 1200 )
684 1201 );
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 ) );
1202 + if ( is_wp_error( $new_chart_id ) ) {
1203 + 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__ );
1204 + } else {
1205 + $post_meta = get_post_meta( $chart_id );
1206 + foreach ( $post_meta as $key => $value ) {
1207 + if ( strpos( $key, 'visualizer-' ) !== false ) {
1208 + add_post_meta( $new_chart_id, $key, maybe_unserialize( $value[0] ) );
1209 + }
1210 + }
691 1211 $redirect = add_query_arg(
692 1212 array(
693 1213 'page' => 'visualizer',
694 1214 'type' => filter_input( INPUT_GET, 'type' ),
1215 + 'vaction' => false,
695 1216 ),
696 - admin_url( 'upload.php' )
1217 + admin_url( 'admin.php' )
697 1218 );
698 1219 }
699 1220 }
1221 +
1222 + if ( defined( 'WP_TESTS_DOMAIN' ) ) {
1223 + wp_die();
1224 + }
1225 +
700 1226 wp_redirect( $redirect );
701 1227 exit;
702 1228 }
703 1229
@@ -743,9 +1269,12 @@
743 1269 $data = $this->_getChartArray();
744 1270 $render = new Visualizer_Render_Page_Data();
745 1271 $render->chart = $this->_chart;
746 1272 $render->type = $data['type'];
747 - unset( $data['settings']['width'], $data['settings']['height'] );
1273 +
1274 + if ( $data && $data['settings'] ) {
1275 + unset( $data['settings']['width'], $data['settings']['height'], $data['settings']['chartArea'] );
1276 + }
748 1277 wp_enqueue_style( 'visualizer-frame' );
749 1278 wp_enqueue_script( 'visualizer-render' );
750 1279 wp_localize_script(
751 1280 'visualizer-render',
@@ -751,9 +1280,9 @@
751 1280 'visualizer-render',
752 1281 'visualizer',
753 1282 array(
754 1283 'l10n' => array(
755 - 'invalid_source' => esc_html__( 'You have entered invalid URL. Please, insert proper URL.', 'visualizer' ),
1284 + 'invalid_source' => esc_html__( 'You have entered an invalid URL. Please provide a valid URL.', 'visualizer' ),
756 1285 'loading' => esc_html__( 'Loading...', 'visualizer' ),
757 1286 ),
758 1287 'charts' => array(
759 1288 'canvas' => $data,
@@ -759,13 +1288,16 @@
759 1288 'canvas' => $data,
760 1289 ),
761 1290 )
762 1291 );
763 - // Added by Ash/Upwork
764 - if ( VISUALIZER_PRO ) {
1292 +
1293 + do_action( 'visualizer_enqueue_scripts_and_styles', $data, $this->_chart->ID );
1294 +
1295 + if ( Visualizer_Module::is_pro() && Visualizer_Module::is_pro_older_than( '1.9.0' ) ) {
765 1296 global $Visualizer_Pro;
766 - $Visualizer_Pro->_enqueueScriptsAndStyles( $data );
1297 + $Visualizer_Pro->_enqueueScriptsAndStyles( $data, $this->_chart->ID );
767 1298 }
1299 +
768 1300 // Added by Ash/Upwork
769 1301 $this->_addAction( 'admin_head', 'renderFlattrScript' );
770 1302 wp_iframe( array( $render, 'render' ) );
771 1303 }
@@ -778,13 +1310,14 @@
778 1310 public function getQueryData() {
779 1311 check_ajax_referer( Visualizer_Plugin::ACTION_FETCH_DB_DATA . Visualizer_Plugin::VERSION, 'security' );
780 1312
781 1313 $params = wp_parse_args( $_POST['params'] );
782 - $source = new Visualizer_Source_Query( stripslashes( $params['query'] ) );
1314 + $chart_id = filter_var( $params['chart_id'], FILTER_VALIDATE_INT );
1315 +
1316 + $source = new Visualizer_Source_Query( stripslashes( $params['query'] ), $chart_id, $params );
783 1317 $html = $source->fetch( true );
784 - $error = '';
785 - if ( empty( $html ) ) {
786 - $error = $source->get_error();
1318 + $error = $source->get_error();
1319 + if ( ! empty( $error ) ) {
787 1320 wp_send_json_error( array( 'msg' => $error ) );
788 1321 }
789 1322 wp_send_json_success( array( 'table' => $html ) );
790 1323 }
@@ -807,21 +1340,42 @@
807 1340 ),
808 1341 )
809 1342 );
810 1343
1344 + $hours = filter_input(
1345 + INPUT_POST,
1346 + 'refresh',
1347 + FILTER_VALIDATE_FLOAT,
1348 + array(
1349 + 'options' => array(
1350 + 'min_range' => -1,
1351 + 'max_range' => apply_filters( 'visualizer_is_business', false ) ? PHP_INT_MAX : -1,
1352 + ),
1353 + )
1354 + );
1355 +
1356 + if ( ! is_numeric( $hours ) ) {
1357 + $hours = -1;
1358 + }
1359 +
811 1360 $render = new Visualizer_Render_Page_Update();
812 1361 if ( $chart_id ) {
813 1362 $params = wp_parse_args( $_POST['params'] );
814 - $source = new Visualizer_Source_Query( stripslashes( $params['query'] ) );
1363 + $source = new Visualizer_Source_Query( stripslashes( $params['query'] ), $chart_id, $params );
815 1364 $source->fetch( false );
816 1365 $error = $source->get_error();
817 1366 if ( empty( $error ) ) {
818 - $hours = $_POST['refresh'];
819 1367 update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY, stripslashes( $params['query'] ) );
820 1368 update_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
821 1369 update_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
822 1370 update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_SCHEDULE, $hours );
823 1371 update_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, 0 );
1372 + if ( isset( $params['db_type'] ) && $params['db_type'] !== Visualizer_Plugin::WP_DB_NAME ) {
1373 + $remote_db_params = $params;
1374 + unset( $remote_db_params['query'] );
1375 + unset( $remote_db_params['chart_id'] );
1376 + update_post_meta( $chart_id, Visualizer_Plugin::CF_REMOTE_DB_PARAMS, $remote_db_params );
1377 + }
824 1378
825 1379 $schedules = get_option( Visualizer_Plugin::CF_DB_SCHEDULE, array() );
826 1380 $schedules[ $chart_id ] = time() + $hours * HOUR_IN_SECONDS;
827 1381 update_option( Visualizer_Plugin::CF_DB_SCHEDULE, $schedules );
@@ -828,13 +1382,14 @@
828 1382
829 1383 wp_update_post(
830 1384 array(
831 1385 'ID' => $chart_id,
832 - 'post_content' => $source->getData(),
1386 + 'post_content' => $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ),
833 1387 )
834 1388 );
835 - $render->data = json_encode( $source->getRawData() );
1389 + $render->data = json_encode( $source->getRawData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ) );
836 1390 $render->series = json_encode( $source->getSeries() );
1391 + $render->id = $chart_id;
837 1392 } else {
838 1393 $render->message = $error;
839 1394 }
840 1395 }
@@ -841,6 +1396,98 @@
841 1396 $render->render();
842 1397 if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
843 1398 defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
844 1399 }
1400 + }
1401 +
1402 +
1403 + /**
1404 + * Saves the filter query and the schedule.
1405 + *
1406 + * @access public
1407 + */
1408 + public function saveFilter() {
1409 + check_ajax_referer( Visualizer_Plugin::ACTION_SAVE_FILTER_QUERY . Visualizer_Plugin::VERSION, 'security' );
1410 +
1411 + $chart_id = filter_input(
1412 + INPUT_GET,
1413 + 'chart',
1414 + FILTER_VALIDATE_INT,
1415 + array(
1416 + 'options' => array(
1417 + 'min_range' => 1,
1418 + ),
1419 + )
1420 + );
1421 +
1422 + $hours = filter_input(
1423 + INPUT_POST,
1424 + 'refresh',
1425 + FILTER_VALIDATE_INT,
1426 + array(
1427 + 'options' => array(
1428 + 'min_range' => -1,
1429 + 'max_range' => apply_filters( 'visualizer_is_business', false ) ? PHP_INT_MAX : -1,
1430 + ),
1431 + )
1432 + );
1433 +
1434 + if ( 0 !== $hours && empty( $hours ) ) {
1435 + $hours = -1;
1436 + }
1437 +
1438 + do_action( 'visualizer_save_filter', $chart_id, $hours );
1439 +
1440 + if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
1441 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1442 + }
1443 + }
1444 +
1445 + /**
1446 + * Save chart image.
1447 + *
1448 + * @param string $base64_img Chart image.
1449 + * @param int $chart_id Chart ID.
1450 + * @param bool $save_attachment Save attachment.
1451 + * @return attachment ID
1452 + */
1453 + public function save_chart_image( $base64_img, $chart_id, $save_attachment = true ) {
1454 + // Delete old chart image.
1455 + $old_attachment_id = get_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_IMAGE, true );
1456 + if ( $old_attachment_id ) {
1457 + wp_delete_attachment( $old_attachment_id, true );
1458 + }
1459 +
1460 + if ( ! $save_attachment ) {
1461 + return 0;
1462 + }
1463 +
1464 + // Upload dir.
1465 + $upload_dir = wp_upload_dir();
1466 + $upload_path = str_replace( '/', DIRECTORY_SEPARATOR, $upload_dir['path'] ) . DIRECTORY_SEPARATOR;
1467 +
1468 + $img = str_replace( 'data:image/png;base64,', '', $base64_img );
1469 + $img = str_replace( ' ', '+', $img );
1470 + $decoded = base64_decode( $img );
1471 + $filename = 'visualization-' . $chart_id . '.png';
1472 + $file_type = 'image/png';
1473 + $hashed_filename = $filename;
1474 +
1475 + // Save the image in the uploads directory.
1476 + require_once ABSPATH . '/wp-admin/includes/file.php';
1477 + \WP_Filesystem();
1478 + global $wp_filesystem;
1479 + $upload_file = $wp_filesystem->put_contents( $upload_path . $hashed_filename, $decoded );
1480 +
1481 + // Insert new chart image.
1482 + $attachment = array(
1483 + 'post_mime_type' => $file_type,
1484 + 'post_title' => preg_replace( '/\.[^.]+$/', '', basename( $hashed_filename ) ),
1485 + 'post_content' => '',
1486 + 'post_status' => 'inherit',
1487 + 'guid' => $upload_dir['url'] . '/' . basename( $hashed_filename ),
1488 + );
1489 +
1490 + $attach_id = wp_insert_attachment( $attachment, $upload_dir['path'] . '/' . $hashed_filename );
1491 + return $attach_id;
845 1492 }
846 1493 }