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