PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.5.0
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.5.0
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.0, at classes/Visualizer/Module/Chart.php

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