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