PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.6.1
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.6.1
4.0.7 4.0.6 4.0.5 4.0.4 4.0.3 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.1 3.1.2 3.1.3 3.10.0 3.10.1 3.10.10 3.10.11 3.10.12 3.10.13 3.10.14 3.10.15 3.10.2 3.10.3 3.10.4 All 148 releases
← All changes | classes/Visualizer/Module/Chart.php +715 -83 3.1.13.6.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,29 @@
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 + if ( isset( $_POST['chart-img'] ) && ! empty( $_POST['chart-img'] ) ) {
531 + $attachment_id = $this->save_chart_image( $_POST['chart-img'], $chart_id );
532 + if ( $attachment_id ) {
533 + update_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_IMAGE, $attachment_id );
534 + }
535 + }
536 + $lib = $this->load_chart_type( $chart_id );
280 537
538 + // the alpha color picker (RGBA) is not supported by google.
539 + $color_picker_dep = 'wp-color-picker';
540 + if ( in_array( $lib, array( 'chartjs', 'datatables' ), true ) && ! wp_script_is( 'wp-color-picker-alpha', 'registered' ) ) {
541 + wp_register_script( 'wp-color-picker-alpha', VISUALIZER_ABSURL . 'js/lib/wp-color-picker-alpha.min.js', array( 'wp-color-picker' ), Visualizer_Plugin::VERSION );
542 + $color_picker_dep = 'wp-color-picker-alpha';
543 + }
544 +
281 545 // enqueue and register scripts and styles
282 546 wp_register_script( 'visualizer-chosen', VISUALIZER_ABSURL . 'js/lib/chosen.jquery.min.js', array( 'jquery' ), Visualizer_Plugin::VERSION );
283 547 wp_register_style( 'visualizer-chosen', VISUALIZER_ABSURL . 'css/lib/chosen.min.css', array(), Visualizer_Plugin::VERSION );
284 548
285 549 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 );
550 + wp_register_script( 'visualizer-frame', VISUALIZER_ABSURL . 'js/frame.js', array( 'visualizer-chosen', 'jquery-ui-accordion', 'jquery-ui-tabs' ), Visualizer_Plugin::VERSION, true );
287 551 wp_register_script( 'visualizer-customization', $this->get_user_customization_js(), array(), null, true );
288 552 wp_register_script(
289 553 'visualizer-render',
290 554 VISUALIZER_ABSURL . 'js/render-facade.js',
@@ -295,19 +559,23 @@
295 559 wp_register_script(
296 560 'visualizer-preview',
297 561 VISUALIZER_ABSURL . 'js/preview.js',
298 562 array(
299 - 'wp-color-picker',
563 + $color_picker_dep,
300 564 'visualizer-render',
301 565 ),
302 566 Visualizer_Plugin::VERSION,
303 567 true
304 568 );
305 - // added by Ash/Upwork
306 - if ( VISUALIZER_PRO ) {
569 + wp_register_script( 'visualizer-editor-simple', VISUALIZER_ABSURL . 'js/simple-editor.js', array( 'jquery' ), Visualizer_Plugin::VERSION, true );
570 +
571 + do_action( 'visualizer_add_scripts' );
572 +
573 + if ( Visualizer_Module::is_pro() && Visualizer_Module::is_pro_older_than( '1.9.0' ) ) {
307 574 global $Visualizer_Pro;
308 575 $Visualizer_Pro->_addScriptsAndStyles();
309 576 }
577 +
310 578 // dispatch pages
311 579 $this->_chart = get_post( $chart_id );
312 580 $tab = isset( $_GET['tab'] ) && ! empty( $_GET['tab'] ) ? $_GET['tab'] : 'visualizer';
313 581
@@ -319,8 +587,9 @@
319 587 if ( isset( $_POST['cancel'] ) && 1 === intval( $_POST['cancel'] ) ) {
320 588 // if the cancel button is clicked.
321 589 $this->undoRevisions( $chart_id, true );
322 590 } elseif ( isset( $_POST['save'] ) && 1 === intval( $_POST['save'] ) ) {
591 + $this->handlePermissions();
323 592 // if the save button is clicked.
324 593 $this->undoRevisions( $chart_id, false );
325 594 } else {
326 595 // if the edit button is clicked.
@@ -335,9 +604,9 @@
335 604 case 'visualizer': // fall through.
336 605 $this->_handleTypesPage();
337 606 break;
338 607 default:
339 - do_action( 'visualizer_pro_handle_tab', $tab, $this->_chart );
608 + // this should never happen.
340 609 break;
341 610 }
342 611 defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
343 612 }
@@ -344,25 +613,26 @@
344 613
345 614 /**
346 615 * Load code editor assets.
347 616 */
348 - private function loadCodeEditorAssets() {
617 + private function loadCodeEditorAssets( $chart_id ) {
349 618 global $wp_version;
350 619
351 - if ( ! VISUALIZER_PRO ) {
620 + $wp_scripts = wp_scripts();
621 +
622 + // data tables assets.
623 + wp_register_script( 'visualizer-datatables', VISUALIZER_ABSURL . 'js/lib/datatables.min.js', array( 'jquery-ui-core' ), Visualizer_Plugin::VERSION );
624 + wp_register_style( 'visualizer-datatables', VISUALIZER_ABSURL . 'css/lib/datatables.min.css', array(), Visualizer_Plugin::VERSION );
625 + 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 );
626 + wp_enqueue_script( 'visualizer-datatables' );
627 + wp_enqueue_style( 'visualizer-jquery-ui' );
628 +
629 + if ( ! Visualizer_Module::is_pro() ) {
352 630 return;
353 631 }
354 632
355 - $table_col_mapping = Visualizer_Source_Query_Params::get_all_db_tables_column_mapping();
633 + $table_col_mapping = Visualizer_Source_Query_Params::get_all_db_tables_column_mapping( $chart_id );
356 634
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 635 if ( version_compare( $wp_version, '4.9.0', '<' ) ) {
366 636 // code mirror assets.
367 637 wp_register_script( 'visualizer-codemirror-core', '//codemirror.net/lib/codemirror.js', array( 'jquery' ), Visualizer_Plugin::VERSION );
368 638 wp_register_script( 'visualizer-codemirror-placeholder', '//codemirror.net/addon/display/placeholder.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
@@ -396,8 +666,25 @@
396 666 return $table_col_mapping;
397 667 }
398 668
399 669 /**
670 + * Handle permissions from the new conslidated settings sidebar.
671 + */
672 + private function handlePermissions() {
673 + if ( ! Visualizer_Module::is_pro() ) {
674 + return;
675 + }
676 +
677 + // we will not support old free and new pro.
678 + // handling new free and old pro.
679 + if ( has_action( 'visualizer_pro_handle_tab' ) ) {
680 + do_action( 'visualizer_pro_handle_tab', 'permissions', $this->_chart );
681 + } else {
682 + do_action( 'visualizer_handle_permissions', $this->_chart );
683 + }
684 + }
685 +
686 + /**
400 687 * Handle data and settings page
401 688 */
402 689 private function _handleDataAndSettingsPage() {
403 690 if ( isset( $_POST['map_api_key'] ) ) {
@@ -402,30 +689,47 @@
402 689 private function _handleDataAndSettingsPage() {
403 690 if ( isset( $_POST['map_api_key'] ) ) {
404 691 update_option( 'visualizer-map-api-key', $_POST['map_api_key'] );
405 692 }
406 - if ( $_SERVER['REQUEST_METHOD'] == 'POST' && isset( $_GET['nonce'] ) && wp_verify_nonce( $_GET['nonce'] ) ) {
407 - if ( $this->_chart->post_status == 'auto-draft' ) {
693 +
694 + if ( $_SERVER['REQUEST_METHOD'] === 'POST' && isset( $_GET['nonce'] ) && wp_verify_nonce( $_GET['nonce'] ) ) {
695 + if ( $this->_chart->post_status === 'auto-draft' ) {
408 696 $this->_chart->post_status = 'publish';
409 697
410 698 // 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 699 // we do not want any difference in data so disable revisions temporarily.
412 - add_filter( 'wp_revisions_to_keep', '__return_false' );
700 + $this->disableRevisionsTemporarily();
701 +
413 702 wp_update_post( $this->_chart->to_array() );
414 703 }
415 704 // save meta data only when it is NOT being canceled.
416 705 if ( ! ( isset( $_POST['cancel'] ) && 1 === intval( $_POST['cancel'] ) ) ) {
417 706 update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, $_POST );
707 +
708 + // we will keep a parameter called 'internal_title' that will be set to the given title or, if empty, the chart ID
709 + // this will help in searching with the chart id.
710 + $settings = get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
711 + $title = null;
712 + if ( isset( $settings['title'] ) && ! empty( $settings['title'] ) ) {
713 + $title = $settings['title'];
714 + if ( is_array( $title ) ) {
715 + $title = $settings['title']['text'];
716 + }
717 + }
718 + if ( empty( $title ) ) {
719 + $title = $this->_chart->ID;
720 + }
721 + $settings['internal_title'] = $title;
722 + update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, $settings );
418 723 }
419 724 $render = new Visualizer_Render_Page_Send();
420 725 $render->text = sprintf( '[visualizer id="%d"]', $this->_chart->ID );
421 726 wp_iframe( array( $render, 'render' ) );
422 -
423 727 return;
424 728 }
425 729 $data = $this->_getChartArray();
426 730 $sidebar = '';
427 - $sidebar_class = 'Visualizer_Render_Sidebar_Type_' . ucfirst( $data['type'] );
731 + $sidebar_class = $this->load_chart_class_name( $this->_chart->ID );
428 732 if ( class_exists( $sidebar_class, true ) ) {
429 733 $sidebar = new $sidebar_class( $data['settings'] );
430 734 $sidebar->__series = $data['series'];
431 735 $sidebar->__data = $data['data'];
@@ -430,14 +734,14 @@
430 734 $sidebar->__series = $data['series'];
431 735 $sidebar->__data = $data['data'];
432 736 } else {
433 737 $sidebar = apply_filters( 'visualizer_pro_chart_type_sidebar', '', $data );
434 - if ( $sidebar != '' ) {
738 + if ( $sidebar !== '' ) {
435 739 $sidebar->__series = $data['series'];
436 740 $sidebar->__data = $data['data'];
437 741 }
438 742 }
439 - unset( $data['settings']['width'], $data['settings']['height'] );
743 + unset( $data['settings']['width'], $data['settings']['height'], $data['settings']['chartArea'] );
440 744 wp_enqueue_style( 'wp-color-picker' );
441 745 wp_enqueue_style( 'visualizer-frame' );
442 746 wp_enqueue_script( 'visualizer-preview' );
443 747 wp_enqueue_script( 'visualizer-chosen' );
@@ -442,17 +746,37 @@
442 746 wp_enqueue_script( 'visualizer-preview' );
443 747 wp_enqueue_script( 'visualizer-chosen' );
444 748 wp_enqueue_script( 'visualizer-render' );
445 749
446 - $table_col_mapping = $this->loadCodeEditorAssets();
750 + if ( Visualizer_Module::can_show_feature( 'simple-editor' ) ) {
751 + wp_enqueue_script( 'visualizer-editor-simple' );
752 + wp_localize_script(
753 + 'visualizer-editor-simple',
754 + 'visualizer1',
755 + array(
756 + 'ajax' => array(
757 + 'url' => admin_url( 'admin-ajax.php' ),
758 + 'nonces' => array(
759 + ),
760 + 'actions' => array(
761 + ),
762 + ),
763 + )
764 + );
765 + }
447 766
767 + $table_col_mapping = $this->loadCodeEditorAssets( $this->_chart->ID );
768 +
448 769 wp_localize_script(
449 770 'visualizer-render',
450 771 'visualizer',
451 772 array(
452 773 'l10n' => array(
453 - 'invalid_source' => esc_html__( 'You have entered invalid URL. Please, insert proper URL.', 'visualizer' ),
774 + 'invalid_source' => esc_html__( 'You have entered an invalid URL. Please provide a valid URL.', 'visualizer' ),
454 775 'loading' => esc_html__( 'Loading...', 'visualizer' ),
776 + 'json_error' => esc_html__( 'An error occured in fetching data.', 'visualizer' ),
777 + 'select_columns' => esc_html__( 'Please select a few columns to include in the chart.', 'visualizer' ),
778 + '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 779 ),
456 780 'charts' => array(
457 781 'canvas' => $data,
458 782 'id' => $this->_chart->ID,
@@ -463,19 +787,28 @@
463 787 'url' => admin_url( 'admin-ajax.php' ),
464 788 'nonces' => array(
465 789 'permissions' => wp_create_nonce( Visualizer_Plugin::ACTION_FETCH_PERMISSIONS_DATA ),
466 790 'db_get_data' => wp_create_nonce( Visualizer_Plugin::ACTION_FETCH_DB_DATA . Visualizer_Plugin::VERSION ),
791 + 'json_get_roots' => wp_create_nonce( Visualizer_Plugin::ACTION_JSON_GET_ROOTS . Visualizer_Plugin::VERSION ),
792 + 'json_get_data' => wp_create_nonce( Visualizer_Plugin::ACTION_JSON_GET_DATA . Visualizer_Plugin::VERSION ),
793 + 'json_set_schedule' => wp_create_nonce( Visualizer_Plugin::ACTION_JSON_SET_SCHEDULE . Visualizer_Plugin::VERSION ),
467 794 ),
468 795 'actions' => array(
469 796 'permissions' => Visualizer_Plugin::ACTION_FETCH_PERMISSIONS_DATA,
470 797 'db_get_data' => Visualizer_Plugin::ACTION_FETCH_DB_DATA,
798 + 'json_get_roots' => Visualizer_Plugin::ACTION_JSON_GET_ROOTS,
799 + 'json_get_data' => Visualizer_Plugin::ACTION_JSON_GET_DATA,
800 + 'json_set_schedule' => Visualizer_Plugin::ACTION_JSON_SET_SCHEDULE,
471 801 ),
472 802 ),
473 803 'db_query' => array(
474 804 'tables' => $table_col_mapping,
475 805 ),
476 - 'is_pro' => VISUALIZER_PRO,
806 + 'is_pro' => Visualizer_Module::is_pro(),
477 807 'page_type' => 'chart',
808 + 'json_tag_separator' => Visualizer_Source_Json::TAG_SEPARATOR,
809 + 'json_tag_separator_view' => Visualizer_Source_Json::TAG_SEPARATOR_VIEW,
810 + 'is_front' => false,
478 811 )
479 812 );
480 813
481 814 $render = new Visualizer_Render_Page_Data();
@@ -483,21 +816,25 @@
483 816 $render->type = $data['type'];
484 817 $render->custom_css = $data['css'];
485 818 $render->sidebar = $sidebar;
486 819 if ( filter_input( INPUT_GET, 'library', FILTER_VALIDATE_BOOLEAN ) ) {
487 - $render->button = filter_input( INPUT_GET, 'action' ) == Visualizer_Plugin::ACTION_EDIT_CHART
820 + $render->button = filter_input( INPUT_GET, 'action' ) === Visualizer_Plugin::ACTION_EDIT_CHART
488 821 ? esc_html__( 'Save Chart', 'visualizer' )
489 822 : esc_html__( 'Create Chart', 'visualizer' );
490 - if ( filter_input( INPUT_GET, 'action' ) == Visualizer_Plugin::ACTION_EDIT_CHART ) {
823 + if ( filter_input( INPUT_GET, 'action' ) === Visualizer_Plugin::ACTION_EDIT_CHART ) {
491 824 $render->cancel_button = esc_html__( 'Cancel', 'visualizer' );
492 825 }
493 826 } else {
494 827 $render->button = esc_attr__( 'Insert Chart', 'visualizer' );
495 828 }
496 - if ( VISUALIZER_PRO ) {
829 +
830 + do_action( 'visualizer_enqueue_scripts_and_styles', $data, $this->_chart->ID );
831 +
832 + if ( Visualizer_Module::is_pro() && Visualizer_Module::is_pro_older_than( '1.9.0' ) ) {
497 833 global $Visualizer_Pro;
498 - $Visualizer_Pro->_enqueueScriptsAndStyles( $data );
834 + $Visualizer_Pro->_enqueueScriptsAndStyles( $data, $this->_chart->ID );
499 835 }
836 +
500 837 $this->_addAction( 'admin_head', 'renderFlattrScript' );
501 838 wp_iframe( array( $render, 'render' ) );
502 839 }
503 840
@@ -509,21 +846,32 @@
509 846 * @access private
510 847 */
511 848 private function _handleTypesPage() {
512 849 // process post request
513 - if ( $_SERVER['REQUEST_METHOD'] == 'POST' && wp_verify_nonce( filter_input( INPUT_POST, 'nonce' ) ) ) {
850 + if ( $_SERVER['REQUEST_METHOD'] === 'POST' && wp_verify_nonce( filter_input( INPUT_POST, 'nonce' ) ) ) {
514 851 $type = filter_input( INPUT_POST, 'type' );
515 - if ( in_array( $type, Visualizer_Plugin::getChartTypes() ) ) {
852 + $library = filter_input( INPUT_POST, 'chart-library' );
853 + if ( in_array( $type, Visualizer_Plugin::getChartTypes(), true ) ) {
854 + if ( empty( $library ) ) {
855 + // library cannot be empty.
856 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, 'Chart library empty while creating the chart! Aborting...', 'error', __FILE__, __LINE__ );
857 + return;
858 + }
859 +
516 860 // save new chart type
517 861 update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_CHART_TYPE, $type );
862 + update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_CHART_LIBRARY, $library );
518 863 // if the chart has default data, update it with appropriate default data for new type
519 864 if ( filter_var( get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_DEFAULT_DATA, true ), FILTER_VALIDATE_BOOLEAN ) ) {
520 865 $source = new Visualizer_Source_Csv( VISUALIZER_ABSPATH . DIRECTORY_SEPARATOR . 'samples' . DIRECTORY_SEPARATOR . $type . '.csv' );
521 866 $source->fetch();
522 - $this->_chart->post_content = $source->getData();
867 + $this->_chart->post_content = $source->getData( get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) );
523 868 wp_update_post( $this->_chart->to_array() );
524 869 update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
525 870 }
871 +
872 + Visualizer_Module_Utility::set_defaults( $this->_chart );
873 +
526 874 // redirect to next tab
527 875 // changed by Ash/Upwork
528 876 wp_redirect( add_query_arg( 'tab', 'settings' ) );
529 877
@@ -531,9 +879,9 @@
531 879 }
532 880 }
533 881 $render = new Visualizer_Render_Page_Types();
534 882 $render->type = get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_CHART_TYPE, true );
535 - $render->types = Visualizer_Module_Admin::_getChartTypesLocalized();
883 + $render->types = Visualizer_Module_Admin::_getChartTypesLocalized( false, false, false, 'types' );
536 884 $render->chart = $this->_chart;
537 885 wp_enqueue_style( 'visualizer-frame' );
538 886 wp_enqueue_script( 'visualizer-frame' );
539 887 wp_iframe( array( $render, 'render' ) );
@@ -549,10 +897,131 @@
549 897 */
550 898 public function renderFlattrScript() {
551 899 echo '';
552 900 }
553 - // changed by Ash/Upwork
901 +
554 902 /**
903 + * Processes the CSV that is sent in the request as a string.
904 + *
905 + * @since 3.2.0
906 + */
907 + private function handleCSVasString( $data, $editor_type ) {
908 + $source = null;
909 +
910 + switch ( $editor_type ) {
911 + case 'text':
912 + // data coming in from the text editor.
913 + $tmpfile = tempnam( get_temp_dir(), Visualizer_Plugin::NAME );
914 + $handle = fopen( $tmpfile, 'w' );
915 + $values = preg_split( '/[\n\r]+/', stripslashes( trim( $data ) ) );
916 + if ( $values ) {
917 + foreach ( $values as $row ) {
918 + if ( empty( $row ) ) {
919 + continue;
920 + }
921 + $row = explode( ',', $row );
922 + $row = array_map(
923 + function( $r ) {
924 + return '' === $r ? ' ' : $r;
925 + },
926 + $row
927 + );
928 + $row = implode( ',', $row );
929 + // don't use fpucsv here because we need to just dump the data
930 + // minus the empty rows
931 + // because fputcsv needs to tokenize
932 + // we can standardize the CSV enclosure here and replace all ' with "
933 + // we can assume that if there are an even number of ' they should be changed to "
934 + // but that will screw up words like fo'c'sle
935 + // so here let's just assume ' will NOT be used for enclosures
936 + fwrite( $handle, $row );
937 + fwrite( $handle, PHP_EOL );
938 + }
939 + }
940 + $source = new Visualizer_Source_Csv( $tmpfile );
941 + fclose( $handle );
942 + break;
943 + case 'table':
944 + // Import from Chart
945 + // fall-through.
946 + case 'excel':
947 + // data coming in from the excel editor.
948 + $source = apply_filters( 'visualizer_pro_handle_chart_data', $data, '' );
949 + break;
950 + }
951 + return $source;
952 + }
953 +
954 + /**
955 + * Parses the data uploaded as an HTML table.
956 + *
957 + * @since 3.2.0
958 + *
959 + * @access private
960 + */
961 + private function handleTabularData() {
962 + $csv = array();
963 + // the datatable mentions the headers twice, so lets remove the duplicates.
964 + $headers = array_unique( array_filter( $_POST['header'] ) );
965 + $types = $_POST['type'];
966 +
967 + // capture all the indexes that correspond to excluded columns.
968 + $exclude = array();
969 + $index = 0;
970 + foreach ( $types as $type ) {
971 + if ( empty( $type ) ) {
972 + $exclude[] = $index;
973 + }
974 + $index++;
975 + }
976 +
977 + // when N headers are being renamed, the number of headers increases by N
978 + // because of the way datatable duplicates header information
979 + // so unset the headers that have been renamed.
980 + if ( count( $headers ) !== count( $types ) ) {
981 + $to = count( $headers );
982 + for ( $i = count( $types ); $i < $to; $i++ ) {
983 + unset( $headers[ $i + 1 ] );
984 + }
985 + }
986 +
987 + $columns = array();
988 + for ( $i = 0; $i < count( $headers ); $i++ ) {
989 + if ( ! isset( $_POST[ 'data' . $i ] ) ) {
990 + continue;
991 + }
992 + $columns[ $i ] = $_POST[ 'data' . $i ];
993 + }
994 +
995 + $csv[] = $headers;
996 + $csv[] = $types;
997 + for ( $j = 0; $j < count( $columns[0] ); $j++ ) {
998 + $row = array();
999 + for ( $i = 0; $i < count( $headers ); $i++ ) {
1000 + $row[] = sanitize_text_field( $columns[ $i ][ $j ] );
1001 + }
1002 + $csv[] = $row;
1003 + }
1004 +
1005 + $tmpfile = tempnam( get_temp_dir(), Visualizer_Plugin::NAME );
1006 + $handle = fopen( $tmpfile, 'w' );
1007 +
1008 + if ( $csv ) {
1009 + $index = 0;
1010 + foreach ( $csv as $row ) {
1011 + // remove all the cells corresponding to the excluded headers.
1012 + foreach ( $exclude as $j ) {
1013 + unset( $row[ $j ] );
1014 + }
1015 + fputcsv( $handle, $row );
1016 + }
1017 + }
1018 + $source = new Visualizer_Source_Csv( $tmpfile );
1019 + fclose( $handle );
1020 + return $source;
1021 + }
1022 +
1023 + /**
555 1024 * Parses uploaded CSV file and saves new data for the chart.
556 1025 *
557 1026 * @since 1.0.0
558 1027 *
@@ -574,9 +1043,9 @@
574 1043
575 1044 // check chart, if chart exists
576 1045 // do not use filter_input as it does not work for phpunit test cases, use filter_var instead
577 1046 $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 ) {
1047 + if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ) {
579 1048 if ( ! $can_die ) {
580 1049 return;
581 1050 }
582 1051 status_header( 400 );
@@ -582,8 +1051,10 @@
582 1051 status_header( 400 );
583 1052 exit;
584 1053 }
585 1054
1055 + 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__ );
1056 +
586 1057 if ( ! isset( $_POST['vz-import-time'] ) ) {
587 1058 apply_filters( 'visualizer_pro_remove_schedule', $chart_id );
588 1059 }
589 1060
@@ -588,15 +1059,33 @@
588 1059 }
589 1060
590 1061 if ( ! isset( $_POST['chart_data_src'] ) || Visualizer_Plugin::CF_SOURCE_FILTER !== $_POST['chart_data_src'] ) {
591 1062 // delete the filters in case this chart is being uploaded from other data sources
592 - delete_post_meta( $chart_id, 'visualizer-filter-config' );
1063 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_FILTER_CONFIG );
1064 + delete_post_meta( $chart_id, '__transient-' . Visualizer_Plugin::CF_FILTER_CONFIG );
1065 + delete_post_meta( $chart_id, '__transient-' . Visualizer_Plugin::CF_DB_QUERY );
593 1066
594 1067 // delete "import from db" specific parameters.
595 1068 delete_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY );
596 1069 delete_post_meta( $chart_id, Visualizer_Plugin::CF_DB_SCHEDULE );
1070 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_REMOTE_DB_PARAMS );
597 1071 }
598 1072
1073 + // delete json related data.
1074 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_URL );
1075 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_ROOT );
1076 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_PAGING );
1077 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_HEADERS );
1078 +
1079 + // delete last error
1080 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR );
1081 +
1082 + // delete editor related data.
1083 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR );
1084 +
1085 + // delete this so that a JSON import can be later edited manually without a problem.
1086 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE );
1087 +
599 1088 $source = null;
600 1089 $render = new Visualizer_Render_Page_Update();
601 1090 if ( isset( $_POST['remote_data'] ) && filter_var( $_POST['remote_data'], FILTER_VALIDATE_URL ) ) {
602 1091 $source = new Visualizer_Source_Csv_Remote( $_POST['remote_data'] );
@@ -602,18 +1091,28 @@
602 1091 $source = new Visualizer_Source_Csv_Remote( $_POST['remote_data'] );
603 1092 if ( isset( $_POST['vz-import-time'] ) ) {
604 1093 apply_filters( 'visualizer_pro_chart_schedule', $chart_id, $_POST['remote_data'], $_POST['vz-import-time'] );
605 1094 }
1095 + // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
606 1096 } elseif ( isset( $_FILES['local_data'] ) && $_FILES['local_data']['error'] == 0 ) {
607 1097 $source = new Visualizer_Source_Csv( $_FILES['local_data']['tmp_name'] );
608 1098 } elseif ( isset( $_POST['chart_data'] ) && strlen( $_POST['chart_data'] ) > 0 ) {
609 - $source = apply_filters( 'visualizer_pro_handle_chart_data', $_POST['chart_data'], '' );
1099 + $source = $this->handleCSVasString( $_POST['chart_data'], $_POST['editor-type'] );
1100 + update_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR, $_POST['editor-type'] );
1101 + } elseif ( isset( $_POST['table_data'] ) && 'yes' === $_POST['table_data'] ) {
1102 + $source = $this->handleTabularData();
1103 + update_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR, $_POST['editor-type'] );
610 1104 } else {
611 - $render->message = esc_html__( 'CSV file with chart data was not uploaded. Please, try again.', 'visualizer' );
1105 + 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__ );
1106 + $render->message = esc_html__( 'CSV file with chart data was not uploaded. Please try again.', 'visualizer' );
1107 + update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, esc_html__( 'CSV file with chart data was not uploaded. Please try again.', 'visualizer' ) );
612 1108 }
1109 +
1110 + 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__ );
1111 +
613 1112 if ( $source ) {
614 1113 if ( $source->fetch() ) {
615 - $content = $source->getData();
1114 + $content = $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) );
616 1115 $populate = true;
617 1116 if ( is_string( $content ) && is_array( unserialize( $content ) ) ) {
618 1117 $json = unserialize( $content );
619 1118 // if source exists, so should data. if source exists but data is blank, do not populate the chart.
@@ -619,8 +1118,9 @@
619 1118 // if source exists, so should data. if source exists but data is blank, do not populate the chart.
620 1119 // if we populate the data even if it is empty, the chart will show "Table has no columns".
621 1120 if ( array_key_exists( 'source', $json ) && ! empty( $json['source'] ) && ( ! array_key_exists( 'data', $json ) || empty( $json['data'] ) ) ) {
622 1121 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__ );
1122 + 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 1123 $populate = false;
624 1124 }
625 1125 }
626 1126 if ( $populate ) {
@@ -629,14 +1129,30 @@
629 1129 wp_update_post( $chart->to_array() );
630 1130 update_post_meta( $chart->ID, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
631 1131 update_post_meta( $chart->ID, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
632 1132 update_post_meta( $chart->ID, Visualizer_Plugin::CF_DEFAULT_DATA, 0 );
1133 +
1134 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Updated post for chart %d', $chart_id ), 'debug', __FILE__, __LINE__ );
1135 +
1136 + Visualizer_Module_Utility::set_defaults( $chart, null );
1137 +
1138 + $settings = get_post_meta( $chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
1139 +
633 1140 $render->id = $chart->ID;
634 - $render->data = json_encode( $source->getRawData() );
1141 + $render->data = json_encode( $source->getRawData( get_post_meta( $chart->ID, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ) );
635 1142 $render->series = json_encode( $source->getSeries() );
1143 + $render->settings = json_encode( $settings );
636 1144 } else {
637 - $render->message = esc_html__( 'CSV file is broken or invalid. Please, try again.', 'visualizer' );
1145 + $error = $source->get_error();
1146 + if ( empty( $error ) ) {
1147 + $error = esc_html__( 'CSV file is broken or invalid. Please try again.', 'visualizer' );
1148 + }
1149 + $render->message = $error;
1150 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( '%s for chart %d.', $error, $chart_id ), 'error', __FILE__, __LINE__ );
1151 + update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, $error );
638 1152 }
1153 + } else {
1154 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Unknown internal error for chart %d.', $chart_id ), 'error', __FILE__, __LINE__ );
639 1155 }
640 1156 $render->render();
641 1157 if ( ! $can_die ) {
642 1158 return;
@@ -652,27 +1168,18 @@
652 1168 * @access public
653 1169 */
654 1170 public function cloneChart() {
655 1171 $chart_id = $success = false;
656 - $nonce = wp_verify_nonce( filter_input( INPUT_GET, 'nonce' ), Visualizer_Plugin::ACTION_CLONE_CHART );
1172 + $nonce = isset( $_GET['nonce'] ) && wp_verify_nonce( $_GET['nonce'], Visualizer_Plugin::ACTION_CLONE_CHART );
657 1173 $capable = current_user_can( 'edit_posts' );
658 1174 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 - );
1175 + $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
669 1176 if ( $chart_id ) {
670 1177 $chart = get_post( $chart_id );
671 - $success = $chart && $chart->post_type == Visualizer_Plugin::CPT_VISUALIZER;
1178 + $success = $chart && $chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
672 1179 }
673 1180 }
674 - $redirect = wp_get_referer();
1181 + $redirect = remove_query_arg( 'vaction', wp_get_referer() );
675 1182 if ( $success ) {
676 1183 $new_chart_id = wp_insert_post(
677 1184 array(
678 1185 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
@@ -681,23 +1188,32 @@
681 1188 'post_status' => $chart->post_status,
682 1189 'post_content' => $chart->post_content,
683 1190 )
684 1191 );
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 ) );
1192 + if ( is_wp_error( $new_chart_id ) ) {
1193 + 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__ );
1194 + } else {
1195 + $post_meta = get_post_meta( $chart_id );
1196 + foreach ( $post_meta as $key => $value ) {
1197 + if ( strpos( $key, 'visualizer-' ) !== false ) {
1198 + add_post_meta( $new_chart_id, $key, maybe_unserialize( $value[0] ) );
1199 + }
1200 + }
691 1201 $redirect = add_query_arg(
692 1202 array(
693 1203 'page' => 'visualizer',
694 1204 'type' => filter_input( INPUT_GET, 'type' ),
1205 + 'vaction' => false,
695 1206 ),
696 - admin_url( 'upload.php' )
1207 + admin_url( 'admin.php' )
697 1208 );
698 1209 }
699 1210 }
1211 +
1212 + if ( defined( 'WP_TESTS_DOMAIN' ) ) {
1213 + wp_die();
1214 + }
1215 +
700 1216 wp_redirect( $redirect );
701 1217 exit;
702 1218 }
703 1219
@@ -743,9 +1259,12 @@
743 1259 $data = $this->_getChartArray();
744 1260 $render = new Visualizer_Render_Page_Data();
745 1261 $render->chart = $this->_chart;
746 1262 $render->type = $data['type'];
747 - unset( $data['settings']['width'], $data['settings']['height'] );
1263 +
1264 + if ( $data && $data['settings'] ) {
1265 + unset( $data['settings']['width'], $data['settings']['height'], $data['settings']['chartArea'] );
1266 + }
748 1267 wp_enqueue_style( 'visualizer-frame' );
749 1268 wp_enqueue_script( 'visualizer-render' );
750 1269 wp_localize_script(
751 1270 'visualizer-render',
@@ -751,9 +1270,9 @@
751 1270 'visualizer-render',
752 1271 'visualizer',
753 1272 array(
754 1273 'l10n' => array(
755 - 'invalid_source' => esc_html__( 'You have entered invalid URL. Please, insert proper URL.', 'visualizer' ),
1274 + 'invalid_source' => esc_html__( 'You have entered an invalid URL. Please provide a valid URL.', 'visualizer' ),
756 1275 'loading' => esc_html__( 'Loading...', 'visualizer' ),
757 1276 ),
758 1277 'charts' => array(
759 1278 'canvas' => $data,
@@ -759,13 +1278,16 @@
759 1278 'canvas' => $data,
760 1279 ),
761 1280 )
762 1281 );
763 - // Added by Ash/Upwork
764 - if ( VISUALIZER_PRO ) {
1282 +
1283 + do_action( 'visualizer_enqueue_scripts_and_styles', $data, $this->_chart->ID );
1284 +
1285 + if ( Visualizer_Module::is_pro() && Visualizer_Module::is_pro_older_than( '1.9.0' ) ) {
765 1286 global $Visualizer_Pro;
766 - $Visualizer_Pro->_enqueueScriptsAndStyles( $data );
1287 + $Visualizer_Pro->_enqueueScriptsAndStyles( $data, $this->_chart->ID );
767 1288 }
1289 +
768 1290 // Added by Ash/Upwork
769 1291 $this->_addAction( 'admin_head', 'renderFlattrScript' );
770 1292 wp_iframe( array( $render, 'render' ) );
771 1293 }
@@ -778,13 +1300,14 @@
778 1300 public function getQueryData() {
779 1301 check_ajax_referer( Visualizer_Plugin::ACTION_FETCH_DB_DATA . Visualizer_Plugin::VERSION, 'security' );
780 1302
781 1303 $params = wp_parse_args( $_POST['params'] );
782 - $source = new Visualizer_Source_Query( stripslashes( $params['query'] ) );
1304 + $chart_id = filter_var( $params['chart_id'], FILTER_VALIDATE_INT );
1305 +
1306 + $source = new Visualizer_Source_Query( stripslashes( $params['query'] ), $chart_id, $params );
783 1307 $html = $source->fetch( true );
784 - $error = '';
785 - if ( empty( $html ) ) {
786 - $error = $source->get_error();
1308 + $error = $source->get_error();
1309 + if ( ! empty( $error ) ) {
787 1310 wp_send_json_error( array( 'msg' => $error ) );
788 1311 }
789 1312 wp_send_json_success( array( 'table' => $html ) );
790 1313 }
@@ -807,21 +1330,42 @@
807 1330 ),
808 1331 )
809 1332 );
810 1333
1334 + $hours = filter_input(
1335 + INPUT_POST,
1336 + 'refresh',
1337 + FILTER_VALIDATE_INT,
1338 + array(
1339 + 'options' => array(
1340 + 'min_range' => -1,
1341 + 'max_range' => apply_filters( 'visualizer_is_business', false ) ? PHP_INT_MAX : -1,
1342 + ),
1343 + )
1344 + );
1345 +
1346 + if ( ! is_int( $hours ) ) {
1347 + $hours = -1;
1348 + }
1349 +
811 1350 $render = new Visualizer_Render_Page_Update();
812 1351 if ( $chart_id ) {
813 1352 $params = wp_parse_args( $_POST['params'] );
814 - $source = new Visualizer_Source_Query( stripslashes( $params['query'] ) );
1353 + $source = new Visualizer_Source_Query( stripslashes( $params['query'] ), $chart_id, $params );
815 1354 $source->fetch( false );
816 1355 $error = $source->get_error();
817 1356 if ( empty( $error ) ) {
818 - $hours = $_POST['refresh'];
819 1357 update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY, stripslashes( $params['query'] ) );
820 1358 update_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
821 1359 update_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
822 1360 update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_SCHEDULE, $hours );
823 1361 update_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, 0 );
1362 + if ( isset( $params['db_type'] ) && $params['db_type'] !== Visualizer_Plugin::WP_DB_NAME ) {
1363 + $remote_db_params = $params;
1364 + unset( $remote_db_params['query'] );
1365 + unset( $remote_db_params['chart_id'] );
1366 + update_post_meta( $chart_id, Visualizer_Plugin::CF_REMOTE_DB_PARAMS, $remote_db_params );
1367 + }
824 1368
825 1369 $schedules = get_option( Visualizer_Plugin::CF_DB_SCHEDULE, array() );
826 1370 $schedules[ $chart_id ] = time() + $hours * HOUR_IN_SECONDS;
827 1371 update_option( Visualizer_Plugin::CF_DB_SCHEDULE, $schedules );
@@ -828,13 +1372,14 @@
828 1372
829 1373 wp_update_post(
830 1374 array(
831 1375 'ID' => $chart_id,
832 - 'post_content' => $source->getData(),
1376 + 'post_content' => $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ),
833 1377 )
834 1378 );
835 - $render->data = json_encode( $source->getRawData() );
1379 + $render->data = json_encode( $source->getRawData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ) );
836 1380 $render->series = json_encode( $source->getSeries() );
1381 + $render->id = $chart_id;
837 1382 } else {
838 1383 $render->message = $error;
839 1384 }
840 1385 }
@@ -841,6 +1386,93 @@
841 1386 $render->render();
842 1387 if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
843 1388 defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
844 1389 }
1390 + }
1391 +
1392 +
1393 + /**
1394 + * Saves the filter query and the schedule.
1395 + *
1396 + * @access public
1397 + */
1398 + public function saveFilter() {
1399 + check_ajax_referer( Visualizer_Plugin::ACTION_SAVE_FILTER_QUERY . Visualizer_Plugin::VERSION, 'security' );
1400 +
1401 + $chart_id = filter_input(
1402 + INPUT_GET,
1403 + 'chart',
1404 + FILTER_VALIDATE_INT,
1405 + array(
1406 + 'options' => array(
1407 + 'min_range' => 1,
1408 + ),
1409 + )
1410 + );
1411 +
1412 + $hours = filter_input(
1413 + INPUT_POST,
1414 + 'refresh',
1415 + FILTER_VALIDATE_INT,
1416 + array(
1417 + 'options' => array(
1418 + 'min_range' => -1,
1419 + 'max_range' => apply_filters( 'visualizer_is_business', false ) ? PHP_INT_MAX : -1,
1420 + ),
1421 + )
1422 + );
1423 +
1424 + if ( 0 !== $hours && empty( $hours ) ) {
1425 + $hours = -1;
1426 + }
1427 +
1428 + do_action( 'visualizer_save_filter', $chart_id, $hours );
1429 +
1430 + if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
1431 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1432 + }
1433 + }
1434 +
1435 + /**
1436 + * Save chart image.
1437 + *
1438 + * @param string $base64_img Chart image.
1439 + * @param int $chart_id Chart ID.
1440 + * @return attachment ID
1441 + */
1442 + public function save_chart_image( $base64_img, $chart_id ) {
1443 + // Upload dir.
1444 + $upload_dir = wp_upload_dir();
1445 + $upload_path = str_replace( '/', DIRECTORY_SEPARATOR, $upload_dir['path'] ) . DIRECTORY_SEPARATOR;
1446 +
1447 + $img = str_replace( 'data:image/png;base64,', '', $base64_img );
1448 + $img = str_replace( ' ', '+', $img );
1449 + $decoded = base64_decode( $img );
1450 + $filename = 'visualization-' . $chart_id . '.png';
1451 + $file_type = 'image/png';
1452 + $hashed_filename = $filename;
1453 +
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 + // Save the image in the uploads directory.
1461 + require_once ABSPATH . '/wp-admin/includes/file.php';
1462 + \WP_Filesystem();
1463 + global $wp_filesystem;
1464 + $upload_file = $wp_filesystem->put_contents( $upload_path . $hashed_filename, $decoded );
1465 +
1466 + // Insert new chart image.
1467 + $attachment = array(
1468 + 'post_mime_type' => $file_type,
1469 + 'post_title' => preg_replace( '/\.[^.]+$/', '', basename( $hashed_filename ) ),
1470 + 'post_content' => '',
1471 + 'post_status' => 'inherit',
1472 + 'guid' => $upload_dir['url'] . '/' . basename( $hashed_filename ),
1473 + );
1474 +
1475 + $attach_id = wp_insert_attachment( $attachment, $upload_dir['path'] . '/' . $hashed_filename );
1476 + return $attach_id;
845 1477 }
846 1478 }