PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.5.1
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.5.1
4.0.7 4.0.6 4.0.5 4.0.4 4.0.3 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.1 3.1.2 3.1.3 3.10.0 3.10.1 3.10.10 3.10.11 3.10.12 3.10.13 3.10.14 3.10.15 3.10.2 3.10.3 3.10.4 All 148 releases
visualizer / classes / Visualizer / Module / Chart.php

Chart.php in Visualizer – Tables & Charts Manager with Built-in AI Generator 3.5.1, at classes/Visualizer/Module/Chart.php

1,421 lines 49.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 // +----------------------------------------------------------------------+
3 // | Copyright 2013 Madpixels (email : visualizer@madpixels.net) |
4 // +----------------------------------------------------------------------+
5 // | This program is free software; you can redistribute it and/or modify |
6 // | it under the terms of the GNU General Public License, version 2, as |
7 // | published by the Free Software Foundation. |
8 // | |
9 // | This program is distributed in the hope that it will be useful, |
10 // | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 // | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 // | GNU General Public License for more details. |
13 // | |
14 // | You should have received a copy of the GNU General Public License |
15 // | along with this program; if not, write to the Free Software |
16 // | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, |
17 // | MA 02110-1301 USA |
18 // +----------------------------------------------------------------------+
19 // | Author: Eugene Manuilov <eugene@manuilov.org> |
20 // +----------------------------------------------------------------------+
21 /**
22 * The module for all stuff related to getting, editing, creating and deleting charts.
23 *
24 * @category Visualizer
25 * @package Module
26 *
27 * @since 1.0.0
28 */
29 class Visualizer_Module_Chart extends Visualizer_Module {
30
31 const NAME = __CLASS__;
32
33 /**
34 * The chart object.
35 *
36 * @since 1.0.0
37 *
38 * @access private
39 * @var WP_Post
40 */
41 private $_chart;
42
43 /**
44 * Constructor.
45 *
46 * @since 1.0.0
47 *
48 * @access public
49 *
50 * @param Visualizer_Plugin $plugin The instance of the plugin.
51 */
52 public function __construct( Visualizer_Plugin $plugin ) {
53 parent::__construct( $plugin );
54 $this->_addAjaxAction( Visualizer_Plugin::ACTION_GET_CHARTS, 'getCharts' );
55 $this->_addAjaxAction( Visualizer_Plugin::ACTION_DELETE_CHART, 'deleteChart' );
56 $this->_addAjaxAction( Visualizer_Plugin::ACTION_CREATE_CHART, 'renderChartPages' );
57 $this->_addAjaxAction( Visualizer_Plugin::ACTION_EDIT_CHART, 'renderChartPages' );
58 $this->_addAjaxAction( Visualizer_Plugin::ACTION_UPLOAD_DATA, 'uploadData' );
59 $this->_addAjaxAction( Visualizer_Plugin::ACTION_CLONE_CHART, 'cloneChart' );
60 $this->_addAjaxAction( Visualizer_Plugin::ACTION_EXPORT_DATA, 'exportData' );
61
62 $this->_addAjaxAction( Visualizer_Plugin::ACTION_FETCH_DB_DATA, 'getQueryData' );
63 $this->_addAjaxAction( Visualizer_Plugin::ACTION_SAVE_DB_QUERY, 'saveQuery' );
64
65 $this->_addAjaxAction( Visualizer_Plugin::ACTION_JSON_GET_ROOTS, 'getJsonRoots' );
66 $this->_addAjaxAction( Visualizer_Plugin::ACTION_JSON_GET_DATA, 'getJsonData' );
67 $this->_addAjaxAction( Visualizer_Plugin::ACTION_JSON_SET_DATA, 'setJsonData' );
68 $this->_addAjaxAction( Visualizer_Plugin::ACTION_JSON_SET_SCHEDULE, 'setJsonSchedule' );
69
70 $this->_addAjaxAction( Visualizer_Plugin::ACTION_SAVE_FILTER_QUERY, 'saveFilter' );
71
72 $this->_addFilter( 'visualizer_get_sidebar', 'getSidebar', 10, 2 );
73
74 }
75
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 /**
283 * Fetches charts from database.
284 *
285 * This method is also called from the media pop-up (classic editor: create a post and add chart from insert content).
286 *
287 * @since 1.0.0
288 *
289 * @access public
290 */
291 public function getCharts() {
292 $query_args = array(
293 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
294 'posts_per_page' => 9,
295 'paged' => filter_input(
296 INPUT_GET,
297 'page',
298 FILTER_VALIDATE_INT,
299 array(
300 'options' => array(
301 'min_range' => 1,
302 'default' => 1,
303 ),
304 )
305 ),
306 );
307 $filter = filter_input( INPUT_GET, 's', FILTER_SANITIZE_STRING );
308 if ( empty( $filter ) ) {
309 // 'filter' is from the modal from the add media button.
310 $filter = filter_input( INPUT_GET, 'filter', FILTER_SANITIZE_STRING );
311 }
312
313 if ( $filter && in_array( $filter, Visualizer_Plugin::getChartTypes(), true ) ) {
314 $query_args['meta_query'] = array(
315 array(
316 'key' => Visualizer_Plugin::CF_CHART_TYPE,
317 'value' => $filter,
318 'compare' => '=',
319 ),
320 );
321 }
322 $query = new WP_Query( $query_args );
323 $charts = array();
324 while ( $query->have_posts() ) {
325 $chart = $query->next_post();
326 $chart_data = $this->_getChartArray( $chart );
327 $chart_data['id'] = $chart->ID;
328 $chart_data['library'] = $this->load_chart_type( $chart->ID );
329 $css = '';
330 $settings = $chart_data['settings'];
331 $arguments = $this->get_inline_custom_css( 'visualizer-chart-' . $chart->ID, $settings );
332 if ( ! empty( $arguments ) ) {
333 $css = $arguments[0];
334 $settings = $arguments[1];
335 }
336 $chart_data['settings'] = $settings;
337 $chart_data['css'] = $css;
338 $charts[] = $chart_data;
339 }
340 self::_sendResponse(
341 array(
342 'success' => true,
343 'data' => $charts,
344 'total' => $query->max_num_pages,
345 )
346 );
347 }
348
349 /**
350 * Returns chart data required for rendering.
351 *
352 * @since 1.0.0
353 *
354 * @access private
355 *
356 * @param WP_Post $chart The chart object.
357 *
358 * @return array The array of chart data.
359 */
360 private function _getChartArray( WP_Post $chart = null ) {
361 if ( is_null( $chart ) ) {
362 $chart = $this->_chart;
363 }
364 $type = get_post_meta( $chart->ID, Visualizer_Plugin::CF_CHART_TYPE, true );
365 $series = apply_filters( Visualizer_Plugin::FILTER_GET_CHART_SERIES, get_post_meta( $chart->ID, Visualizer_Plugin::CF_SERIES, true ), $chart->ID, $type );
366 $data = self::get_chart_data( $chart, $type );
367 $library = $this->load_chart_type( $chart->ID );
368
369 $settings = get_post_meta( $chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
370 $settings = apply_filters( Visualizer_Plugin::FILTER_GET_CHART_SETTINGS, $settings, $chart->ID, $type );
371 if ( ! empty( $atts['settings'] ) ) {
372 $settings = apply_filters( $atts['settings'], $settings, $chart->ID, $type );
373 }
374
375 $css = '';
376 $arguments = $this->get_inline_custom_css( 'visualizer-' . $chart->ID, $settings );
377 if ( ! empty( $arguments ) ) {
378 $css = $arguments[0];
379 $settings = $arguments[1];
380 }
381
382 $date_formats = Visualizer_Source::get_date_formats_if_exists( $series, $data );
383
384 return array(
385 'type' => $type,
386 'series' => $series,
387 'settings' => $settings,
388 'data' => $data,
389 'library' => $library,
390 'css' => $css,
391 'date_formats' => $date_formats,
392 );
393 }
394
395 /**
396 * Sends json response.
397 *
398 * @since 1.0.0
399 *
400 * @access private
401 *
402 * @param array $results The response array.
403 */
404 public static function _sendResponse( $results ) {
405 header( 'Content-type: application/json' );
406 nocache_headers();
407 echo json_encode( $results );
408 defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
409 }
410
411 /**
412 * Deletes a chart from database.
413 *
414 * @since 1.0.0
415 * @uses wp_delete_post() To delete a chart.
416 *
417 * @access public
418 */
419 public function deleteChart() {
420 $is_post = $_SERVER['REQUEST_METHOD'] === 'POST';
421 $input_method = $is_post ? INPUT_POST : INPUT_GET;
422 $chart_id = $success = false;
423 $nonce = wp_verify_nonce( filter_input( $input_method, 'nonce' ) );
424 $capable = current_user_can( 'delete_posts' );
425 if ( $nonce && $capable ) {
426 $chart_id = filter_input(
427 $input_method,
428 'chart',
429 FILTER_VALIDATE_INT,
430 array(
431 'options' => array(
432 'min_range' => 1,
433 ),
434 )
435 );
436 if ( $chart_id ) {
437 $chart = get_post( $chart_id );
438 $success = $chart && $chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
439 }
440 }
441 if ( $success ) {
442 wp_delete_post( $chart_id, true );
443 }
444 if ( $is_post ) {
445 self::_sendResponse(
446 array(
447 'success' => $success,
448 )
449 );
450 }
451 wp_redirect( remove_query_arg( 'vaction', wp_get_referer() ) );
452 exit;
453 }
454
455 /**
456 * Delete charts that are still in auto-draft mode.
457 */
458 private function deleteOldCharts() {
459 $query = new WP_Query(
460 array(
461 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
462 'post_status' => 'auto-draft',
463 'fields' => 'ids',
464 'update_post_meta_cache' => false,
465 'update_post_term_cache' => false,
466 'posts_per_page' => 50,
467 'date_query' => array(
468 array(
469 'before' => 'today',
470 ),
471 ),
472 )
473 );
474
475 if ( $query->have_posts() ) {
476 $ids = array();
477 while ( $query->have_posts() ) {
478 wp_delete_post( $query->next_post(), true );
479 }
480 }
481 }
482
483 /**
484 * Renders appropriate page for chart builder. Creates new auto draft chart
485 * if no chart has been specified.
486 *
487 * @since 1.0.0
488 *
489 * @access public
490 */
491 public function renderChartPages() {
492 defined( 'IFRAME_REQUEST' ) || define( 'IFRAME_REQUEST', 1 );
493 // Set current screen for the render chart.
494 set_current_screen( 'visualizer_render_chart' );
495 // check chart, if chart not exists, will create new one and redirects to the same page with proper chart id
496 $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
497 if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ) {
498 $this->deleteOldCharts();
499 $default_type = isset( $_GET['type'] ) && ! empty( $_GET['type'] ) ? $_GET['type'] : 'line';
500 $source = new Visualizer_Source_Csv( VISUALIZER_ABSPATH . DIRECTORY_SEPARATOR . 'samples' . DIRECTORY_SEPARATOR . $default_type . '.csv' );
501 $source->fetch();
502 $chart_id = wp_insert_post(
503 array(
504 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
505 'post_title' => 'Visualization',
506 'post_author' => get_current_user_id(),
507 'post_status' => 'auto-draft',
508 'post_content' => $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ),
509 )
510 );
511 if ( $chart_id && ! is_wp_error( $chart_id ) ) {
512 add_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_TYPE, $default_type );
513 add_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, 1 );
514 add_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
515 add_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
516 add_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_LIBRARY, '' );
517 add_post_meta(
518 $chart_id,
519 Visualizer_Plugin::CF_SETTINGS,
520 array(
521 'focusTarget' => 'datum',
522 )
523 );
524 do_action( 'visualizer_pro_new_chart_defaults', $chart_id );
525 }
526 wp_redirect( add_query_arg( 'chart', (int) $chart_id ) );
527 defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
528 }
529
530 $lib = $this->load_chart_type( $chart_id );
531
532 // the alpha color picker (RGBA) is not supported by google.
533 $color_picker_dep = 'wp-color-picker';
534 if ( in_array( $lib, array( 'chartjs', 'datatables' ), true ) && ! wp_script_is( 'wp-color-picker-alpha', 'registered' ) ) {
535 wp_register_script( 'wp-color-picker-alpha', VISUALIZER_ABSURL . 'js/lib/wp-color-picker-alpha.min.js', array( 'wp-color-picker' ), Visualizer_Plugin::VERSION );
536 $color_picker_dep = 'wp-color-picker-alpha';
537 }
538
539 // enqueue and register scripts and styles
540 wp_register_script( 'visualizer-chosen', VISUALIZER_ABSURL . 'js/lib/chosen.jquery.min.js', array( 'jquery' ), Visualizer_Plugin::VERSION );
541 wp_register_style( 'visualizer-chosen', VISUALIZER_ABSURL . 'css/lib/chosen.min.css', array(), Visualizer_Plugin::VERSION );
542
543 wp_register_style( 'visualizer-frame', VISUALIZER_ABSURL . 'css/frame.css', array( 'visualizer-chosen' ), Visualizer_Plugin::VERSION );
544 wp_register_script( 'visualizer-frame', VISUALIZER_ABSURL . 'js/frame.js', array( 'visualizer-chosen', 'jquery-ui-accordion', 'jquery-ui-tabs' ), Visualizer_Plugin::VERSION, true );
545 wp_register_script( 'visualizer-customization', $this->get_user_customization_js(), array(), null, true );
546 wp_register_script(
547 'visualizer-render',
548 VISUALIZER_ABSURL . 'js/render-facade.js',
549 apply_filters( 'visualizer_assets_render', array( 'visualizer-frame', 'visualizer-customization' ), false ),
550 Visualizer_Plugin::VERSION,
551 true
552 );
553 wp_register_script(
554 'visualizer-preview',
555 VISUALIZER_ABSURL . 'js/preview.js',
556 array(
557 $color_picker_dep,
558 'visualizer-render',
559 ),
560 Visualizer_Plugin::VERSION,
561 true
562 );
563 wp_register_script( 'visualizer-editor-simple', VISUALIZER_ABSURL . 'js/simple-editor.js', array( 'jquery' ), Visualizer_Plugin::VERSION, true );
564
565 do_action( 'visualizer_add_scripts' );
566
567 if ( Visualizer_Module::is_pro() && Visualizer_Module::is_pro_older_than( '1.9.0' ) ) {
568 global $Visualizer_Pro;
569 $Visualizer_Pro->_addScriptsAndStyles();
570 }
571
572 // dispatch pages
573 $this->_chart = get_post( $chart_id );
574 $tab = isset( $_GET['tab'] ) && ! empty( $_GET['tab'] ) ? $_GET['tab'] : 'visualizer';
575
576 // skip chart type pages only for existing charts.
577 if ( VISUALIZER_SKIP_CHART_TYPE_PAGE && 'auto-draft' !== $this->_chart->post_status && ( ! empty( $_GET['tab'] ) && 'visualizer' === $_GET['tab'] ) ) {
578 $tab = 'settings';
579 }
580
581 if ( isset( $_POST['cancel'] ) && 1 === intval( $_POST['cancel'] ) ) {
582 // if the cancel button is clicked.
583 $this->undoRevisions( $chart_id, true );
584 } elseif ( isset( $_POST['save'] ) && 1 === intval( $_POST['save'] ) ) {
585 $this->handlePermissions();
586 // if the save button is clicked.
587 $this->undoRevisions( $chart_id, false );
588 } else {
589 // if the edit button is clicked.
590 $this->_chart = $this->handleExistingRevisions( $chart_id, $this->_chart );
591 }
592
593 switch ( $tab ) {
594 case 'settings':
595 $this->_handleDataAndSettingsPage();
596 break;
597 case 'type': // fall through.
598 case 'visualizer': // fall through.
599 $this->_handleTypesPage();
600 break;
601 default:
602 // this should never happen.
603 break;
604 }
605 defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
606 }
607
608 /**
609 * Load code editor assets.
610 */
611 private function loadCodeEditorAssets( $chart_id ) {
612 global $wp_version;
613
614 $wp_scripts = wp_scripts();
615
616 // data tables assets.
617 wp_register_script( 'visualizer-datatables', VISUALIZER_ABSURL . 'js/lib/datatables.min.js', array( 'jquery-ui-core' ), Visualizer_Plugin::VERSION );
618 wp_register_style( 'visualizer-datatables', VISUALIZER_ABSURL . 'css/lib/datatables.min.css', array(), Visualizer_Plugin::VERSION );
619 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 );
620 wp_enqueue_script( 'visualizer-datatables' );
621 wp_enqueue_style( 'visualizer-jquery-ui' );
622
623 if ( ! Visualizer_Module::is_pro() ) {
624 return;
625 }
626
627 $table_col_mapping = Visualizer_Source_Query_Params::get_all_db_tables_column_mapping( $chart_id );
628
629 if ( version_compare( $wp_version, '4.9.0', '<' ) ) {
630 // code mirror assets.
631 wp_register_script( 'visualizer-codemirror-core', '//codemirror.net/lib/codemirror.js', array( 'jquery' ), Visualizer_Plugin::VERSION );
632 wp_register_script( 'visualizer-codemirror-placeholder', '//codemirror.net/addon/display/placeholder.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
633 wp_register_script( 'visualizer-codemirror-matchbrackets', '//codemirror.net/addon/edit/matchbrackets.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
634 wp_register_script( 'visualizer-codemirror-closebrackets', '//codemirror.net/addon/edit/closebrackets.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
635 wp_register_script( 'visualizer-codemirror-sql', '//codemirror.net/mode/sql/sql.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
636 wp_register_script( 'visualizer-codemirror-sql-hint', '//codemirror.net/addon/hint/sql-hint.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
637 wp_register_script( 'visualizer-codemirror-hint', '//codemirror.net/addon/hint/show-hint.js', array( 'visualizer-codemirror-sql', 'visualizer-codemirror-sql-hint', 'visualizer-codemirror-placeholder', 'visualizer-codemirror-matchbrackets', 'visualizer-codemirror-closebrackets' ), Visualizer_Plugin::VERSION );
638 wp_register_style( 'visualizer-codemirror-core', '//codemirror.net/lib/codemirror.css', array(), Visualizer_Plugin::VERSION );
639 wp_register_style( 'visualizer-codemirror-hint', '//codemirror.net/addon/hint/show-hint.css', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
640
641 wp_enqueue_script( 'visualizer-codemirror-hint' );
642 wp_enqueue_style( 'visualizer-codemirror-hint' );
643 } else {
644 wp_enqueue_code_editor(
645 array(
646 'type' => 'sql',
647 'codemirror' => array(
648 'autofocus' => true,
649 'lineWrapping' => true,
650 'dragDrop' => false,
651 'matchBrackets' => true,
652 'autoCloseBrackets' => true,
653 'extraKeys' => array( 'Ctrl-Space' => 'autocomplete' ),
654 'hintOptions' => array( 'tables' => $table_col_mapping ),
655 ),
656 )
657 );
658 }
659
660 return $table_col_mapping;
661 }
662
663 /**
664 * Handle permissions from the new conslidated settings sidebar.
665 */
666 private function handlePermissions() {
667 if ( ! Visualizer_Module::is_pro() ) {
668 return;
669 }
670
671 // we will not support old free and new pro.
672 // handling new free and old pro.
673 if ( has_action( 'visualizer_pro_handle_tab' ) ) {
674 do_action( 'visualizer_pro_handle_tab', 'permissions', $this->_chart );
675 } else {
676 do_action( 'visualizer_handle_permissions', $this->_chart );
677 }
678 }
679
680 /**
681 * Handle data and settings page
682 */
683 private function _handleDataAndSettingsPage() {
684 if ( isset( $_POST['map_api_key'] ) ) {
685 update_option( 'visualizer-map-api-key', $_POST['map_api_key'] );
686 }
687
688 if ( $_SERVER['REQUEST_METHOD'] === 'POST' && isset( $_GET['nonce'] ) && wp_verify_nonce( $_GET['nonce'] ) ) {
689 if ( $this->_chart->post_status === 'auto-draft' ) {
690 $this->_chart->post_status = 'publish';
691
692 // 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.
693 // we do not want any difference in data so disable revisions temporarily.
694 $this->disableRevisionsTemporarily();
695
696 wp_update_post( $this->_chart->to_array() );
697 }
698 // save meta data only when it is NOT being canceled.
699 if ( ! ( isset( $_POST['cancel'] ) && 1 === intval( $_POST['cancel'] ) ) ) {
700 update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, $_POST );
701
702 // we will keep a parameter called 'internal_title' that will be set to the given title or, if empty, the chart ID
703 // this will help in searching with the chart id.
704 $settings = get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
705 $title = null;
706 if ( isset( $settings['title'] ) && ! empty( $settings['title'] ) ) {
707 $title = $settings['title'];
708 if ( is_array( $title ) ) {
709 $title = $settings['title']['text'];
710 }
711 }
712 if ( empty( $title ) ) {
713 $title = $this->_chart->ID;
714 }
715 $settings['internal_title'] = $title;
716 update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, $settings );
717 }
718 $render = new Visualizer_Render_Page_Send();
719 $render->text = sprintf( '[visualizer id="%d"]', $this->_chart->ID );
720 wp_iframe( array( $render, 'render' ) );
721 return;
722 }
723 $data = $this->_getChartArray();
724 $sidebar = '';
725 $sidebar_class = $this->load_chart_class_name( $this->_chart->ID );
726 if ( class_exists( $sidebar_class, true ) ) {
727 $sidebar = new $sidebar_class( $data['settings'] );
728 $sidebar->__series = $data['series'];
729 $sidebar->__data = $data['data'];
730 } else {
731 $sidebar = apply_filters( 'visualizer_pro_chart_type_sidebar', '', $data );
732 if ( $sidebar !== '' ) {
733 $sidebar->__series = $data['series'];
734 $sidebar->__data = $data['data'];
735 }
736 }
737 unset( $data['settings']['width'], $data['settings']['height'], $data['settings']['chartArea'] );
738 wp_enqueue_style( 'wp-color-picker' );
739 wp_enqueue_style( 'visualizer-frame' );
740 wp_enqueue_script( 'visualizer-preview' );
741 wp_enqueue_script( 'visualizer-chosen' );
742 wp_enqueue_script( 'visualizer-render' );
743
744 if ( Visualizer_Module::can_show_feature( 'simple-editor' ) ) {
745 wp_enqueue_script( 'visualizer-editor-simple' );
746 wp_localize_script(
747 'visualizer-editor-simple',
748 'visualizer1',
749 array(
750 'ajax' => array(
751 'url' => admin_url( 'admin-ajax.php' ),
752 'nonces' => array(
753 ),
754 'actions' => array(
755 ),
756 ),
757 )
758 );
759 }
760
761 $table_col_mapping = $this->loadCodeEditorAssets( $this->_chart->ID );
762
763 wp_localize_script(
764 'visualizer-render',
765 'visualizer',
766 array(
767 'l10n' => array(
768 'invalid_source' => esc_html__( 'You have entered an invalid URL. Please provide a valid URL.', 'visualizer' ),
769 'loading' => esc_html__( 'Loading...', 'visualizer' ),
770 'json_error' => esc_html__( 'An error occured in fetching data.', 'visualizer' ),
771 'select_columns' => esc_html__( 'Please select a few columns to include in the chart.', 'visualizer' ),
772 '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' ),
773 ),
774 'charts' => array(
775 'canvas' => $data,
776 'id' => $this->_chart->ID,
777 ),
778 'language' => $this->get_language(),
779 'map_api_key' => get_option( 'visualizer-map-api-key' ),
780 'ajax' => array(
781 'url' => admin_url( 'admin-ajax.php' ),
782 'nonces' => array(
783 'permissions' => wp_create_nonce( Visualizer_Plugin::ACTION_FETCH_PERMISSIONS_DATA ),
784 'db_get_data' => wp_create_nonce( Visualizer_Plugin::ACTION_FETCH_DB_DATA . Visualizer_Plugin::VERSION ),
785 'json_get_roots' => wp_create_nonce( Visualizer_Plugin::ACTION_JSON_GET_ROOTS . Visualizer_Plugin::VERSION ),
786 'json_get_data' => wp_create_nonce( Visualizer_Plugin::ACTION_JSON_GET_DATA . Visualizer_Plugin::VERSION ),
787 'json_set_schedule' => wp_create_nonce( Visualizer_Plugin::ACTION_JSON_SET_SCHEDULE . Visualizer_Plugin::VERSION ),
788 ),
789 'actions' => array(
790 'permissions' => Visualizer_Plugin::ACTION_FETCH_PERMISSIONS_DATA,
791 'db_get_data' => Visualizer_Plugin::ACTION_FETCH_DB_DATA,
792 'json_get_roots' => Visualizer_Plugin::ACTION_JSON_GET_ROOTS,
793 'json_get_data' => Visualizer_Plugin::ACTION_JSON_GET_DATA,
794 'json_set_schedule' => Visualizer_Plugin::ACTION_JSON_SET_SCHEDULE,
795 ),
796 ),
797 'db_query' => array(
798 'tables' => $table_col_mapping,
799 ),
800 'is_pro' => Visualizer_Module::is_pro(),
801 'page_type' => 'chart',
802 'json_tag_separator' => Visualizer_Source_Json::TAG_SEPARATOR,
803 'json_tag_separator_view' => Visualizer_Source_Json::TAG_SEPARATOR_VIEW,
804 'is_front' => false,
805 )
806 );
807
808 $render = new Visualizer_Render_Page_Data();
809 $render->chart = $this->_chart;
810 $render->type = $data['type'];
811 $render->custom_css = $data['css'];
812 $render->sidebar = $sidebar;
813 if ( filter_input( INPUT_GET, 'library', FILTER_VALIDATE_BOOLEAN ) ) {
814 $render->button = filter_input( INPUT_GET, 'action' ) === Visualizer_Plugin::ACTION_EDIT_CHART
815 ? esc_html__( 'Save Chart', 'visualizer' )
816 : esc_html__( 'Create Chart', 'visualizer' );
817 if ( filter_input( INPUT_GET, 'action' ) === Visualizer_Plugin::ACTION_EDIT_CHART ) {
818 $render->cancel_button = esc_html__( 'Cancel', 'visualizer' );
819 }
820 } else {
821 $render->button = esc_attr__( 'Insert Chart', 'visualizer' );
822 }
823
824 do_action( 'visualizer_enqueue_scripts_and_styles', $data, $this->_chart->ID );
825
826 if ( Visualizer_Module::is_pro() && Visualizer_Module::is_pro_older_than( '1.9.0' ) ) {
827 global $Visualizer_Pro;
828 $Visualizer_Pro->_enqueueScriptsAndStyles( $data, $this->_chart->ID );
829 }
830
831 $this->_addAction( 'admin_head', 'renderFlattrScript' );
832 wp_iframe( array( $render, 'render' ) );
833 }
834
835 /**
836 * Handles chart type selection page.
837 *
838 * @since 1.0.0
839 *
840 * @access private
841 */
842 private function _handleTypesPage() {
843 // process post request
844 if ( $_SERVER['REQUEST_METHOD'] === 'POST' && wp_verify_nonce( filter_input( INPUT_POST, 'nonce' ) ) ) {
845 $type = filter_input( INPUT_POST, 'type' );
846 $library = filter_input( INPUT_POST, 'chart-library' );
847 if ( in_array( $type, Visualizer_Plugin::getChartTypes(), true ) ) {
848 if ( empty( $library ) ) {
849 // library cannot be empty.
850 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, 'Chart library empty while creating the chart! Aborting...', 'error', __FILE__, __LINE__ );
851 return;
852 }
853
854 // save new chart type
855 update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_CHART_TYPE, $type );
856 update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_CHART_LIBRARY, $library );
857 // if the chart has default data, update it with appropriate default data for new type
858 if ( filter_var( get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_DEFAULT_DATA, true ), FILTER_VALIDATE_BOOLEAN ) ) {
859 $source = new Visualizer_Source_Csv( VISUALIZER_ABSPATH . DIRECTORY_SEPARATOR . 'samples' . DIRECTORY_SEPARATOR . $type . '.csv' );
860 $source->fetch();
861 $this->_chart->post_content = $source->getData( get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) );
862 wp_update_post( $this->_chart->to_array() );
863 update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
864 }
865
866 Visualizer_Module_Utility::set_defaults( $this->_chart );
867
868 // redirect to next tab
869 // changed by Ash/Upwork
870 wp_redirect( add_query_arg( 'tab', 'settings' ) );
871
872 return;
873 }
874 }
875 $render = new Visualizer_Render_Page_Types();
876 $render->type = get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_CHART_TYPE, true );
877 $render->types = Visualizer_Module_Admin::_getChartTypesLocalized( false, false, false, 'types' );
878 $render->chart = $this->_chart;
879 wp_enqueue_style( 'visualizer-frame' );
880 wp_enqueue_script( 'visualizer-frame' );
881 wp_iframe( array( $render, 'render' ) );
882 }
883
884 /**
885 * Renders flattr script in the iframe <head>
886 *
887 * @since 1.4.2
888 * @action admin_head
889 *
890 * @access public
891 */
892 public function renderFlattrScript() {
893 echo '';
894 }
895
896 /**
897 * Processes the CSV that is sent in the request as a string.
898 *
899 * @since 3.2.0
900 */
901 private function handleCSVasString( $data, $editor_type ) {
902 $source = null;
903
904 switch ( $editor_type ) {
905 case 'text':
906 // data coming in from the text editor.
907 $tmpfile = tempnam( get_temp_dir(), Visualizer_Plugin::NAME );
908 $handle = fopen( $tmpfile, 'w' );
909 $values = preg_split( '/[\n\r]+/', stripslashes( trim( $data ) ) );
910 if ( $values ) {
911 foreach ( $values as $row ) {
912 if ( empty( $row ) ) {
913 continue;
914 }
915 // don't use fpucsv here because we need to just dump the data
916 // minus the empty rows
917 // because fputcsv needs to tokenize
918 // we can standardize the CSV enclosure here and replace all ' with "
919 // we can assume that if there are an even number of ' they should be changed to "
920 // but that will screw up words like fo'c'sle
921 // so here let's just assume ' will NOT be used for enclosures
922 fwrite( $handle, $row );
923 fwrite( $handle, PHP_EOL );
924 }
925 }
926 $source = new Visualizer_Source_Csv( $tmpfile );
927 fclose( $handle );
928 break;
929 case 'table':
930 // Import from Chart
931 // fall-through.
932 case 'excel':
933 // data coming in from the excel editor.
934 $source = apply_filters( 'visualizer_pro_handle_chart_data', $data, '' );
935 break;
936 }
937 return $source;
938 }
939
940 /**
941 * Parses the data uploaded as an HTML table.
942 *
943 * @since 3.2.0
944 *
945 * @access private
946 */
947 private function handleTabularData() {
948 $csv = array();
949 // the datatable mentions the headers twice, so lets remove the duplicates.
950 $headers = array_unique( array_filter( $_POST['header'] ) );
951 $types = $_POST['type'];
952
953 // capture all the indexes that correspond to excluded columns.
954 $exclude = array();
955 $index = 0;
956 foreach ( $types as $type ) {
957 if ( empty( $type ) ) {
958 $exclude[] = $index;
959 }
960 $index++;
961 }
962
963 // when N headers are being renamed, the number of headers increases by N
964 // because of the way datatable duplicates header information
965 // so unset the headers that have been renamed.
966 if ( count( $headers ) !== count( $types ) ) {
967 $to = count( $headers );
968 for ( $i = count( $types ); $i < $to; $i++ ) {
969 unset( $headers[ $i + 1 ] );
970 }
971 }
972
973 $columns = array();
974 for ( $i = 0; $i < count( $headers ); $i++ ) {
975 if ( ! isset( $_POST[ 'data' . $i ] ) ) {
976 continue;
977 }
978 $columns[ $i ] = $_POST[ 'data' . $i ];
979 }
980
981 $csv[] = $headers;
982 $csv[] = $types;
983 for ( $j = 0; $j < count( $columns[0] ); $j++ ) {
984 $row = array();
985 for ( $i = 0; $i < count( $headers ); $i++ ) {
986 $row[] = sanitize_text_field( $columns[ $i ][ $j ] );
987 }
988 $csv[] = $row;
989 }
990
991 $tmpfile = tempnam( get_temp_dir(), Visualizer_Plugin::NAME );
992 $handle = fopen( $tmpfile, 'w' );
993
994 if ( $csv ) {
995 $index = 0;
996 foreach ( $csv as $row ) {
997 // remove all the cells corresponding to the excluded headers.
998 foreach ( $exclude as $j ) {
999 unset( $row[ $j ] );
1000 }
1001 fputcsv( $handle, $row );
1002 }
1003 }
1004 $source = new Visualizer_Source_Csv( $tmpfile );
1005 fclose( $handle );
1006 return $source;
1007 }
1008
1009 /**
1010 * Parses uploaded CSV file and saves new data for the chart.
1011 *
1012 * @since 1.0.0
1013 *
1014 * @access public
1015 */
1016 public function uploadData() {
1017 // if this is being called internally from pro and VISUALIZER_DO_NOT_DIE is set.
1018 // otherwise, assume this is a normal web request.
1019 $can_die = ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE );
1020
1021 // validate nonce
1022 if ( ! isset( $_GET['nonce'] ) || ! wp_verify_nonce( $_GET['nonce'] ) ) {
1023 if ( ! $can_die ) {
1024 return;
1025 }
1026 status_header( 403 );
1027 exit;
1028 }
1029
1030 // check chart, if chart exists
1031 // do not use filter_input as it does not work for phpunit test cases, use filter_var instead
1032 $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
1033 if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ) {
1034 if ( ! $can_die ) {
1035 return;
1036 }
1037 status_header( 400 );
1038 exit;
1039 }
1040
1041 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__ );
1042
1043 if ( ! isset( $_POST['vz-import-time'] ) ) {
1044 apply_filters( 'visualizer_pro_remove_schedule', $chart_id );
1045 }
1046
1047 if ( ! isset( $_POST['chart_data_src'] ) || Visualizer_Plugin::CF_SOURCE_FILTER !== $_POST['chart_data_src'] ) {
1048 // delete the filters in case this chart is being uploaded from other data sources
1049 delete_post_meta( $chart_id, Visualizer_Plugin::CF_FILTER_CONFIG );
1050 delete_post_meta( $chart_id, '__transient-' . Visualizer_Plugin::CF_FILTER_CONFIG );
1051 delete_post_meta( $chart_id, '__transient-' . Visualizer_Plugin::CF_DB_QUERY );
1052
1053 // delete "import from db" specific parameters.
1054 delete_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY );
1055 delete_post_meta( $chart_id, Visualizer_Plugin::CF_DB_SCHEDULE );
1056 delete_post_meta( $chart_id, Visualizer_Plugin::CF_REMOTE_DB_PARAMS );
1057 }
1058
1059 // delete json related data.
1060 delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_URL );
1061 delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_ROOT );
1062 delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_PAGING );
1063 delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_HEADERS );
1064
1065 // delete last error
1066 delete_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR );
1067
1068 // delete editor related data.
1069 delete_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR );
1070
1071 // delete this so that a JSON import can be later edited manually without a problem.
1072 delete_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE );
1073
1074 $source = null;
1075 $render = new Visualizer_Render_Page_Update();
1076 if ( isset( $_POST['remote_data'] ) && filter_var( $_POST['remote_data'], FILTER_VALIDATE_URL ) ) {
1077 $source = new Visualizer_Source_Csv_Remote( $_POST['remote_data'] );
1078 if ( isset( $_POST['vz-import-time'] ) ) {
1079 apply_filters( 'visualizer_pro_chart_schedule', $chart_id, $_POST['remote_data'], $_POST['vz-import-time'] );
1080 }
1081 // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
1082 } elseif ( isset( $_FILES['local_data'] ) && $_FILES['local_data']['error'] == 0 ) {
1083 $source = new Visualizer_Source_Csv( $_FILES['local_data']['tmp_name'] );
1084 } elseif ( isset( $_POST['chart_data'] ) && strlen( $_POST['chart_data'] ) > 0 ) {
1085 $source = $this->handleCSVasString( $_POST['chart_data'], $_POST['editor-type'] );
1086 update_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR, $_POST['editor-type'] );
1087 } elseif ( isset( $_POST['table_data'] ) && 'yes' === $_POST['table_data'] ) {
1088 $source = $this->handleTabularData();
1089 update_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR, $_POST['editor-type'] );
1090 } else {
1091 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__ );
1092 $render->message = esc_html__( 'CSV file with chart data was not uploaded. Please try again.', 'visualizer' );
1093 update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, esc_html__( 'CSV file with chart data was not uploaded. Please try again.', 'visualizer' ) );
1094 }
1095
1096 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__ );
1097
1098 if ( $source ) {
1099 if ( $source->fetch() ) {
1100 $content = $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) );
1101 $populate = true;
1102 if ( is_string( $content ) && is_array( unserialize( $content ) ) ) {
1103 $json = unserialize( $content );
1104 // if source exists, so should data. if source exists but data is blank, do not populate the chart.
1105 // if we populate the data even if it is empty, the chart will show "Table has no columns".
1106 if ( array_key_exists( 'source', $json ) && ! empty( $json['source'] ) && ( ! array_key_exists( 'data', $json ) || empty( $json['data'] ) ) ) {
1107 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__ );
1108 update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, sprintf( 'Not populating chart data as source exists (%s) but data is empty!', $json['source'] ) );
1109 $populate = false;
1110 }
1111 }
1112 if ( $populate ) {
1113 $chart->post_content = $content;
1114 }
1115 wp_update_post( $chart->to_array() );
1116 update_post_meta( $chart->ID, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
1117 update_post_meta( $chart->ID, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
1118 update_post_meta( $chart->ID, Visualizer_Plugin::CF_DEFAULT_DATA, 0 );
1119
1120 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Updated post for chart %d', $chart_id ), 'debug', __FILE__, __LINE__ );
1121
1122 Visualizer_Module_Utility::set_defaults( $chart, null );
1123
1124 $settings = get_post_meta( $chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
1125
1126 $render->id = $chart->ID;
1127 $render->data = json_encode( $source->getRawData( get_post_meta( $chart->ID, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ) );
1128 $render->series = json_encode( $source->getSeries() );
1129 $render->settings = json_encode( $settings );
1130 } else {
1131 $error = $source->get_error();
1132 if ( empty( $error ) ) {
1133 $error = esc_html__( 'CSV file is broken or invalid. Please try again.', 'visualizer' );
1134 }
1135 $render->message = $error;
1136 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( '%s for chart %d.', $error, $chart_id ), 'error', __FILE__, __LINE__ );
1137 update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, $error );
1138 }
1139 } else {
1140 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Unknown internal error for chart %d.', $chart_id ), 'error', __FILE__, __LINE__ );
1141 }
1142 $render->render();
1143 if ( ! $can_die ) {
1144 return;
1145 }
1146 defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1147 }
1148
1149 /**
1150 * Clones the chart.
1151 *
1152 * @since 1.0.0
1153 *
1154 * @access public
1155 */
1156 public function cloneChart() {
1157 $chart_id = $success = false;
1158 $nonce = isset( $_GET['nonce'] ) && wp_verify_nonce( $_GET['nonce'], Visualizer_Plugin::ACTION_CLONE_CHART );
1159 $capable = current_user_can( 'edit_posts' );
1160 if ( $nonce && $capable ) {
1161 $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
1162 if ( $chart_id ) {
1163 $chart = get_post( $chart_id );
1164 $success = $chart && $chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
1165 }
1166 }
1167 $redirect = remove_query_arg( 'vaction', wp_get_referer() );
1168 if ( $success ) {
1169 $new_chart_id = wp_insert_post(
1170 array(
1171 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
1172 'post_title' => 'Visualization',
1173 'post_author' => get_current_user_id(),
1174 'post_status' => $chart->post_status,
1175 'post_content' => $chart->post_content,
1176 )
1177 );
1178 if ( is_wp_error( $new_chart_id ) ) {
1179 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__ );
1180 } else {
1181 $post_meta = get_post_meta( $chart_id );
1182 foreach ( $post_meta as $key => $value ) {
1183 if ( strpos( $key, 'visualizer-' ) !== false ) {
1184 add_post_meta( $new_chart_id, $key, maybe_unserialize( $value[0] ) );
1185 }
1186 }
1187 $redirect = add_query_arg(
1188 array(
1189 'page' => 'visualizer',
1190 'type' => filter_input( INPUT_GET, 'type' ),
1191 'vaction' => false,
1192 ),
1193 admin_url( 'admin.php' )
1194 );
1195 }
1196 }
1197
1198 if ( defined( 'WP_TESTS_DOMAIN' ) ) {
1199 wp_die();
1200 }
1201
1202 wp_redirect( $redirect );
1203 exit;
1204 }
1205
1206 /**
1207 * Exports the chart data
1208 *
1209 * @since 1.0.0
1210 *
1211 * @access public
1212 */
1213 public function exportData() {
1214 check_ajax_referer( Visualizer_Plugin::ACTION_EXPORT_DATA . Visualizer_Plugin::VERSION, 'security' );
1215 $capable = current_user_can( 'edit_posts' );
1216 if ( $capable ) {
1217 $chart_id = isset( $_GET['chart'] ) ? filter_var(
1218 $_GET['chart'],
1219 FILTER_VALIDATE_INT,
1220 array(
1221 'options' => array(
1222 'min_range' => 1,
1223 ),
1224 )
1225 ) : '';
1226 if ( $chart_id ) {
1227 $data = $this->_getDataAs( $chart_id, 'csv' );
1228 if ( $data ) {
1229 echo wp_send_json_success( $data );
1230 }
1231 }
1232 }
1233
1234 defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1235 }
1236
1237 /**
1238 * Handles chart data page.
1239 *
1240 * @since 1.0.0
1241 *
1242 * @access private
1243 */
1244 private function _handleDataPage() {
1245 $data = $this->_getChartArray();
1246 $render = new Visualizer_Render_Page_Data();
1247 $render->chart = $this->_chart;
1248 $render->type = $data['type'];
1249
1250 if ( $data && $data['settings'] ) {
1251 unset( $data['settings']['width'], $data['settings']['height'], $data['settings']['chartArea'] );
1252 }
1253 wp_enqueue_style( 'visualizer-frame' );
1254 wp_enqueue_script( 'visualizer-render' );
1255 wp_localize_script(
1256 'visualizer-render',
1257 'visualizer',
1258 array(
1259 'l10n' => array(
1260 'invalid_source' => esc_html__( 'You have entered an invalid URL. Please provide a valid URL.', 'visualizer' ),
1261 'loading' => esc_html__( 'Loading...', 'visualizer' ),
1262 ),
1263 'charts' => array(
1264 'canvas' => $data,
1265 ),
1266 )
1267 );
1268
1269 do_action( 'visualizer_enqueue_scripts_and_styles', $data, $this->_chart->ID );
1270
1271 if ( Visualizer_Module::is_pro() && Visualizer_Module::is_pro_older_than( '1.9.0' ) ) {
1272 global $Visualizer_Pro;
1273 $Visualizer_Pro->_enqueueScriptsAndStyles( $data, $this->_chart->ID );
1274 }
1275
1276 // Added by Ash/Upwork
1277 $this->_addAction( 'admin_head', 'renderFlattrScript' );
1278 wp_iframe( array( $render, 'render' ) );
1279 }
1280
1281 /**
1282 * Returns the data for the query.
1283 *
1284 * @access public
1285 */
1286 public function getQueryData() {
1287 check_ajax_referer( Visualizer_Plugin::ACTION_FETCH_DB_DATA . Visualizer_Plugin::VERSION, 'security' );
1288
1289 $params = wp_parse_args( $_POST['params'] );
1290 $chart_id = filter_var( $params['chart_id'], FILTER_VALIDATE_INT );
1291
1292 $source = new Visualizer_Source_Query( stripslashes( $params['query'] ), $chart_id, $params );
1293 $html = $source->fetch( true );
1294 $error = $source->get_error();
1295 if ( ! empty( $error ) ) {
1296 wp_send_json_error( array( 'msg' => $error ) );
1297 }
1298 wp_send_json_success( array( 'table' => $html ) );
1299 }
1300
1301 /**
1302 * Saves the query and the schedule.
1303 *
1304 * @access public
1305 */
1306 public function saveQuery() {
1307 check_ajax_referer( Visualizer_Plugin::ACTION_SAVE_DB_QUERY . Visualizer_Plugin::VERSION, 'security' );
1308
1309 $chart_id = filter_input(
1310 INPUT_GET,
1311 'chart',
1312 FILTER_VALIDATE_INT,
1313 array(
1314 'options' => array(
1315 'min_range' => 1,
1316 ),
1317 )
1318 );
1319
1320 $hours = filter_input(
1321 INPUT_POST,
1322 'refresh',
1323 FILTER_VALIDATE_INT,
1324 array(
1325 'options' => array(
1326 'min_range' => -1,
1327 'max_range' => apply_filters( 'visualizer_is_business', false ) ? PHP_INT_MAX : -1,
1328 ),
1329 )
1330 );
1331
1332 if ( ! is_int( $hours ) ) {
1333 $hours = -1;
1334 }
1335
1336 $render = new Visualizer_Render_Page_Update();
1337 if ( $chart_id ) {
1338 $params = wp_parse_args( $_POST['params'] );
1339 $source = new Visualizer_Source_Query( stripslashes( $params['query'] ), $chart_id, $params );
1340 $source->fetch( false );
1341 $error = $source->get_error();
1342 if ( empty( $error ) ) {
1343 update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY, stripslashes( $params['query'] ) );
1344 update_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
1345 update_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
1346 update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_SCHEDULE, $hours );
1347 update_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, 0 );
1348 if ( isset( $params['db_type'] ) && $params['db_type'] !== Visualizer_Plugin::WP_DB_NAME ) {
1349 $remote_db_params = $params;
1350 unset( $remote_db_params['query'] );
1351 unset( $remote_db_params['chart_id'] );
1352 update_post_meta( $chart_id, Visualizer_Plugin::CF_REMOTE_DB_PARAMS, $remote_db_params );
1353 }
1354
1355 $schedules = get_option( Visualizer_Plugin::CF_DB_SCHEDULE, array() );
1356 $schedules[ $chart_id ] = time() + $hours * HOUR_IN_SECONDS;
1357 update_option( Visualizer_Plugin::CF_DB_SCHEDULE, $schedules );
1358
1359 wp_update_post(
1360 array(
1361 'ID' => $chart_id,
1362 'post_content' => $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ),
1363 )
1364 );
1365 $render->data = json_encode( $source->getRawData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ) );
1366 $render->series = json_encode( $source->getSeries() );
1367 $render->id = $chart_id;
1368 } else {
1369 $render->message = $error;
1370 }
1371 }
1372 $render->render();
1373 if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
1374 defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1375 }
1376 }
1377
1378
1379 /**
1380 * Saves the filter query and the schedule.
1381 *
1382 * @access public
1383 */
1384 public function saveFilter() {
1385 check_ajax_referer( Visualizer_Plugin::ACTION_SAVE_FILTER_QUERY . Visualizer_Plugin::VERSION, 'security' );
1386
1387 $chart_id = filter_input(
1388 INPUT_GET,
1389 'chart',
1390 FILTER_VALIDATE_INT,
1391 array(
1392 'options' => array(
1393 'min_range' => 1,
1394 ),
1395 )
1396 );
1397
1398 $hours = filter_input(
1399 INPUT_POST,
1400 'refresh',
1401 FILTER_VALIDATE_INT,
1402 array(
1403 'options' => array(
1404 'min_range' => -1,
1405 'max_range' => apply_filters( 'visualizer_is_business', false ) ? PHP_INT_MAX : -1,
1406 ),
1407 )
1408 );
1409
1410 if ( 0 !== $hours && empty( $hours ) ) {
1411 $hours = -1;
1412 }
1413
1414 do_action( 'visualizer_save_filter', $chart_id, $hours );
1415
1416 if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
1417 defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1418 }
1419 }
1420 }
1421