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