PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.7.7
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.7.7
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 +758 -91 3.1.23.7.7 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,47 @@
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 + update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, $settings );
418 741 }
419 742 $render = new Visualizer_Render_Page_Send();
420 743 $render->text = sprintf( '[visualizer id="%d"]', $this->_chart->ID );
421 744 wp_iframe( array( $render, 'render' ) );
422 -
423 745 return;
424 746 }
425 747 $data = $this->_getChartArray();
426 748 $sidebar = '';
427 - $sidebar_class = 'Visualizer_Render_Sidebar_Type_' . ucfirst( $data['type'] );
749 + $sidebar_class = $this->load_chart_class_name( $this->_chart->ID );
428 750 if ( class_exists( $sidebar_class, true ) ) {
429 751 $sidebar = new $sidebar_class( $data['settings'] );
430 752 $sidebar->__series = $data['series'];
431 753 $sidebar->__data = $data['data'];
@@ -430,14 +752,14 @@
430 752 $sidebar->__series = $data['series'];
431 753 $sidebar->__data = $data['data'];
432 754 } else {
433 755 $sidebar = apply_filters( 'visualizer_pro_chart_type_sidebar', '', $data );
434 - if ( $sidebar != '' ) {
756 + if ( $sidebar !== '' ) {
435 757 $sidebar->__series = $data['series'];
436 758 $sidebar->__data = $data['data'];
437 759 }
438 760 }
439 - unset( $data['settings']['width'], $data['settings']['height'] );
761 + unset( $data['settings']['width'], $data['settings']['height'], $data['settings']['chartArea'] );
440 762 wp_enqueue_style( 'wp-color-picker' );
441 763 wp_enqueue_style( 'visualizer-frame' );
442 764 wp_enqueue_script( 'visualizer-preview' );
443 765 wp_enqueue_script( 'visualizer-chosen' );
@@ -442,17 +764,37 @@
442 764 wp_enqueue_script( 'visualizer-preview' );
443 765 wp_enqueue_script( 'visualizer-chosen' );
444 766 wp_enqueue_script( 'visualizer-render' );
445 767
446 - $table_col_mapping = $this->loadCodeEditorAssets();
768 + if ( Visualizer_Module::can_show_feature( 'simple-editor' ) ) {
769 + wp_enqueue_script( 'visualizer-editor-simple' );
770 + wp_localize_script(
771 + 'visualizer-editor-simple',
772 + 'visualizer1',
773 + array(
774 + 'ajax' => array(
775 + 'url' => admin_url( 'admin-ajax.php' ),
776 + 'nonces' => array(
777 + ),
778 + 'actions' => array(
779 + ),
780 + ),
781 + )
782 + );
783 + }
447 784
785 + $table_col_mapping = $this->loadCodeEditorAssets( $this->_chart->ID );
786 +
448 787 wp_localize_script(
449 788 'visualizer-render',
450 789 'visualizer',
451 790 array(
452 791 'l10n' => array(
453 - 'invalid_source' => esc_html__( 'You have entered invalid URL. Please, insert proper URL.', 'visualizer' ),
792 + 'invalid_source' => esc_html__( 'You have entered an invalid URL. Please provide a valid URL.', 'visualizer' ),
454 793 'loading' => esc_html__( 'Loading...', 'visualizer' ),
794 + 'json_error' => esc_html__( 'An error occured in fetching data.', 'visualizer' ),
795 + 'select_columns' => esc_html__( 'Please select a few columns to include in the chart.', 'visualizer' ),
796 + '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 797 ),
456 798 'charts' => array(
457 799 'canvas' => $data,
458 800 'id' => $this->_chart->ID,
@@ -463,19 +805,28 @@
463 805 'url' => admin_url( 'admin-ajax.php' ),
464 806 'nonces' => array(
465 807 'permissions' => wp_create_nonce( Visualizer_Plugin::ACTION_FETCH_PERMISSIONS_DATA ),
466 808 'db_get_data' => wp_create_nonce( Visualizer_Plugin::ACTION_FETCH_DB_DATA . Visualizer_Plugin::VERSION ),
809 + 'json_get_roots' => wp_create_nonce( Visualizer_Plugin::ACTION_JSON_GET_ROOTS . Visualizer_Plugin::VERSION ),
810 + 'json_get_data' => wp_create_nonce( Visualizer_Plugin::ACTION_JSON_GET_DATA . Visualizer_Plugin::VERSION ),
811 + 'json_set_schedule' => wp_create_nonce( Visualizer_Plugin::ACTION_JSON_SET_SCHEDULE . Visualizer_Plugin::VERSION ),
467 812 ),
468 813 'actions' => array(
469 814 'permissions' => Visualizer_Plugin::ACTION_FETCH_PERMISSIONS_DATA,
470 815 'db_get_data' => Visualizer_Plugin::ACTION_FETCH_DB_DATA,
816 + 'json_get_roots' => Visualizer_Plugin::ACTION_JSON_GET_ROOTS,
817 + 'json_get_data' => Visualizer_Plugin::ACTION_JSON_GET_DATA,
818 + 'json_set_schedule' => Visualizer_Plugin::ACTION_JSON_SET_SCHEDULE,
471 819 ),
472 820 ),
473 821 'db_query' => array(
474 822 'tables' => $table_col_mapping,
475 823 ),
476 - 'is_pro' => VISUALIZER_PRO,
824 + 'is_pro' => Visualizer_Module::is_pro(),
477 825 'page_type' => 'chart',
826 + 'json_tag_separator' => Visualizer_Source_Json::TAG_SEPARATOR,
827 + 'json_tag_separator_view' => Visualizer_Source_Json::TAG_SEPARATOR_VIEW,
828 + 'is_front' => false,
478 829 )
479 830 );
480 831
481 832 $render = new Visualizer_Render_Page_Data();
@@ -483,21 +834,25 @@
483 834 $render->type = $data['type'];
484 835 $render->custom_css = $data['css'];
485 836 $render->sidebar = $sidebar;
486 837 if ( filter_input( INPUT_GET, 'library', FILTER_VALIDATE_BOOLEAN ) ) {
487 - $render->button = filter_input( INPUT_GET, 'action' ) == Visualizer_Plugin::ACTION_EDIT_CHART
838 + $render->button = filter_input( INPUT_GET, 'action' ) === Visualizer_Plugin::ACTION_EDIT_CHART
488 839 ? esc_html__( 'Save Chart', 'visualizer' )
489 840 : esc_html__( 'Create Chart', 'visualizer' );
490 - if ( filter_input( INPUT_GET, 'action' ) == Visualizer_Plugin::ACTION_EDIT_CHART ) {
841 + if ( filter_input( INPUT_GET, 'action' ) === Visualizer_Plugin::ACTION_EDIT_CHART ) {
491 842 $render->cancel_button = esc_html__( 'Cancel', 'visualizer' );
492 843 }
493 844 } else {
494 845 $render->button = esc_attr__( 'Insert Chart', 'visualizer' );
495 846 }
496 - if ( VISUALIZER_PRO ) {
847 +
848 + do_action( 'visualizer_enqueue_scripts_and_styles', $data, $this->_chart->ID );
849 +
850 + if ( Visualizer_Module::is_pro() && Visualizer_Module::is_pro_older_than( '1.9.0' ) ) {
497 851 global $Visualizer_Pro;
498 - $Visualizer_Pro->_enqueueScriptsAndStyles( $data );
852 + $Visualizer_Pro->_enqueueScriptsAndStyles( $data, $this->_chart->ID );
499 853 }
854 +
500 855 $this->_addAction( 'admin_head', 'renderFlattrScript' );
501 856 wp_iframe( array( $render, 'render' ) );
502 857 }
503 858
@@ -509,24 +864,35 @@
509 864 * @access private
510 865 */
511 866 private function _handleTypesPage() {
512 867 // process post request
513 - if ( $_SERVER['REQUEST_METHOD'] == 'POST' && wp_verify_nonce( filter_input( INPUT_POST, 'nonce' ) ) ) {
868 + if ( $_SERVER['REQUEST_METHOD'] === 'POST' && wp_verify_nonce( filter_input( INPUT_POST, 'nonce' ) ) ) {
514 869 $type = filter_input( INPUT_POST, 'type' );
515 - if ( in_array( $type, Visualizer_Plugin::getChartTypes() ) ) {
870 + $library = filter_input( INPUT_POST, 'chart-library' );
871 + if ( in_array( $type, Visualizer_Plugin::getChartTypes(), true ) ) {
872 + if ( empty( $library ) ) {
873 + // library cannot be empty.
874 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, 'Chart library empty while creating the chart! Aborting...', 'error', __FILE__, __LINE__ );
875 + return;
876 + }
877 +
516 878 // save new chart type
517 879 update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_CHART_TYPE, $type );
880 + update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_CHART_LIBRARY, $library );
518 881 // if the chart has default data, update it with appropriate default data for new type
519 882 if ( filter_var( get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_DEFAULT_DATA, true ), FILTER_VALIDATE_BOOLEAN ) ) {
520 883 $source = new Visualizer_Source_Csv( VISUALIZER_ABSPATH . DIRECTORY_SEPARATOR . 'samples' . DIRECTORY_SEPARATOR . $type . '.csv' );
521 884 $source->fetch();
522 - $this->_chart->post_content = $source->getData();
885 + $this->_chart->post_content = $source->getData( get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) );
523 886 wp_update_post( $this->_chart->to_array() );
524 887 update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
525 888 }
889 +
890 + Visualizer_Module_Utility::set_defaults( $this->_chart );
891 +
526 892 // redirect to next tab
527 893 // changed by Ash/Upwork
528 - wp_redirect( add_query_arg( 'tab', 'settings' ) );
894 + wp_redirect( esc_url_raw( add_query_arg( 'tab', 'settings' ) ) );
529 895
530 896 return;
531 897 }
532 898 }
@@ -531,9 +897,9 @@
531 897 }
532 898 }
533 899 $render = new Visualizer_Render_Page_Types();
534 900 $render->type = get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_CHART_TYPE, true );
535 - $render->types = Visualizer_Module_Admin::_getChartTypesLocalized();
901 + $render->types = Visualizer_Module_Admin::_getChartTypesLocalized( false, false, false, 'types' );
536 902 $render->chart = $this->_chart;
537 903 wp_enqueue_style( 'visualizer-frame' );
538 904 wp_enqueue_script( 'visualizer-frame' );
539 905 wp_iframe( array( $render, 'render' ) );
@@ -549,10 +915,131 @@
549 915 */
550 916 public function renderFlattrScript() {
551 917 echo '';
552 918 }
553 - // changed by Ash/Upwork
919 +
554 920 /**
921 + * Processes the CSV that is sent in the request as a string.
922 + *
923 + * @since 3.2.0
924 + */
925 + private function handleCSVasString( $data, $editor_type ) {
926 + $source = null;
927 +
928 + switch ( $editor_type ) {
929 + case 'text':
930 + // data coming in from the text editor.
931 + $tmpfile = tempnam( get_temp_dir(), Visualizer_Plugin::NAME );
932 + $handle = fopen( $tmpfile, 'w' );
933 + $values = preg_split( '/[\n\r]+/', stripslashes( trim( $data ) ) );
934 + if ( $values ) {
935 + foreach ( $values as $row ) {
936 + if ( empty( $row ) ) {
937 + continue;
938 + }
939 + $row = explode( ',', $row );
940 + $row = array_map(
941 + function( $r ) {
942 + return '' === $r ? ' ' : $r;
943 + },
944 + $row
945 + );
946 + $row = implode( ',', $row );
947 + // don't use fpucsv here because we need to just dump the data
948 + // minus the empty rows
949 + // because fputcsv needs to tokenize
950 + // we can standardize the CSV enclosure here and replace all ' with "
951 + // we can assume that if there are an even number of ' they should be changed to "
952 + // but that will screw up words like fo'c'sle
953 + // so here let's just assume ' will NOT be used for enclosures
954 + fwrite( $handle, $row );
955 + fwrite( $handle, PHP_EOL );
956 + }
957 + }
958 + $source = new Visualizer_Source_Csv( $tmpfile );
959 + fclose( $handle );
960 + break;
961 + case 'table':
962 + // Import from Chart
963 + // fall-through.
964 + case 'excel':
965 + // data coming in from the excel editor.
966 + $source = apply_filters( 'visualizer_pro_handle_chart_data', $data, '' );
967 + break;
968 + }
969 + return $source;
970 + }
971 +
972 + /**
973 + * Parses the data uploaded as an HTML table.
974 + *
975 + * @since 3.2.0
976 + *
977 + * @access private
978 + */
979 + private function handleTabularData() {
980 + $csv = array();
981 + // the datatable mentions the headers twice, so lets remove the duplicates.
982 + $headers = array_unique( array_filter( $_POST['header'] ) );
983 + $types = $_POST['type'];
984 +
985 + // capture all the indexes that correspond to excluded columns.
986 + $exclude = array();
987 + $index = 0;
988 + foreach ( $types as $type ) {
989 + if ( empty( $type ) ) {
990 + $exclude[] = $index;
991 + }
992 + $index++;
993 + }
994 +
995 + // when N headers are being renamed, the number of headers increases by N
996 + // because of the way datatable duplicates header information
997 + // so unset the headers that have been renamed.
998 + if ( count( $headers ) !== count( $types ) ) {
999 + $to = count( $headers );
1000 + for ( $i = count( $types ); $i < $to; $i++ ) {
1001 + unset( $headers[ $i + 1 ] );
1002 + }
1003 + }
1004 +
1005 + $columns = array();
1006 + for ( $i = 0; $i < count( $headers ); $i++ ) {
1007 + if ( ! isset( $_POST[ 'data' . $i ] ) ) {
1008 + continue;
1009 + }
1010 + $columns[ $i ] = $_POST[ 'data' . $i ];
1011 + }
1012 +
1013 + $csv[] = $headers;
1014 + $csv[] = $types;
1015 + for ( $j = 0; $j < count( $columns[0] ); $j++ ) {
1016 + $row = array();
1017 + for ( $i = 0; $i < count( $headers ); $i++ ) {
1018 + $row[] = sanitize_text_field( $columns[ $i ][ $j ] );
1019 + }
1020 + $csv[] = $row;
1021 + }
1022 +
1023 + $tmpfile = tempnam( get_temp_dir(), Visualizer_Plugin::NAME );
1024 + $handle = fopen( $tmpfile, 'w' );
1025 +
1026 + if ( $csv ) {
1027 + $index = 0;
1028 + foreach ( $csv as $row ) {
1029 + // remove all the cells corresponding to the excluded headers.
1030 + foreach ( $exclude as $j ) {
1031 + unset( $row[ $j ] );
1032 + }
1033 + fputcsv( $handle, $row );
1034 + }
1035 + }
1036 + $source = new Visualizer_Source_Csv( $tmpfile );
1037 + fclose( $handle );
1038 + return $source;
1039 + }
1040 +
1041 + /**
555 1042 * Parses uploaded CSV file and saves new data for the chart.
556 1043 *
557 1044 * @since 1.0.0
558 1045 *
@@ -574,9 +1061,9 @@
574 1061
575 1062 // check chart, if chart exists
576 1063 // do not use filter_input as it does not work for phpunit test cases, use filter_var instead
577 1064 $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 ) {
1065 + if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ) {
579 1066 if ( ! $can_die ) {
580 1067 return;
581 1068 }
582 1069 status_header( 400 );
@@ -582,8 +1069,10 @@
582 1069 status_header( 400 );
583 1070 exit;
584 1071 }
585 1072
1073 + 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__ );
1074 +
586 1075 if ( ! isset( $_POST['vz-import-time'] ) ) {
587 1076 apply_filters( 'visualizer_pro_remove_schedule', $chart_id );
588 1077 }
589 1078
@@ -588,15 +1077,33 @@
588 1077 }
589 1078
590 1079 if ( ! isset( $_POST['chart_data_src'] ) || Visualizer_Plugin::CF_SOURCE_FILTER !== $_POST['chart_data_src'] ) {
591 1080 // delete the filters in case this chart is being uploaded from other data sources
592 - delete_post_meta( $chart_id, 'visualizer-filter-config' );
1081 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_FILTER_CONFIG );
1082 + delete_post_meta( $chart_id, '__transient-' . Visualizer_Plugin::CF_FILTER_CONFIG );
1083 + delete_post_meta( $chart_id, '__transient-' . Visualizer_Plugin::CF_DB_QUERY );
593 1084
594 1085 // delete "import from db" specific parameters.
595 1086 delete_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY );
596 1087 delete_post_meta( $chart_id, Visualizer_Plugin::CF_DB_SCHEDULE );
1088 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_REMOTE_DB_PARAMS );
597 1089 }
598 1090
1091 + // delete json related data.
1092 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_URL );
1093 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_ROOT );
1094 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_PAGING );
1095 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_HEADERS );
1096 +
1097 + // delete last error
1098 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR );
1099 +
1100 + // delete editor related data.
1101 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR );
1102 +
1103 + // delete this so that a JSON import can be later edited manually without a problem.
1104 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE );
1105 +
599 1106 $source = null;
600 1107 $render = new Visualizer_Render_Page_Update();
601 1108 if ( isset( $_POST['remote_data'] ) && filter_var( $_POST['remote_data'], FILTER_VALIDATE_URL ) ) {
602 1109 $source = new Visualizer_Source_Csv_Remote( $_POST['remote_data'] );
@@ -602,18 +1109,28 @@
602 1109 $source = new Visualizer_Source_Csv_Remote( $_POST['remote_data'] );
603 1110 if ( isset( $_POST['vz-import-time'] ) ) {
604 1111 apply_filters( 'visualizer_pro_chart_schedule', $chart_id, $_POST['remote_data'], $_POST['vz-import-time'] );
605 1112 }
1113 + // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
606 1114 } elseif ( isset( $_FILES['local_data'] ) && $_FILES['local_data']['error'] == 0 ) {
607 1115 $source = new Visualizer_Source_Csv( $_FILES['local_data']['tmp_name'] );
608 1116 } elseif ( isset( $_POST['chart_data'] ) && strlen( $_POST['chart_data'] ) > 0 ) {
609 - $source = apply_filters( 'visualizer_pro_handle_chart_data', $_POST['chart_data'], '' );
1117 + $source = $this->handleCSVasString( $_POST['chart_data'], $_POST['editor-type'] );
1118 + update_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR, $_POST['editor-type'] );
1119 + } elseif ( isset( $_POST['table_data'] ) && 'yes' === $_POST['table_data'] ) {
1120 + $source = $this->handleTabularData();
1121 + update_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR, $_POST['editor-type'] );
610 1122 } else {
611 - $render->message = esc_html__( 'CSV file with chart data was not uploaded. Please, try again.', 'visualizer' );
1123 + 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__ );
1124 + $render->message = esc_html__( 'CSV file with chart data was not uploaded. Please try again.', 'visualizer' );
1125 + update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, esc_html__( 'CSV file with chart data was not uploaded. Please try again.', 'visualizer' ) );
612 1126 }
1127 +
1128 + 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__ );
1129 +
613 1130 if ( $source ) {
614 1131 if ( $source->fetch() ) {
615 - $content = $source->getData();
1132 + $content = $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) );
616 1133 $populate = true;
617 1134 if ( is_string( $content ) && is_array( unserialize( $content ) ) ) {
618 1135 $json = unserialize( $content );
619 1136 // if source exists, so should data. if source exists but data is blank, do not populate the chart.
@@ -619,8 +1136,9 @@
619 1136 // if source exists, so should data. if source exists but data is blank, do not populate the chart.
620 1137 // if we populate the data even if it is empty, the chart will show "Table has no columns".
621 1138 if ( array_key_exists( 'source', $json ) && ! empty( $json['source'] ) && ( ! array_key_exists( 'data', $json ) || empty( $json['data'] ) ) ) {
622 1139 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__ );
1140 + 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 1141 $populate = false;
624 1142 }
625 1143 }
626 1144 if ( $populate ) {
@@ -626,17 +1144,43 @@
626 1144 if ( $populate ) {
627 1145 $chart->post_content = $content;
628 1146 }
629 1147 wp_update_post( $chart->to_array() );
1148 +
630 1149 update_post_meta( $chart->ID, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
631 1150 update_post_meta( $chart->ID, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
632 1151 update_post_meta( $chart->ID, Visualizer_Plugin::CF_DEFAULT_DATA, 0 );
1152 +
1153 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Updated post for chart %d', $chart_id ), 'debug', __FILE__, __LINE__ );
1154 +
1155 + Visualizer_Module_Utility::set_defaults( $chart, null );
1156 +
1157 + $settings = get_post_meta( $chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
1158 + if ( isset( $settings['series'] ) && ! ( count( $settings['series'] ) - count( $source->getSeries() ) > 1 ) ) {
1159 + $diff_total_series = abs( count( $settings['series'] ) - count( $source->getSeries() ) );
1160 + if ( $diff_total_series ) {
1161 + foreach ( range( 1, $diff_total_series ) as $k => $diff_series ) {
1162 + $settings['series'][] = end( $settings['series'] );
1163 + }
1164 + update_post_meta( $chart->ID, Visualizer_Plugin::CF_SETTINGS, $settings );
1165 + }
1166 + }
1167 +
633 1168 $render->id = $chart->ID;
634 - $render->data = json_encode( $source->getRawData() );
1169 + $render->data = json_encode( $source->getRawData( get_post_meta( $chart->ID, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ) );
635 1170 $render->series = json_encode( $source->getSeries() );
1171 + $render->settings = json_encode( $settings );
636 1172 } else {
637 - $render->message = esc_html__( 'CSV file is broken or invalid. Please, try again.', 'visualizer' );
1173 + $error = $source->get_error();
1174 + if ( empty( $error ) ) {
1175 + $error = esc_html__( 'CSV file is broken or invalid. Please try again.', 'visualizer' );
1176 + }
1177 + $render->message = $error;
1178 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( '%s for chart %d.', $error, $chart_id ), 'error', __FILE__, __LINE__ );
1179 + update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, $error );
638 1180 }
1181 + } else {
1182 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Unknown internal error for chart %d.', $chart_id ), 'error', __FILE__, __LINE__ );
639 1183 }
640 1184 $render->render();
641 1185 if ( ! $can_die ) {
642 1186 return;
@@ -652,27 +1196,18 @@
652 1196 * @access public
653 1197 */
654 1198 public function cloneChart() {
655 1199 $chart_id = $success = false;
656 - $nonce = wp_verify_nonce( filter_input( INPUT_GET, 'nonce' ), Visualizer_Plugin::ACTION_CLONE_CHART );
1200 + $nonce = isset( $_GET['nonce'] ) && wp_verify_nonce( $_GET['nonce'], Visualizer_Plugin::ACTION_CLONE_CHART );
657 1201 $capable = current_user_can( 'edit_posts' );
658 1202 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 - );
1203 + $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
669 1204 if ( $chart_id ) {
670 1205 $chart = get_post( $chart_id );
671 - $success = $chart && $chart->post_type == Visualizer_Plugin::CPT_VISUALIZER;
1206 + $success = $chart && $chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
672 1207 }
673 1208 }
674 - $redirect = wp_get_referer();
1209 + $redirect = remove_query_arg( 'vaction', wp_get_referer() );
675 1210 if ( $success ) {
676 1211 $new_chart_id = wp_insert_post(
677 1212 array(
678 1213 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
@@ -681,23 +1216,34 @@
681 1216 'post_status' => $chart->post_status,
682 1217 'post_content' => $chart->post_content,
683 1218 )
684 1219 );
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' )
1220 + if ( is_wp_error( $new_chart_id ) ) {
1221 + 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__ );
1222 + } else {
1223 + $post_meta = get_post_meta( $chart_id );
1224 + foreach ( $post_meta as $key => $value ) {
1225 + if ( strpos( $key, 'visualizer-' ) !== false ) {
1226 + add_post_meta( $new_chart_id, $key, maybe_unserialize( $value[0] ) );
1227 + }
1228 + }
1229 + $redirect = esc_url(
1230 + add_query_arg(
1231 + array(
1232 + 'page' => 'visualizer',
1233 + 'type' => filter_input( INPUT_GET, 'type' ),
1234 + 'vaction' => false,
1235 + ),
1236 + admin_url( 'admin.php' )
1237 + )
697 1238 );
698 1239 }
699 1240 }
1241 +
1242 + if ( defined( 'WP_TESTS_DOMAIN' ) ) {
1243 + wp_die();
1244 + }
1245 +
700 1246 wp_redirect( $redirect );
701 1247 exit;
702 1248 }
703 1249
@@ -743,9 +1289,12 @@
743 1289 $data = $this->_getChartArray();
744 1290 $render = new Visualizer_Render_Page_Data();
745 1291 $render->chart = $this->_chart;
746 1292 $render->type = $data['type'];
747 - unset( $data['settings']['width'], $data['settings']['height'] );
1293 +
1294 + if ( $data && $data['settings'] ) {
1295 + unset( $data['settings']['width'], $data['settings']['height'], $data['settings']['chartArea'] );
1296 + }
748 1297 wp_enqueue_style( 'visualizer-frame' );
749 1298 wp_enqueue_script( 'visualizer-render' );
750 1299 wp_localize_script(
751 1300 'visualizer-render',
@@ -751,9 +1300,9 @@
751 1300 'visualizer-render',
752 1301 'visualizer',
753 1302 array(
754 1303 'l10n' => array(
755 - 'invalid_source' => esc_html__( 'You have entered invalid URL. Please, insert proper URL.', 'visualizer' ),
1304 + 'invalid_source' => esc_html__( 'You have entered an invalid URL. Please provide a valid URL.', 'visualizer' ),
756 1305 'loading' => esc_html__( 'Loading...', 'visualizer' ),
757 1306 ),
758 1307 'charts' => array(
759 1308 'canvas' => $data,
@@ -759,13 +1308,16 @@
759 1308 'canvas' => $data,
760 1309 ),
761 1310 )
762 1311 );
763 - // Added by Ash/Upwork
764 - if ( VISUALIZER_PRO ) {
1312 +
1313 + do_action( 'visualizer_enqueue_scripts_and_styles', $data, $this->_chart->ID );
1314 +
1315 + if ( Visualizer_Module::is_pro() && Visualizer_Module::is_pro_older_than( '1.9.0' ) ) {
765 1316 global $Visualizer_Pro;
766 - $Visualizer_Pro->_enqueueScriptsAndStyles( $data );
1317 + $Visualizer_Pro->_enqueueScriptsAndStyles( $data, $this->_chart->ID );
767 1318 }
1319 +
768 1320 // Added by Ash/Upwork
769 1321 $this->_addAction( 'admin_head', 'renderFlattrScript' );
770 1322 wp_iframe( array( $render, 'render' ) );
771 1323 }
@@ -778,13 +1330,14 @@
778 1330 public function getQueryData() {
779 1331 check_ajax_referer( Visualizer_Plugin::ACTION_FETCH_DB_DATA . Visualizer_Plugin::VERSION, 'security' );
780 1332
781 1333 $params = wp_parse_args( $_POST['params'] );
782 - $source = new Visualizer_Source_Query( stripslashes( $params['query'] ) );
1334 + $chart_id = filter_var( $params['chart_id'], FILTER_VALIDATE_INT );
1335 +
1336 + $source = new Visualizer_Source_Query( stripslashes( $params['query'] ), $chart_id, $params );
783 1337 $html = $source->fetch( true );
784 - $error = '';
785 - if ( empty( $html ) ) {
786 - $error = $source->get_error();
1338 + $error = $source->get_error();
1339 + if ( ! empty( $error ) ) {
787 1340 wp_send_json_error( array( 'msg' => $error ) );
788 1341 }
789 1342 wp_send_json_success( array( 'table' => $html ) );
790 1343 }
@@ -807,21 +1360,42 @@
807 1360 ),
808 1361 )
809 1362 );
810 1363
1364 + $hours = filter_input(
1365 + INPUT_POST,
1366 + 'refresh',
1367 + FILTER_VALIDATE_FLOAT,
1368 + array(
1369 + 'options' => array(
1370 + 'min_range' => -1,
1371 + 'max_range' => apply_filters( 'visualizer_is_business', false ) ? PHP_INT_MAX : -1,
1372 + ),
1373 + )
1374 + );
1375 +
1376 + if ( ! is_numeric( $hours ) ) {
1377 + $hours = -1;
1378 + }
1379 +
811 1380 $render = new Visualizer_Render_Page_Update();
812 1381 if ( $chart_id ) {
813 1382 $params = wp_parse_args( $_POST['params'] );
814 - $source = new Visualizer_Source_Query( stripslashes( $params['query'] ) );
1383 + $source = new Visualizer_Source_Query( stripslashes( $params['query'] ), $chart_id, $params );
815 1384 $source->fetch( false );
816 1385 $error = $source->get_error();
817 1386 if ( empty( $error ) ) {
818 - $hours = $_POST['refresh'];
819 1387 update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY, stripslashes( $params['query'] ) );
820 1388 update_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
821 1389 update_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
822 1390 update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_SCHEDULE, $hours );
823 1391 update_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, 0 );
1392 + if ( isset( $params['db_type'] ) && $params['db_type'] !== Visualizer_Plugin::WP_DB_NAME ) {
1393 + $remote_db_params = $params;
1394 + unset( $remote_db_params['query'] );
1395 + unset( $remote_db_params['chart_id'] );
1396 + update_post_meta( $chart_id, Visualizer_Plugin::CF_REMOTE_DB_PARAMS, $remote_db_params );
1397 + }
824 1398
825 1399 $schedules = get_option( Visualizer_Plugin::CF_DB_SCHEDULE, array() );
826 1400 $schedules[ $chart_id ] = time() + $hours * HOUR_IN_SECONDS;
827 1401 update_option( Visualizer_Plugin::CF_DB_SCHEDULE, $schedules );
@@ -828,13 +1402,14 @@
828 1402
829 1403 wp_update_post(
830 1404 array(
831 1405 'ID' => $chart_id,
832 - 'post_content' => $source->getData(),
1406 + 'post_content' => $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ),
833 1407 )
834 1408 );
835 - $render->data = json_encode( $source->getRawData() );
1409 + $render->data = json_encode( $source->getRawData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ) );
836 1410 $render->series = json_encode( $source->getSeries() );
1411 + $render->id = $chart_id;
837 1412 } else {
838 1413 $render->message = $error;
839 1414 }
840 1415 }
@@ -841,6 +1416,98 @@
841 1416 $render->render();
842 1417 if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
843 1418 defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
844 1419 }
1420 + }
1421 +
1422 +
1423 + /**
1424 + * Saves the filter query and the schedule.
1425 + *
1426 + * @access public
1427 + */
1428 + public function saveFilter() {
1429 + check_ajax_referer( Visualizer_Plugin::ACTION_SAVE_FILTER_QUERY . Visualizer_Plugin::VERSION, 'security' );
1430 +
1431 + $chart_id = filter_input(
1432 + INPUT_GET,
1433 + 'chart',
1434 + FILTER_VALIDATE_INT,
1435 + array(
1436 + 'options' => array(
1437 + 'min_range' => 1,
1438 + ),
1439 + )
1440 + );
1441 +
1442 + $hours = filter_input(
1443 + INPUT_POST,
1444 + 'refresh',
1445 + FILTER_VALIDATE_INT,
1446 + array(
1447 + 'options' => array(
1448 + 'min_range' => -1,
1449 + 'max_range' => apply_filters( 'visualizer_is_business', false ) ? PHP_INT_MAX : -1,
1450 + ),
1451 + )
1452 + );
1453 +
1454 + if ( 0 !== $hours && empty( $hours ) ) {
1455 + $hours = -1;
1456 + }
1457 +
1458 + do_action( 'visualizer_save_filter', $chart_id, $hours );
1459 +
1460 + if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
1461 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1462 + }
1463 + }
1464 +
1465 + /**
1466 + * Save chart image.
1467 + *
1468 + * @param string $base64_img Chart image.
1469 + * @param int $chart_id Chart ID.
1470 + * @param bool $save_attachment Save attachment.
1471 + * @return attachment ID
1472 + */
1473 + public function save_chart_image( $base64_img, $chart_id, $save_attachment = true ) {
1474 + // Delete old chart image.
1475 + $old_attachment_id = get_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_IMAGE, true );
1476 + if ( $old_attachment_id ) {
1477 + wp_delete_attachment( $old_attachment_id, true );
1478 + }
1479 +
1480 + if ( ! $save_attachment ) {
1481 + return 0;
1482 + }
1483 +
1484 + // Upload dir.
1485 + $upload_dir = wp_upload_dir();
1486 + $upload_path = str_replace( '/', DIRECTORY_SEPARATOR, $upload_dir['path'] ) . DIRECTORY_SEPARATOR;
1487 +
1488 + $img = str_replace( 'data:image/png;base64,', '', $base64_img );
1489 + $img = str_replace( ' ', '+', $img );
1490 + $decoded = base64_decode( $img );
1491 + $filename = 'visualization-' . $chart_id . '.png';
1492 + $file_type = 'image/png';
1493 + $hashed_filename = $filename;
1494 +
1495 + // Save the image in the uploads directory.
1496 + require_once ABSPATH . '/wp-admin/includes/file.php';
1497 + \WP_Filesystem();
1498 + global $wp_filesystem;
1499 + $upload_file = $wp_filesystem->put_contents( $upload_path . $hashed_filename, $decoded );
1500 +
1501 + // Insert new chart image.
1502 + $attachment = array(
1503 + 'post_mime_type' => $file_type,
1504 + 'post_title' => preg_replace( '/\.[^.]+$/', '', basename( $hashed_filename ) ),
1505 + 'post_content' => '',
1506 + 'post_status' => 'inherit',
1507 + 'guid' => $upload_dir['url'] . '/' . basename( $hashed_filename ),
1508 + );
1509 +
1510 + $attach_id = wp_insert_attachment( $attachment, $upload_dir['path'] . '/' . $hashed_filename );
1511 + return $attach_id;
845 1512 }
846 1513 }