PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.7.12
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.7.12
4.0.8 4.0.7 4.0.6 4.0.5 4.0.4 4.0.3 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.1 3.1.2 3.1.3 3.10.0 3.10.1 3.10.10 3.10.11 3.10.12 3.10.13 3.10.14 3.10.15 3.10.2 3.10.3 All 149 releases
← All changes | classes/Visualizer/Module/Chart.php +708 -93 3.1.33.7.12 View file →
@@ -60,12 +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 +
64 70 $this->_addAjaxAction( Visualizer_Plugin::ACTION_SAVE_FILTER_QUERY, 'saveFilter' );
71 +
72 + $this->_addFilter( 'visualizer_get_sidebar', 'getSidebar', 10, 2 );
73 +
65 74 }
66 75
67 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 + /**
68 283 * Fetches charts from database.
69 284 *
70 285 * This method is also called from the media pop-up (classic editor: create a post and add chart from insert content).
71 286 *
@@ -94,9 +309,9 @@
94 309 // 'filter' is from the modal from the add media button.
95 310 $filter = filter_input( INPUT_GET, 'filter', FILTER_SANITIZE_STRING );
96 311 }
97 312
98 - if ( $filter && in_array( $filter, Visualizer_Plugin::getChartTypes() ) ) {
313 + if ( $filter && in_array( $filter, Visualizer_Plugin::getChartTypes(), true ) ) {
99 314 $query_args['meta_query'] = array(
100 315 array(
101 316 'key' => Visualizer_Plugin::CF_CHART_TYPE,
102 317 'value' => $filter,
@@ -147,9 +362,9 @@
147 362 $chart = $this->_chart;
148 363 }
149 364 $type = get_post_meta( $chart->ID, Visualizer_Plugin::CF_CHART_TYPE, true );
150 365 $series = apply_filters( Visualizer_Plugin::FILTER_GET_CHART_SERIES, get_post_meta( $chart->ID, Visualizer_Plugin::CF_SERIES, true ), $chart->ID, $type );
151 - $data = apply_filters( Visualizer_Plugin::FILTER_GET_CHART_DATA, unserialize( $chart->post_content ), $chart->ID, $type );
366 + $data = self::get_chart_data( $chart, $type );
152 367 $library = $this->load_chart_type( $chart->ID );
153 368
154 369 $settings = get_post_meta( $chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
155 370 $settings = apply_filters( Visualizer_Plugin::FILTER_GET_CHART_SETTINGS, $settings, $chart->ID, $type );
@@ -201,9 +416,9 @@
201 416 *
202 417 * @access public
203 418 */
204 419 public function deleteChart() {
205 - $is_post = $_SERVER['REQUEST_METHOD'] == 'POST';
420 + $is_post = $_SERVER['REQUEST_METHOD'] === 'POST';
206 421 $input_method = $is_post ? INPUT_POST : INPUT_GET;
207 422 $chart_id = $success = false;
208 423 $nonce = wp_verify_nonce( filter_input( $input_method, 'nonce' ) );
209 424 $capable = current_user_can( 'delete_posts' );
@@ -219,9 +434,9 @@
219 434 )
220 435 );
221 436 if ( $chart_id ) {
222 437 $chart = get_post( $chart_id );
223 - $success = $chart && $chart->post_type == Visualizer_Plugin::CPT_VISUALIZER;
438 + $success = $chart && $chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
224 439 }
225 440 }
226 441 if ( $success ) {
227 442 wp_delete_post( $chart_id, true );
@@ -232,9 +447,9 @@
232 447 'success' => $success,
233 448 )
234 449 );
235 450 }
236 - wp_redirect( wp_get_referer() );
451 + wp_redirect( remove_query_arg( 'vaction', wp_get_referer() ) );
237 452 exit;
238 453 }
239 454
240 455 /**
@@ -274,11 +489,16 @@
274 489 * @access public
275 490 */
276 491 public function renderChartPages() {
277 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' );
278 498 // check chart, if chart not exists, will create new one and redirects to the same page with proper chart id
279 499 $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
280 - 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 ) {
281 501 $this->deleteOldCharts();
282 502 $default_type = isset( $_GET['type'] ) && ! empty( $_GET['type'] ) ? $_GET['type'] : 'line';
283 503 $source = new Visualizer_Source_Csv( VISUALIZER_ABSPATH . DIRECTORY_SEPARATOR . 'samples' . DIRECTORY_SEPARATOR . $default_type . '.csv' );
284 504 $source->fetch();
@@ -287,9 +507,9 @@
287 507 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
288 508 'post_title' => 'Visualization',
289 509 'post_author' => get_current_user_id(),
290 510 'post_status' => 'auto-draft',
291 - 'post_content' => $source->getData(),
511 + 'post_content' => $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ),
292 512 )
293 513 );
294 514 if ( $chart_id && ! is_wp_error( $chart_id ) ) {
295 515 add_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_TYPE, $default_type );
@@ -295,8 +515,9 @@
295 515 add_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_TYPE, $default_type );
296 516 add_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, 1 );
297 517 add_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
298 518 add_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
519 + add_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_LIBRARY, '' );
299 520 add_post_meta(
300 521 $chart_id,
301 522 Visualizer_Plugin::CF_SETTINGS,
302 523 array(
@@ -304,20 +525,40 @@
304 525 )
305 526 );
306 527 do_action( 'visualizer_pro_new_chart_defaults', $chart_id );
307 528 }
308 - wp_redirect( add_query_arg( 'chart', (int) $chart_id ) );
309 - 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();
310 535 }
311 536
312 - $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;
313 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 +
314 555 // enqueue and register scripts and styles
315 556 wp_register_script( 'visualizer-chosen', VISUALIZER_ABSURL . 'js/lib/chosen.jquery.min.js', array( 'jquery' ), Visualizer_Plugin::VERSION );
316 557 wp_register_style( 'visualizer-chosen', VISUALIZER_ABSURL . 'css/lib/chosen.min.css', array(), Visualizer_Plugin::VERSION );
317 558
318 559 wp_register_style( 'visualizer-frame', VISUALIZER_ABSURL . 'css/frame.css', array( 'visualizer-chosen' ), Visualizer_Plugin::VERSION );
319 - 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 );
320 561 wp_register_script( 'visualizer-customization', $this->get_user_customization_js(), array(), null, true );
321 562 wp_register_script(
322 563 'visualizer-render',
323 564 VISUALIZER_ABSURL . 'js/render-facade.js',
@@ -328,19 +569,23 @@
328 569 wp_register_script(
329 570 'visualizer-preview',
330 571 VISUALIZER_ABSURL . 'js/preview.js',
331 572 array(
332 - 'wp-color-picker',
573 + $color_picker_dep,
333 574 'visualizer-render',
334 575 ),
335 576 Visualizer_Plugin::VERSION,
336 577 true
337 578 );
338 - // added by Ash/Upwork
339 - 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' ) ) {
340 584 global $Visualizer_Pro;
341 585 $Visualizer_Pro->_addScriptsAndStyles();
342 586 }
587 +
343 588 // dispatch pages
344 589 $this->_chart = get_post( $chart_id );
345 590 $tab = isset( $_GET['tab'] ) && ! empty( $_GET['tab'] ) ? $_GET['tab'] : 'visualizer';
346 591
@@ -352,8 +597,9 @@
352 597 if ( isset( $_POST['cancel'] ) && 1 === intval( $_POST['cancel'] ) ) {
353 598 // if the cancel button is clicked.
354 599 $this->undoRevisions( $chart_id, true );
355 600 } elseif ( isset( $_POST['save'] ) && 1 === intval( $_POST['save'] ) ) {
601 + $this->handlePermissions();
356 602 // if the save button is clicked.
357 603 $this->undoRevisions( $chart_id, false );
358 604 } else {
359 605 // if the edit button is clicked.
@@ -359,8 +605,16 @@
359 605 // if the edit button is clicked.
360 606 $this->_chart = $this->handleExistingRevisions( $chart_id, $this->_chart );
361 607 }
362 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 +
363 617 switch ( $tab ) {
364 618 case 'settings':
365 619 $this->_handleDataAndSettingsPage();
366 620 break;
@@ -368,9 +622,9 @@
368 622 case 'visualizer': // fall through.
369 623 $this->_handleTypesPage();
370 624 break;
371 625 default:
372 - do_action( 'visualizer_pro_handle_tab', $tab, $this->_chart );
626 + // this should never happen.
373 627 break;
374 628 }
375 629 defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
376 630 }
@@ -377,25 +631,26 @@
377 631
378 632 /**
379 633 * Load code editor assets.
380 634 */
381 - private function loadCodeEditorAssets() {
635 + private function loadCodeEditorAssets( $chart_id ) {
382 636 global $wp_version;
383 637
384 - 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() ) {
385 648 return;
386 649 }
387 650
388 - $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 );
389 652
390 - // data tables assets.
391 - wp_register_script( 'visualizer-datatables', '//cdn.datatables.net/1.10.16/js/jquery.dataTables.min.js', array( 'jquery-ui-core' ), Visualizer_Plugin::VERSION );
392 - wp_register_style( 'visualizer-datatables', '//cdn.datatables.net/1.10.16/css/jquery.dataTables.min.css', array(), Visualizer_Plugin::VERSION );
393 - wp_register_style( 'visualizer-datatables-ui', '//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css', array( 'visualizer-datatables' ), Visualizer_Plugin::VERSION );
394 -
395 - wp_enqueue_script( 'visualizer-datatables' );
396 - wp_enqueue_style( 'visualizer-datatables-ui' );
397 -
398 653 if ( version_compare( $wp_version, '4.9.0', '<' ) ) {
399 654 // code mirror assets.
400 655 wp_register_script( 'visualizer-codemirror-core', '//codemirror.net/lib/codemirror.js', array( 'jquery' ), Visualizer_Plugin::VERSION );
401 656 wp_register_script( 'visualizer-codemirror-placeholder', '//codemirror.net/addon/display/placeholder.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
@@ -429,8 +684,25 @@
429 684 return $table_col_mapping;
430 685 }
431 686
432 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 + /**
433 705 * Handle data and settings page
434 706 */
435 707 private function _handleDataAndSettingsPage() {
436 708 if ( isset( $_POST['map_api_key'] ) ) {
@@ -435,30 +707,53 @@
435 707 private function _handleDataAndSettingsPage() {
436 708 if ( isset( $_POST['map_api_key'] ) ) {
437 709 update_option( 'visualizer-map-api-key', $_POST['map_api_key'] );
438 710 }
439 - if ( $_SERVER['REQUEST_METHOD'] == 'POST' && isset( $_GET['nonce'] ) && wp_verify_nonce( $_GET['nonce'] ) ) {
440 - 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' ) {
441 714 $this->_chart->post_status = 'publish';
442 715
443 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.
444 717 // we do not want any difference in data so disable revisions temporarily.
445 - add_filter( 'wp_revisions_to_keep', '__return_false' );
718 + $this->disableRevisionsTemporarily();
719 +
446 720 wp_update_post( $this->_chart->to_array() );
447 721 }
448 722 // save meta data only when it is NOT being canceled.
449 723 if ( ! ( isset( $_POST['cancel'] ) && 1 === intval( $_POST['cancel'] ) ) ) {
450 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 );
451 747 }
452 748 $render = new Visualizer_Render_Page_Send();
453 749 $render->text = sprintf( '[visualizer id="%d"]', $this->_chart->ID );
454 750 wp_iframe( array( $render, 'render' ) );
455 -
456 751 return;
457 752 }
458 753 $data = $this->_getChartArray();
459 754 $sidebar = '';
460 - $sidebar_class = 'Visualizer_Render_Sidebar_Type_' . ucfirst( $data['type'] );
755 + $sidebar_class = $this->load_chart_class_name( $this->_chart->ID );
461 756 if ( class_exists( $sidebar_class, true ) ) {
462 757 $sidebar = new $sidebar_class( $data['settings'] );
463 758 $sidebar->__series = $data['series'];
464 759 $sidebar->__data = $data['data'];
@@ -463,14 +758,14 @@
463 758 $sidebar->__series = $data['series'];
464 759 $sidebar->__data = $data['data'];
465 760 } else {
466 761 $sidebar = apply_filters( 'visualizer_pro_chart_type_sidebar', '', $data );
467 - if ( $sidebar != '' ) {
762 + if ( $sidebar !== '' ) {
468 763 $sidebar->__series = $data['series'];
469 764 $sidebar->__data = $data['data'];
470 765 }
471 766 }
472 - unset( $data['settings']['width'], $data['settings']['height'] );
767 + unset( $data['settings']['width'], $data['settings']['height'], $data['settings']['chartArea'] );
473 768 wp_enqueue_style( 'wp-color-picker' );
474 769 wp_enqueue_style( 'visualizer-frame' );
475 770 wp_enqueue_script( 'visualizer-preview' );
476 771 wp_enqueue_script( 'visualizer-chosen' );
@@ -475,17 +770,37 @@
475 770 wp_enqueue_script( 'visualizer-preview' );
476 771 wp_enqueue_script( 'visualizer-chosen' );
477 772 wp_enqueue_script( 'visualizer-render' );
478 773
479 - $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 + }
480 790
791 + $table_col_mapping = $this->loadCodeEditorAssets( $this->_chart->ID );
792 +
481 793 wp_localize_script(
482 794 'visualizer-render',
483 795 'visualizer',
484 796 array(
485 797 'l10n' => array(
486 - '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' ),
487 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' ),
488 803 ),
489 804 'charts' => array(
490 805 'canvas' => $data,
491 806 'id' => $this->_chart->ID,
@@ -496,19 +811,28 @@
496 811 'url' => admin_url( 'admin-ajax.php' ),
497 812 'nonces' => array(
498 813 'permissions' => wp_create_nonce( Visualizer_Plugin::ACTION_FETCH_PERMISSIONS_DATA ),
499 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 ),
500 818 ),
501 819 'actions' => array(
502 820 'permissions' => Visualizer_Plugin::ACTION_FETCH_PERMISSIONS_DATA,
503 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,
504 825 ),
505 826 ),
506 827 'db_query' => array(
507 828 'tables' => $table_col_mapping,
508 829 ),
509 - 'is_pro' => VISUALIZER_PRO,
830 + 'is_pro' => Visualizer_Module::is_pro(),
510 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,
511 835 )
512 836 );
513 837
514 838 $render = new Visualizer_Render_Page_Data();
@@ -516,21 +840,25 @@
516 840 $render->type = $data['type'];
517 841 $render->custom_css = $data['css'];
518 842 $render->sidebar = $sidebar;
519 843 if ( filter_input( INPUT_GET, 'library', FILTER_VALIDATE_BOOLEAN ) ) {
520 - $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
521 845 ? esc_html__( 'Save Chart', 'visualizer' )
522 846 : esc_html__( 'Create Chart', 'visualizer' );
523 - if ( filter_input( INPUT_GET, 'action' ) == Visualizer_Plugin::ACTION_EDIT_CHART ) {
847 + if ( filter_input( INPUT_GET, 'action' ) === Visualizer_Plugin::ACTION_EDIT_CHART ) {
524 848 $render->cancel_button = esc_html__( 'Cancel', 'visualizer' );
525 849 }
526 850 } else {
527 851 $render->button = esc_attr__( 'Insert Chart', 'visualizer' );
528 852 }
529 - 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' ) ) {
530 857 global $Visualizer_Pro;
531 - $Visualizer_Pro->_enqueueScriptsAndStyles( $data );
858 + $Visualizer_Pro->_enqueueScriptsAndStyles( $data, $this->_chart->ID );
532 859 }
860 +
533 861 $this->_addAction( 'admin_head', 'renderFlattrScript' );
534 862 wp_iframe( array( $render, 'render' ) );
535 863 }
536 864
@@ -542,24 +870,35 @@
542 870 * @access private
543 871 */
544 872 private function _handleTypesPage() {
545 873 // process post request
546 - 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' ) ) ) {
547 875 $type = filter_input( INPUT_POST, 'type' );
548 - 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 +
549 884 // save new chart type
550 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 );
551 887 // if the chart has default data, update it with appropriate default data for new type
552 888 if ( filter_var( get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_DEFAULT_DATA, true ), FILTER_VALIDATE_BOOLEAN ) ) {
553 889 $source = new Visualizer_Source_Csv( VISUALIZER_ABSPATH . DIRECTORY_SEPARATOR . 'samples' . DIRECTORY_SEPARATOR . $type . '.csv' );
554 890 $source->fetch();
555 - $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 ) );
556 892 wp_update_post( $this->_chart->to_array() );
557 893 update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
558 894 }
895 +
896 + Visualizer_Module_Utility::set_defaults( $this->_chart );
897 +
559 898 // redirect to next tab
560 899 // changed by Ash/Upwork
561 - wp_redirect( add_query_arg( 'tab', 'settings' ) );
900 + wp_redirect( esc_url_raw( add_query_arg( 'tab', 'settings' ) ) );
562 901
563 902 return;
564 903 }
565 904 }
@@ -564,9 +903,9 @@
564 903 }
565 904 }
566 905 $render = new Visualizer_Render_Page_Types();
567 906 $render->type = get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_CHART_TYPE, true );
568 - $render->types = Visualizer_Module_Admin::_getChartTypesLocalized();
907 + $render->types = Visualizer_Module_Admin::_getChartTypesLocalized( false, false, false, 'types' );
569 908 $render->chart = $this->_chart;
570 909 wp_enqueue_style( 'visualizer-frame' );
571 910 wp_enqueue_script( 'visualizer-frame' );
572 911 wp_iframe( array( $render, 'render' ) );
@@ -582,10 +921,131 @@
582 921 */
583 922 public function renderFlattrScript() {
584 923 echo '';
585 924 }
586 - // changed by Ash/Upwork
925 +
587 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 + /**
588 1048 * Parses uploaded CSV file and saves new data for the chart.
589 1049 *
590 1050 * @since 1.0.0
591 1051 *
@@ -607,9 +1067,9 @@
607 1067
608 1068 // check chart, if chart exists
609 1069 // do not use filter_input as it does not work for phpunit test cases, use filter_var instead
610 1070 $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
611 - 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 ) {
612 1072 if ( ! $can_die ) {
613 1073 return;
614 1074 }
615 1075 status_header( 400 );
@@ -615,8 +1075,10 @@
615 1075 status_header( 400 );
616 1076 exit;
617 1077 }
618 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 +
619 1081 if ( ! isset( $_POST['vz-import-time'] ) ) {
620 1082 apply_filters( 'visualizer_pro_remove_schedule', $chart_id );
621 1083 }
622 1084
@@ -628,27 +1090,58 @@
628 1090
629 1091 // delete "import from db" specific parameters.
630 1092 delete_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY );
631 1093 delete_post_meta( $chart_id, Visualizer_Plugin::CF_DB_SCHEDULE );
1094 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_REMOTE_DB_PARAMS );
632 1095 }
633 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 +
634 1112 $source = null;
635 1113 $render = new Visualizer_Render_Page_Update();
636 - if ( isset( $_POST['remote_data'] ) && filter_var( $_POST['remote_data'], FILTER_VALIDATE_URL ) ) {
637 - $source = new Visualizer_Source_Csv_Remote( $_POST['remote_data'] );
1114 +
1115 + $remote_data = false;
1116 + if ( isset( $_POST['remote_data'] ) && function_exists( 'wp_http_validate_url' ) ) {
1117 + $remote_data = wp_http_validate_url( $_POST['remote_data'] );
1118 + }
1119 + if ( false !== $remote_data ) {
1120 + $source = new Visualizer_Source_Csv_Remote( $remote_data );
638 1121 if ( isset( $_POST['vz-import-time'] ) ) {
639 - apply_filters( 'visualizer_pro_chart_schedule', $chart_id, $_POST['remote_data'], $_POST['vz-import-time'] );
1122 + apply_filters( 'visualizer_pro_chart_schedule', $chart_id, $remote_data, $_POST['vz-import-time'] );
640 1123 }
1124 + // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
641 1125 } elseif ( isset( $_FILES['local_data'] ) && $_FILES['local_data']['error'] == 0 ) {
642 1126 $source = new Visualizer_Source_Csv( $_FILES['local_data']['tmp_name'] );
643 1127 } elseif ( isset( $_POST['chart_data'] ) && strlen( $_POST['chart_data'] ) > 0 ) {
644 - $source = apply_filters( 'visualizer_pro_handle_chart_data', $_POST['chart_data'], '', $chart_id, $_POST );
1128 + $source = $this->handleCSVasString( $_POST['chart_data'], $_POST['editor-type'] );
1129 + update_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR, $_POST['editor-type'] );
1130 + } elseif ( isset( $_POST['table_data'] ) && 'yes' === $_POST['table_data'] ) {
1131 + $source = $this->handleTabularData();
1132 + update_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR, $_POST['editor-type'] );
645 1133 } else {
646 - $render->message = esc_html__( 'CSV file with chart data was not uploaded. Please, try again.', 'visualizer' );
1134 + 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__ );
1135 + $render->message = esc_html__( 'CSV file with chart data was not uploaded. Please try again.', 'visualizer' );
1136 + update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, esc_html__( 'CSV file with chart data was not uploaded. Please try again.', 'visualizer' ) );
647 1137 }
1138 +
1139 + 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__ );
1140 +
648 1141 if ( $source ) {
649 1142 if ( $source->fetch() ) {
650 - $content = $source->getData();
1143 + $content = $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) );
651 1144 $populate = true;
652 1145 if ( is_string( $content ) && is_array( unserialize( $content ) ) ) {
653 1146 $json = unserialize( $content );
654 1147 // if source exists, so should data. if source exists but data is blank, do not populate the chart.
@@ -654,8 +1147,9 @@
654 1147 // if source exists, so should data. if source exists but data is blank, do not populate the chart.
655 1148 // if we populate the data even if it is empty, the chart will show "Table has no columns".
656 1149 if ( array_key_exists( 'source', $json ) && ! empty( $json['source'] ) && ( ! array_key_exists( 'data', $json ) || empty( $json['data'] ) ) ) {
657 1150 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__ );
1151 + update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, sprintf( 'Not populating chart data as source exists (%s) but data is empty!', $json['source'] ) );
658 1152 $populate = false;
659 1153 }
660 1154 }
661 1155 if ( $populate ) {
@@ -661,17 +1155,43 @@
661 1155 if ( $populate ) {
662 1156 $chart->post_content = $content;
663 1157 }
664 1158 wp_update_post( $chart->to_array() );
1159 +
665 1160 update_post_meta( $chart->ID, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
666 1161 update_post_meta( $chart->ID, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
667 1162 update_post_meta( $chart->ID, Visualizer_Plugin::CF_DEFAULT_DATA, 0 );
1163 +
1164 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Updated post for chart %d', $chart_id ), 'debug', __FILE__, __LINE__ );
1165 +
1166 + Visualizer_Module_Utility::set_defaults( $chart, null );
1167 +
1168 + $settings = get_post_meta( $chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
1169 + if ( isset( $settings['series'] ) && ! ( count( $settings['series'] ) - count( $source->getSeries() ) > 1 ) ) {
1170 + $diff_total_series = abs( count( $settings['series'] ) - count( $source->getSeries() ) );
1171 + if ( $diff_total_series ) {
1172 + foreach ( range( 1, $diff_total_series ) as $k => $diff_series ) {
1173 + $settings['series'][] = end( $settings['series'] );
1174 + }
1175 + update_post_meta( $chart->ID, Visualizer_Plugin::CF_SETTINGS, $settings );
1176 + }
1177 + }
1178 +
668 1179 $render->id = $chart->ID;
669 - $render->data = json_encode( $source->getRawData() );
1180 + $render->data = json_encode( $source->getRawData( get_post_meta( $chart->ID, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ) );
670 1181 $render->series = json_encode( $source->getSeries() );
1182 + $render->settings = json_encode( $settings );
671 1183 } else {
672 - $render->message = esc_html__( 'CSV file is broken or invalid. Please, try again.', 'visualizer' );
1184 + $error = $source->get_error();
1185 + if ( empty( $error ) ) {
1186 + $error = esc_html__( 'CSV file is broken or invalid. Please try again.', 'visualizer' );
1187 + }
1188 + $render->message = $error;
1189 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( '%s for chart %d.', $error, $chart_id ), 'error', __FILE__, __LINE__ );
1190 + update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, $error );
673 1191 }
1192 + } else {
1193 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Unknown internal error for chart %d.', $chart_id ), 'error', __FILE__, __LINE__ );
674 1194 }
675 1195 $render->render();
676 1196 if ( ! $can_die ) {
677 1197 return;
@@ -687,27 +1207,18 @@
687 1207 * @access public
688 1208 */
689 1209 public function cloneChart() {
690 1210 $chart_id = $success = false;
691 - $nonce = wp_verify_nonce( filter_input( INPUT_GET, 'nonce' ), Visualizer_Plugin::ACTION_CLONE_CHART );
1211 + $nonce = isset( $_GET['nonce'] ) && wp_verify_nonce( $_GET['nonce'], Visualizer_Plugin::ACTION_CLONE_CHART );
692 1212 $capable = current_user_can( 'edit_posts' );
693 1213 if ( $nonce && $capable ) {
694 - $chart_id = filter_input(
695 - INPUT_GET,
696 - 'chart',
697 - FILTER_VALIDATE_INT,
698 - array(
699 - 'options' => array(
700 - 'min_range' => 1,
701 - ),
702 - )
703 - );
1214 + $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
704 1215 if ( $chart_id ) {
705 1216 $chart = get_post( $chart_id );
706 - $success = $chart && $chart->post_type == Visualizer_Plugin::CPT_VISUALIZER;
1217 + $success = $chart && $chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
707 1218 }
708 1219 }
709 - $redirect = wp_get_referer();
1220 + $redirect = remove_query_arg( 'vaction', wp_get_referer() );
710 1221 if ( $success ) {
711 1222 $new_chart_id = wp_insert_post(
712 1223 array(
713 1224 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
@@ -716,23 +1227,35 @@
716 1227 'post_status' => $chart->post_status,
717 1228 'post_content' => $chart->post_content,
718 1229 )
719 1230 );
720 - if ( $new_chart_id && ! is_wp_error( $new_chart_id ) ) {
721 - add_post_meta( $new_chart_id, Visualizer_Plugin::CF_CHART_TYPE, get_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_TYPE, true ) );
722 - add_post_meta( $new_chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, get_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, true ) );
723 - add_post_meta( $new_chart_id, Visualizer_Plugin::CF_SOURCE, get_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, true ) );
724 - add_post_meta( $new_chart_id, Visualizer_Plugin::CF_SERIES, get_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, true ) );
725 - add_post_meta( $new_chart_id, Visualizer_Plugin::CF_SETTINGS, get_post_meta( $chart_id, Visualizer_Plugin::CF_SETTINGS, true ) );
726 - $redirect = add_query_arg(
727 - array(
728 - 'page' => 'visualizer',
729 - 'type' => filter_input( INPUT_GET, 'type' ),
1231 + if ( is_wp_error( $new_chart_id ) ) {
1232 + 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__ );
1233 + } else {
1234 + $post_meta = get_post_meta( $chart_id );
1235 + foreach ( $post_meta as $key => $value ) {
1236 + if ( strpos( $key, 'visualizer-' ) !== false ) {
1237 + add_post_meta( $new_chart_id, $key, maybe_unserialize( $value[0] ) );
1238 + }
1239 + }
1240 + $redirect = esc_url(
1241 + add_query_arg(
1242 + array(
1243 + 'page' => 'visualizer',
1244 + 'type' => filter_input( INPUT_GET, 'type' ),
1245 + 'vaction' => false,
1246 + ),
1247 + admin_url( 'admin.php' )
730 1248 ),
731 - admin_url( 'upload.php' )
1249 + null,
1250 + 'db'
732 1251 );
733 1252 }
734 1253 }
1254 +
1255 + if ( defined( 'WP_TESTS_DOMAIN' ) ) {
1256 + wp_die();
1257 + }
735 1258 wp_redirect( $redirect );
736 1259 exit;
737 1260 }
738 1261
@@ -778,9 +1301,12 @@
778 1301 $data = $this->_getChartArray();
779 1302 $render = new Visualizer_Render_Page_Data();
780 1303 $render->chart = $this->_chart;
781 1304 $render->type = $data['type'];
782 - unset( $data['settings']['width'], $data['settings']['height'] );
1305 +
1306 + if ( $data && $data['settings'] ) {
1307 + unset( $data['settings']['width'], $data['settings']['height'], $data['settings']['chartArea'] );
1308 + }
783 1309 wp_enqueue_style( 'visualizer-frame' );
784 1310 wp_enqueue_script( 'visualizer-render' );
785 1311 wp_localize_script(
786 1312 'visualizer-render',
@@ -786,9 +1312,9 @@
786 1312 'visualizer-render',
787 1313 'visualizer',
788 1314 array(
789 1315 'l10n' => array(
790 - 'invalid_source' => esc_html__( 'You have entered invalid URL. Please, insert proper URL.', 'visualizer' ),
1316 + 'invalid_source' => esc_html__( 'You have entered an invalid URL. Please provide a valid URL.', 'visualizer' ),
791 1317 'loading' => esc_html__( 'Loading...', 'visualizer' ),
792 1318 ),
793 1319 'charts' => array(
794 1320 'canvas' => $data,
@@ -794,13 +1320,16 @@
794 1320 'canvas' => $data,
795 1321 ),
796 1322 )
797 1323 );
798 - // Added by Ash/Upwork
799 - if ( VISUALIZER_PRO ) {
1324 +
1325 + do_action( 'visualizer_enqueue_scripts_and_styles', $data, $this->_chart->ID );
1326 +
1327 + if ( Visualizer_Module::is_pro() && Visualizer_Module::is_pro_older_than( '1.9.0' ) ) {
800 1328 global $Visualizer_Pro;
801 - $Visualizer_Pro->_enqueueScriptsAndStyles( $data );
1329 + $Visualizer_Pro->_enqueueScriptsAndStyles( $data, $this->_chart->ID );
802 1330 }
1331 +
803 1332 // Added by Ash/Upwork
804 1333 $this->_addAction( 'admin_head', 'renderFlattrScript' );
805 1334 wp_iframe( array( $render, 'render' ) );
806 1335 }
@@ -813,13 +1342,14 @@
813 1342 public function getQueryData() {
814 1343 check_ajax_referer( Visualizer_Plugin::ACTION_FETCH_DB_DATA . Visualizer_Plugin::VERSION, 'security' );
815 1344
816 1345 $params = wp_parse_args( $_POST['params'] );
817 - $source = new Visualizer_Source_Query( stripslashes( $params['query'] ) );
1346 + $chart_id = filter_var( $params['chart_id'], FILTER_VALIDATE_INT );
1347 +
1348 + $source = new Visualizer_Source_Query( stripslashes( $params['query'] ), $chart_id, $params );
818 1349 $html = $source->fetch( true );
819 - $error = '';
820 - if ( empty( $html ) ) {
821 - $error = $source->get_error();
1350 + $error = $source->get_error();
1351 + if ( ! empty( $error ) ) {
822 1352 wp_send_json_error( array( 'msg' => $error ) );
823 1353 }
824 1354 wp_send_json_success( array( 'table' => $html ) );
825 1355 }
@@ -842,21 +1372,42 @@
842 1372 ),
843 1373 )
844 1374 );
845 1375
1376 + $hours = filter_input(
1377 + INPUT_POST,
1378 + 'refresh',
1379 + FILTER_VALIDATE_FLOAT,
1380 + array(
1381 + 'options' => array(
1382 + 'min_range' => -1,
1383 + 'max_range' => apply_filters( 'visualizer_is_business', false ) ? PHP_INT_MAX : -1,
1384 + ),
1385 + )
1386 + );
1387 +
1388 + if ( ! is_numeric( $hours ) ) {
1389 + $hours = -1;
1390 + }
1391 +
846 1392 $render = new Visualizer_Render_Page_Update();
847 1393 if ( $chart_id ) {
848 1394 $params = wp_parse_args( $_POST['params'] );
849 - $source = new Visualizer_Source_Query( stripslashes( $params['query'] ) );
1395 + $source = new Visualizer_Source_Query( stripslashes( $params['query'] ), $chart_id, $params );
850 1396 $source->fetch( false );
851 1397 $error = $source->get_error();
852 1398 if ( empty( $error ) ) {
853 - $hours = $_POST['refresh'];
854 1399 update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY, stripslashes( $params['query'] ) );
855 1400 update_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
856 1401 update_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
857 1402 update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_SCHEDULE, $hours );
858 1403 update_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, 0 );
1404 + if ( isset( $params['db_type'] ) && $params['db_type'] !== Visualizer_Plugin::WP_DB_NAME ) {
1405 + $remote_db_params = $params;
1406 + unset( $remote_db_params['query'] );
1407 + unset( $remote_db_params['chart_id'] );
1408 + update_post_meta( $chart_id, Visualizer_Plugin::CF_REMOTE_DB_PARAMS, $remote_db_params );
1409 + }
859 1410
860 1411 $schedules = get_option( Visualizer_Plugin::CF_DB_SCHEDULE, array() );
861 1412 $schedules[ $chart_id ] = time() + $hours * HOUR_IN_SECONDS;
862 1413 update_option( Visualizer_Plugin::CF_DB_SCHEDULE, $schedules );
@@ -863,13 +1414,14 @@
863 1414
864 1415 wp_update_post(
865 1416 array(
866 1417 'ID' => $chart_id,
867 - 'post_content' => $source->getData(),
1418 + 'post_content' => $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ),
868 1419 )
869 1420 );
870 - $render->data = json_encode( $source->getRawData() );
1421 + $render->data = json_encode( $source->getRawData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ) );
871 1422 $render->series = json_encode( $source->getSeries() );
1423 + $render->id = $chart_id;
872 1424 } else {
873 1425 $render->message = $error;
874 1426 }
875 1427 }
@@ -898,13 +1450,76 @@
898 1450 ),
899 1451 )
900 1452 );
901 1453
902 - $hours = $_POST['refresh'];
1454 + $hours = filter_input(
1455 + INPUT_POST,
1456 + 'refresh',
1457 + FILTER_VALIDATE_INT,
1458 + array(
1459 + 'options' => array(
1460 + 'min_range' => -1,
1461 + 'max_range' => apply_filters( 'visualizer_is_business', false ) ? PHP_INT_MAX : -1,
1462 + ),
1463 + )
1464 + );
903 1465
1466 + if ( 0 !== $hours && empty( $hours ) ) {
1467 + $hours = -1;
1468 + }
1469 +
904 1470 do_action( 'visualizer_save_filter', $chart_id, $hours );
905 1471
906 1472 if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
907 1473 defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
908 1474 }
1475 + }
1476 +
1477 + /**
1478 + * Save chart image.
1479 + *
1480 + * @param string $base64_img Chart image.
1481 + * @param int $chart_id Chart ID.
1482 + * @param bool $save_attachment Save attachment.
1483 + * @return attachment ID
1484 + */
1485 + public function save_chart_image( $base64_img, $chart_id, $save_attachment = true ) {
1486 + // Delete old chart image.
1487 + $old_attachment_id = get_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_IMAGE, true );
1488 + if ( $old_attachment_id ) {
1489 + wp_delete_attachment( $old_attachment_id, true );
1490 + }
1491 +
1492 + if ( ! $save_attachment ) {
1493 + return 0;
1494 + }
1495 +
1496 + // Upload dir.
1497 + $upload_dir = wp_upload_dir();
1498 + $upload_path = str_replace( '/', DIRECTORY_SEPARATOR, $upload_dir['path'] ) . DIRECTORY_SEPARATOR;
1499 +
1500 + $img = str_replace( 'data:image/png;base64,', '', $base64_img );
1501 + $img = str_replace( ' ', '+', $img );
1502 + $decoded = base64_decode( $img );
1503 + $filename = 'visualization-' . $chart_id . '.png';
1504 + $file_type = 'image/png';
1505 + $hashed_filename = $filename;
1506 +
1507 + // Save the image in the uploads directory.
1508 + require_once ABSPATH . '/wp-admin/includes/file.php';
1509 + \WP_Filesystem();
1510 + global $wp_filesystem;
1511 + $upload_file = $wp_filesystem->put_contents( $upload_path . $hashed_filename, $decoded );
1512 +
1513 + // Insert new chart image.
1514 + $attachment = array(
1515 + 'post_mime_type' => $file_type,
1516 + 'post_title' => preg_replace( '/\.[^.]+$/', '', basename( $hashed_filename ) ),
1517 + 'post_content' => '',
1518 + 'post_status' => 'inherit',
1519 + 'guid' => $upload_dir['url'] . '/' . basename( $hashed_filename ),
1520 + );
1521 +
1522 + $attach_id = wp_insert_attachment( $attachment, $upload_dir['path'] . '/' . $hashed_filename );
1523 + return $attach_id;
909 1524 }
910 1525 }