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