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