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

1,415 lines 49.3 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 // check chart, if chart not exists, will create new one and redirects to the same page with proper chart id
490 $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
491 if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ) {
492 $this->deleteOldCharts();
493 $default_type = isset( $_GET['type'] ) && ! empty( $_GET['type'] ) ? $_GET['type'] : 'line';
494 $source = new Visualizer_Source_Csv( VISUALIZER_ABSPATH . DIRECTORY_SEPARATOR . 'samples' . DIRECTORY_SEPARATOR . $default_type . '.csv' );
495 $source->fetch();
496 $chart_id = wp_insert_post(
497 array(
498 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
499 'post_title' => 'Visualization',
500 'post_author' => get_current_user_id(),
501 'post_status' => 'auto-draft',
502 'post_content' => $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ),
503 )
504 );
505 if ( $chart_id && ! is_wp_error( $chart_id ) ) {
506 add_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_TYPE, $default_type );
507 add_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, 1 );
508 add_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
509 add_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
510 add_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_LIBRARY, '' );
511 add_post_meta(
512 $chart_id,
513 Visualizer_Plugin::CF_SETTINGS,
514 array(
515 'focusTarget' => 'datum',
516 )
517 );
518 do_action( 'visualizer_pro_new_chart_defaults', $chart_id );
519 }
520 wp_redirect( add_query_arg( 'chart', (int) $chart_id ) );
521 defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
522 }
523
524 $lib = $this->load_chart_type( $chart_id );
525
526 // the alpha color picker (RGBA) is not supported by google.
527 $color_picker_dep = 'wp-color-picker';
528 if ( in_array( $lib, array( 'chartjs', 'datatables' ), true ) && ! wp_script_is( 'wp-color-picker-alpha', 'registered' ) ) {
529 wp_register_script( 'wp-color-picker-alpha', VISUALIZER_ABSURL . 'js/lib/wp-color-picker-alpha.min.js', array( 'wp-color-picker' ), Visualizer_Plugin::VERSION );
530 $color_picker_dep = 'wp-color-picker-alpha';
531 }
532
533 // enqueue and register scripts and styles
534 wp_register_script( 'visualizer-chosen', VISUALIZER_ABSURL . 'js/lib/chosen.jquery.min.js', array( 'jquery' ), Visualizer_Plugin::VERSION );
535 wp_register_style( 'visualizer-chosen', VISUALIZER_ABSURL . 'css/lib/chosen.min.css', array(), Visualizer_Plugin::VERSION );
536
537 wp_register_style( 'visualizer-frame', VISUALIZER_ABSURL . 'css/frame.css', array( 'visualizer-chosen' ), Visualizer_Plugin::VERSION );
538 wp_register_script( 'visualizer-frame', VISUALIZER_ABSURL . 'js/frame.js', array( 'visualizer-chosen', 'jquery-ui-accordion', 'jquery-ui-tabs' ), Visualizer_Plugin::VERSION, true );
539 wp_register_script( 'visualizer-customization', $this->get_user_customization_js(), array(), null, true );
540 wp_register_script(
541 'visualizer-render',
542 VISUALIZER_ABSURL . 'js/render-facade.js',
543 apply_filters( 'visualizer_assets_render', array( 'visualizer-frame', 'visualizer-customization' ), false ),
544 Visualizer_Plugin::VERSION,
545 true
546 );
547 wp_register_script(
548 'visualizer-preview',
549 VISUALIZER_ABSURL . 'js/preview.js',
550 array(
551 $color_picker_dep,
552 'visualizer-render',
553 ),
554 Visualizer_Plugin::VERSION,
555 true
556 );
557 wp_register_script( 'visualizer-editor-simple', VISUALIZER_ABSURL . 'js/simple-editor.js', array( 'jquery' ), Visualizer_Plugin::VERSION, true );
558
559 do_action( 'visualizer_add_scripts' );
560
561 if ( Visualizer_Module::is_pro() && Visualizer_Module::is_pro_older_than( '1.9.0' ) ) {
562 global $Visualizer_Pro;
563 $Visualizer_Pro->_addScriptsAndStyles();
564 }
565
566 // dispatch pages
567 $this->_chart = get_post( $chart_id );
568 $tab = isset( $_GET['tab'] ) && ! empty( $_GET['tab'] ) ? $_GET['tab'] : 'visualizer';
569
570 // skip chart type pages only for existing charts.
571 if ( VISUALIZER_SKIP_CHART_TYPE_PAGE && 'auto-draft' !== $this->_chart->post_status && ( ! empty( $_GET['tab'] ) && 'visualizer' === $_GET['tab'] ) ) {
572 $tab = 'settings';
573 }
574
575 if ( isset( $_POST['cancel'] ) && 1 === intval( $_POST['cancel'] ) ) {
576 // if the cancel button is clicked.
577 $this->undoRevisions( $chart_id, true );
578 } elseif ( isset( $_POST['save'] ) && 1 === intval( $_POST['save'] ) ) {
579 $this->handlePermissions();
580 // if the save button is clicked.
581 $this->undoRevisions( $chart_id, false );
582 } else {
583 // if the edit button is clicked.
584 $this->_chart = $this->handleExistingRevisions( $chart_id, $this->_chart );
585 }
586
587 switch ( $tab ) {
588 case 'settings':
589 $this->_handleDataAndSettingsPage();
590 break;
591 case 'type': // fall through.
592 case 'visualizer': // fall through.
593 $this->_handleTypesPage();
594 break;
595 default:
596 // this should never happen.
597 break;
598 }
599 defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
600 }
601
602 /**
603 * Load code editor assets.
604 */
605 private function loadCodeEditorAssets( $chart_id ) {
606 global $wp_version;
607
608 $wp_scripts = wp_scripts();
609
610 // data tables assets.
611 wp_register_script( 'visualizer-datatables', VISUALIZER_ABSURL . 'js/lib/datatables.min.js', array( 'jquery-ui-core' ), Visualizer_Plugin::VERSION );
612 wp_register_style( 'visualizer-datatables', VISUALIZER_ABSURL . 'css/lib/datatables.min.css', array(), Visualizer_Plugin::VERSION );
613 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 );
614 wp_enqueue_script( 'visualizer-datatables' );
615 wp_enqueue_style( 'visualizer-jquery-ui' );
616
617 if ( ! Visualizer_Module::is_pro() ) {
618 return;
619 }
620
621 $table_col_mapping = Visualizer_Source_Query_Params::get_all_db_tables_column_mapping( $chart_id );
622
623 if ( version_compare( $wp_version, '4.9.0', '<' ) ) {
624 // code mirror assets.
625 wp_register_script( 'visualizer-codemirror-core', '//codemirror.net/lib/codemirror.js', array( 'jquery' ), Visualizer_Plugin::VERSION );
626 wp_register_script( 'visualizer-codemirror-placeholder', '//codemirror.net/addon/display/placeholder.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
627 wp_register_script( 'visualizer-codemirror-matchbrackets', '//codemirror.net/addon/edit/matchbrackets.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
628 wp_register_script( 'visualizer-codemirror-closebrackets', '//codemirror.net/addon/edit/closebrackets.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
629 wp_register_script( 'visualizer-codemirror-sql', '//codemirror.net/mode/sql/sql.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
630 wp_register_script( 'visualizer-codemirror-sql-hint', '//codemirror.net/addon/hint/sql-hint.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
631 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 );
632 wp_register_style( 'visualizer-codemirror-core', '//codemirror.net/lib/codemirror.css', array(), Visualizer_Plugin::VERSION );
633 wp_register_style( 'visualizer-codemirror-hint', '//codemirror.net/addon/hint/show-hint.css', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
634
635 wp_enqueue_script( 'visualizer-codemirror-hint' );
636 wp_enqueue_style( 'visualizer-codemirror-hint' );
637 } else {
638 wp_enqueue_code_editor(
639 array(
640 'type' => 'sql',
641 'codemirror' => array(
642 'autofocus' => true,
643 'lineWrapping' => true,
644 'dragDrop' => false,
645 'matchBrackets' => true,
646 'autoCloseBrackets' => true,
647 'extraKeys' => array( 'Ctrl-Space' => 'autocomplete' ),
648 'hintOptions' => array( 'tables' => $table_col_mapping ),
649 ),
650 )
651 );
652 }
653
654 return $table_col_mapping;
655 }
656
657 /**
658 * Handle permissions from the new conslidated settings sidebar.
659 */
660 private function handlePermissions() {
661 if ( ! Visualizer_Module::is_pro() ) {
662 return;
663 }
664
665 // we will not support old free and new pro.
666 // handling new free and old pro.
667 if ( has_action( 'visualizer_pro_handle_tab' ) ) {
668 do_action( 'visualizer_pro_handle_tab', 'permissions', $this->_chart );
669 } else {
670 do_action( 'visualizer_handle_permissions', $this->_chart );
671 }
672 }
673
674 /**
675 * Handle data and settings page
676 */
677 private function _handleDataAndSettingsPage() {
678 if ( isset( $_POST['map_api_key'] ) ) {
679 update_option( 'visualizer-map-api-key', $_POST['map_api_key'] );
680 }
681
682 if ( $_SERVER['REQUEST_METHOD'] === 'POST' && isset( $_GET['nonce'] ) && wp_verify_nonce( $_GET['nonce'] ) ) {
683 if ( $this->_chart->post_status === 'auto-draft' ) {
684 $this->_chart->post_status = 'publish';
685
686 // 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.
687 // we do not want any difference in data so disable revisions temporarily.
688 $this->disableRevisionsTemporarily();
689
690 wp_update_post( $this->_chart->to_array() );
691 }
692 // save meta data only when it is NOT being canceled.
693 if ( ! ( isset( $_POST['cancel'] ) && 1 === intval( $_POST['cancel'] ) ) ) {
694 update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, $_POST );
695
696 // we will keep a parameter called 'internal_title' that will be set to the given title or, if empty, the chart ID
697 // this will help in searching with the chart id.
698 $settings = get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
699 $title = null;
700 if ( isset( $settings['title'] ) && ! empty( $settings['title'] ) ) {
701 $title = $settings['title'];
702 if ( is_array( $title ) ) {
703 $title = $settings['title']['text'];
704 }
705 }
706 if ( empty( $title ) ) {
707 $title = $this->_chart->ID;
708 }
709 $settings['internal_title'] = $title;
710 update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, $settings );
711 }
712 $render = new Visualizer_Render_Page_Send();
713 $render->text = sprintf( '[visualizer id="%d"]', $this->_chart->ID );
714 wp_iframe( array( $render, 'render' ) );
715 return;
716 }
717 $data = $this->_getChartArray();
718 $sidebar = '';
719 $sidebar_class = $this->load_chart_class_name( $this->_chart->ID );
720 if ( class_exists( $sidebar_class, true ) ) {
721 $sidebar = new $sidebar_class( $data['settings'] );
722 $sidebar->__series = $data['series'];
723 $sidebar->__data = $data['data'];
724 } else {
725 $sidebar = apply_filters( 'visualizer_pro_chart_type_sidebar', '', $data );
726 if ( $sidebar !== '' ) {
727 $sidebar->__series = $data['series'];
728 $sidebar->__data = $data['data'];
729 }
730 }
731 unset( $data['settings']['width'], $data['settings']['height'], $data['settings']['chartArea'] );
732 wp_enqueue_style( 'wp-color-picker' );
733 wp_enqueue_style( 'visualizer-frame' );
734 wp_enqueue_script( 'visualizer-preview' );
735 wp_enqueue_script( 'visualizer-chosen' );
736 wp_enqueue_script( 'visualizer-render' );
737
738 if ( Visualizer_Module::can_show_feature( 'simple-editor' ) ) {
739 wp_enqueue_script( 'visualizer-editor-simple' );
740 wp_localize_script(
741 'visualizer-editor-simple',
742 'visualizer1',
743 array(
744 'ajax' => array(
745 'url' => admin_url( 'admin-ajax.php' ),
746 'nonces' => array(
747 ),
748 'actions' => array(
749 ),
750 ),
751 )
752 );
753 }
754
755 $table_col_mapping = $this->loadCodeEditorAssets( $this->_chart->ID );
756
757 wp_localize_script(
758 'visualizer-render',
759 'visualizer',
760 array(
761 'l10n' => array(
762 'invalid_source' => esc_html__( 'You have entered an invalid URL. Please provide a valid URL.', 'visualizer' ),
763 'loading' => esc_html__( 'Loading...', 'visualizer' ),
764 'json_error' => esc_html__( 'An error occured in fetching data.', 'visualizer' ),
765 'select_columns' => esc_html__( 'Please select a few columns to include in the chart.', 'visualizer' ),
766 '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' ),
767 ),
768 'charts' => array(
769 'canvas' => $data,
770 'id' => $this->_chart->ID,
771 ),
772 'language' => $this->get_language(),
773 'map_api_key' => get_option( 'visualizer-map-api-key' ),
774 'ajax' => array(
775 'url' => admin_url( 'admin-ajax.php' ),
776 'nonces' => array(
777 'permissions' => wp_create_nonce( Visualizer_Plugin::ACTION_FETCH_PERMISSIONS_DATA ),
778 'db_get_data' => wp_create_nonce( Visualizer_Plugin::ACTION_FETCH_DB_DATA . Visualizer_Plugin::VERSION ),
779 'json_get_roots' => wp_create_nonce( Visualizer_Plugin::ACTION_JSON_GET_ROOTS . Visualizer_Plugin::VERSION ),
780 'json_get_data' => wp_create_nonce( Visualizer_Plugin::ACTION_JSON_GET_DATA . Visualizer_Plugin::VERSION ),
781 'json_set_schedule' => wp_create_nonce( Visualizer_Plugin::ACTION_JSON_SET_SCHEDULE . Visualizer_Plugin::VERSION ),
782 ),
783 'actions' => array(
784 'permissions' => Visualizer_Plugin::ACTION_FETCH_PERMISSIONS_DATA,
785 'db_get_data' => Visualizer_Plugin::ACTION_FETCH_DB_DATA,
786 'json_get_roots' => Visualizer_Plugin::ACTION_JSON_GET_ROOTS,
787 'json_get_data' => Visualizer_Plugin::ACTION_JSON_GET_DATA,
788 'json_set_schedule' => Visualizer_Plugin::ACTION_JSON_SET_SCHEDULE,
789 ),
790 ),
791 'db_query' => array(
792 'tables' => $table_col_mapping,
793 ),
794 'is_pro' => Visualizer_Module::is_pro(),
795 'page_type' => 'chart',
796 'json_tag_separator' => Visualizer_Source_Json::TAG_SEPARATOR,
797 'json_tag_separator_view' => Visualizer_Source_Json::TAG_SEPARATOR_VIEW,
798 'is_front' => false,
799 )
800 );
801
802 $render = new Visualizer_Render_Page_Data();
803 $render->chart = $this->_chart;
804 $render->type = $data['type'];
805 $render->custom_css = $data['css'];
806 $render->sidebar = $sidebar;
807 if ( filter_input( INPUT_GET, 'library', FILTER_VALIDATE_BOOLEAN ) ) {
808 $render->button = filter_input( INPUT_GET, 'action' ) === Visualizer_Plugin::ACTION_EDIT_CHART
809 ? esc_html__( 'Save Chart', 'visualizer' )
810 : esc_html__( 'Create Chart', 'visualizer' );
811 if ( filter_input( INPUT_GET, 'action' ) === Visualizer_Plugin::ACTION_EDIT_CHART ) {
812 $render->cancel_button = esc_html__( 'Cancel', 'visualizer' );
813 }
814 } else {
815 $render->button = esc_attr__( 'Insert Chart', 'visualizer' );
816 }
817
818 do_action( 'visualizer_enqueue_scripts_and_styles', $data, $this->_chart->ID );
819
820 if ( Visualizer_Module::is_pro() && Visualizer_Module::is_pro_older_than( '1.9.0' ) ) {
821 global $Visualizer_Pro;
822 $Visualizer_Pro->_enqueueScriptsAndStyles( $data, $this->_chart->ID );
823 }
824
825 $this->_addAction( 'admin_head', 'renderFlattrScript' );
826 wp_iframe( array( $render, 'render' ) );
827 }
828
829 /**
830 * Handles chart type selection page.
831 *
832 * @since 1.0.0
833 *
834 * @access private
835 */
836 private function _handleTypesPage() {
837 // process post request
838 if ( $_SERVER['REQUEST_METHOD'] === 'POST' && wp_verify_nonce( filter_input( INPUT_POST, 'nonce' ) ) ) {
839 $type = filter_input( INPUT_POST, 'type' );
840 $library = filter_input( INPUT_POST, 'chart-library' );
841 if ( in_array( $type, Visualizer_Plugin::getChartTypes(), true ) ) {
842 if ( empty( $library ) ) {
843 // library cannot be empty.
844 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, 'Chart library empty while creating the chart! Aborting...', 'error', __FILE__, __LINE__ );
845 return;
846 }
847
848 // save new chart type
849 update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_CHART_TYPE, $type );
850 update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_CHART_LIBRARY, $library );
851 // if the chart has default data, update it with appropriate default data for new type
852 if ( filter_var( get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_DEFAULT_DATA, true ), FILTER_VALIDATE_BOOLEAN ) ) {
853 $source = new Visualizer_Source_Csv( VISUALIZER_ABSPATH . DIRECTORY_SEPARATOR . 'samples' . DIRECTORY_SEPARATOR . $type . '.csv' );
854 $source->fetch();
855 $this->_chart->post_content = $source->getData( get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) );
856 wp_update_post( $this->_chart->to_array() );
857 update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
858 }
859
860 Visualizer_Module_Utility::set_defaults( $this->_chart );
861
862 // redirect to next tab
863 // changed by Ash/Upwork
864 wp_redirect( add_query_arg( 'tab', 'settings' ) );
865
866 return;
867 }
868 }
869 $render = new Visualizer_Render_Page_Types();
870 $render->type = get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_CHART_TYPE, true );
871 $render->types = Visualizer_Module_Admin::_getChartTypesLocalized( false, false, false, 'types' );
872 $render->chart = $this->_chart;
873 wp_enqueue_style( 'visualizer-frame' );
874 wp_enqueue_script( 'visualizer-frame' );
875 wp_iframe( array( $render, 'render' ) );
876 }
877
878 /**
879 * Renders flattr script in the iframe <head>
880 *
881 * @since 1.4.2
882 * @action admin_head
883 *
884 * @access public
885 */
886 public function renderFlattrScript() {
887 echo '';
888 }
889
890 /**
891 * Processes the CSV that is sent in the request as a string.
892 *
893 * @since 3.2.0
894 */
895 private function handleCSVasString( $data, $editor_type ) {
896 $source = null;
897
898 switch ( $editor_type ) {
899 case 'text':
900 // data coming in from the text editor.
901 $tmpfile = tempnam( get_temp_dir(), Visualizer_Plugin::NAME );
902 $handle = fopen( $tmpfile, 'w' );
903 $values = preg_split( '/[\n\r]+/', stripslashes( trim( $data ) ) );
904 if ( $values ) {
905 foreach ( $values as $row ) {
906 if ( empty( $row ) ) {
907 continue;
908 }
909 // don't use fpucsv here because we need to just dump the data
910 // minus the empty rows
911 // because fputcsv needs to tokenize
912 // we can standardize the CSV enclosure here and replace all ' with "
913 // we can assume that if there are an even number of ' they should be changed to "
914 // but that will screw up words like fo'c'sle
915 // so here let's just assume ' will NOT be used for enclosures
916 fwrite( $handle, $row );
917 fwrite( $handle, PHP_EOL );
918 }
919 }
920 $source = new Visualizer_Source_Csv( $tmpfile );
921 fclose( $handle );
922 break;
923 case 'table':
924 // Import from Chart
925 // fall-through.
926 case 'excel':
927 // data coming in from the excel editor.
928 $source = apply_filters( 'visualizer_pro_handle_chart_data', $data, '' );
929 break;
930 }
931 return $source;
932 }
933
934 /**
935 * Parses the data uploaded as an HTML table.
936 *
937 * @since 3.2.0
938 *
939 * @access private
940 */
941 private function handleTabularData() {
942 $csv = array();
943 // the datatable mentions the headers twice, so lets remove the duplicates.
944 $headers = array_unique( array_filter( $_POST['header'] ) );
945 $types = $_POST['type'];
946
947 // capture all the indexes that correspond to excluded columns.
948 $exclude = array();
949 $index = 0;
950 foreach ( $types as $type ) {
951 if ( empty( $type ) ) {
952 $exclude[] = $index;
953 }
954 $index++;
955 }
956
957 // when N headers are being renamed, the number of headers increases by N
958 // because of the way datatable duplicates header information
959 // so unset the headers that have been renamed.
960 if ( count( $headers ) !== count( $types ) ) {
961 $to = count( $headers );
962 for ( $i = count( $types ); $i < $to; $i++ ) {
963 unset( $headers[ $i + 1 ] );
964 }
965 }
966
967 $columns = array();
968 for ( $i = 0; $i < count( $headers ); $i++ ) {
969 if ( ! isset( $_POST[ 'data' . $i ] ) ) {
970 continue;
971 }
972 $columns[ $i ] = $_POST[ 'data' . $i ];
973 }
974
975 $csv[] = $headers;
976 $csv[] = $types;
977 for ( $j = 0; $j < count( $columns[0] ); $j++ ) {
978 $row = array();
979 for ( $i = 0; $i < count( $headers ); $i++ ) {
980 $row[] = sanitize_text_field( $columns[ $i ][ $j ] );
981 }
982 $csv[] = $row;
983 }
984
985 $tmpfile = tempnam( get_temp_dir(), Visualizer_Plugin::NAME );
986 $handle = fopen( $tmpfile, 'w' );
987
988 if ( $csv ) {
989 $index = 0;
990 foreach ( $csv as $row ) {
991 // remove all the cells corresponding to the excluded headers.
992 foreach ( $exclude as $j ) {
993 unset( $row[ $j ] );
994 }
995 fputcsv( $handle, $row );
996 }
997 }
998 $source = new Visualizer_Source_Csv( $tmpfile );
999 fclose( $handle );
1000 return $source;
1001 }
1002
1003 /**
1004 * Parses uploaded CSV file and saves new data for the chart.
1005 *
1006 * @since 1.0.0
1007 *
1008 * @access public
1009 */
1010 public function uploadData() {
1011 // if this is being called internally from pro and VISUALIZER_DO_NOT_DIE is set.
1012 // otherwise, assume this is a normal web request.
1013 $can_die = ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE );
1014
1015 // validate nonce
1016 if ( ! isset( $_GET['nonce'] ) || ! wp_verify_nonce( $_GET['nonce'] ) ) {
1017 if ( ! $can_die ) {
1018 return;
1019 }
1020 status_header( 403 );
1021 exit;
1022 }
1023
1024 // check chart, if chart exists
1025 // do not use filter_input as it does not work for phpunit test cases, use filter_var instead
1026 $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
1027 if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ) {
1028 if ( ! $can_die ) {
1029 return;
1030 }
1031 status_header( 400 );
1032 exit;
1033 }
1034
1035 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__ );
1036
1037 if ( ! isset( $_POST['vz-import-time'] ) ) {
1038 apply_filters( 'visualizer_pro_remove_schedule', $chart_id );
1039 }
1040
1041 if ( ! isset( $_POST['chart_data_src'] ) || Visualizer_Plugin::CF_SOURCE_FILTER !== $_POST['chart_data_src'] ) {
1042 // delete the filters in case this chart is being uploaded from other data sources
1043 delete_post_meta( $chart_id, Visualizer_Plugin::CF_FILTER_CONFIG );
1044 delete_post_meta( $chart_id, '__transient-' . Visualizer_Plugin::CF_FILTER_CONFIG );
1045 delete_post_meta( $chart_id, '__transient-' . Visualizer_Plugin::CF_DB_QUERY );
1046
1047 // delete "import from db" specific parameters.
1048 delete_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY );
1049 delete_post_meta( $chart_id, Visualizer_Plugin::CF_DB_SCHEDULE );
1050 delete_post_meta( $chart_id, Visualizer_Plugin::CF_REMOTE_DB_PARAMS );
1051 }
1052
1053 // delete json related data.
1054 delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_URL );
1055 delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_ROOT );
1056 delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_PAGING );
1057 delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_HEADERS );
1058
1059 // delete last error
1060 delete_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR );
1061
1062 // delete editor related data.
1063 delete_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR );
1064
1065 // delete this so that a JSON import can be later edited manually without a problem.
1066 delete_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE );
1067
1068 $source = null;
1069 $render = new Visualizer_Render_Page_Update();
1070 if ( isset( $_POST['remote_data'] ) && filter_var( $_POST['remote_data'], FILTER_VALIDATE_URL ) ) {
1071 $source = new Visualizer_Source_Csv_Remote( $_POST['remote_data'] );
1072 if ( isset( $_POST['vz-import-time'] ) ) {
1073 apply_filters( 'visualizer_pro_chart_schedule', $chart_id, $_POST['remote_data'], $_POST['vz-import-time'] );
1074 }
1075 // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
1076 } elseif ( isset( $_FILES['local_data'] ) && $_FILES['local_data']['error'] == 0 ) {
1077 $source = new Visualizer_Source_Csv( $_FILES['local_data']['tmp_name'] );
1078 } elseif ( isset( $_POST['chart_data'] ) && strlen( $_POST['chart_data'] ) > 0 ) {
1079 $source = $this->handleCSVasString( $_POST['chart_data'], $_POST['editor-type'] );
1080 update_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR, $_POST['editor-type'] );
1081 } elseif ( isset( $_POST['table_data'] ) && 'yes' === $_POST['table_data'] ) {
1082 $source = $this->handleTabularData();
1083 update_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR, $_POST['editor-type'] );
1084 } else {
1085 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__ );
1086 $render->message = esc_html__( 'CSV file with chart data was not uploaded. Please try again.', 'visualizer' );
1087 update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, esc_html__( 'CSV file with chart data was not uploaded. Please try again.', 'visualizer' ) );
1088 }
1089
1090 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__ );
1091
1092 if ( $source ) {
1093 if ( $source->fetch() ) {
1094 $content = $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) );
1095 $populate = true;
1096 if ( is_string( $content ) && is_array( unserialize( $content ) ) ) {
1097 $json = unserialize( $content );
1098 // if source exists, so should data. if source exists but data is blank, do not populate the chart.
1099 // if we populate the data even if it is empty, the chart will show "Table has no columns".
1100 if ( array_key_exists( 'source', $json ) && ! empty( $json['source'] ) && ( ! array_key_exists( 'data', $json ) || empty( $json['data'] ) ) ) {
1101 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__ );
1102 update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, sprintf( 'Not populating chart data as source exists (%s) but data is empty!', $json['source'] ) );
1103 $populate = false;
1104 }
1105 }
1106 if ( $populate ) {
1107 $chart->post_content = $content;
1108 }
1109 wp_update_post( $chart->to_array() );
1110 update_post_meta( $chart->ID, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
1111 update_post_meta( $chart->ID, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
1112 update_post_meta( $chart->ID, Visualizer_Plugin::CF_DEFAULT_DATA, 0 );
1113
1114 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Updated post for chart %d', $chart_id ), 'debug', __FILE__, __LINE__ );
1115
1116 Visualizer_Module_Utility::set_defaults( $chart, null );
1117
1118 $settings = get_post_meta( $chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
1119
1120 $render->id = $chart->ID;
1121 $render->data = json_encode( $source->getRawData( get_post_meta( $chart->ID, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ) );
1122 $render->series = json_encode( $source->getSeries() );
1123 $render->settings = json_encode( $settings );
1124 } else {
1125 $error = $source->get_error();
1126 if ( empty( $error ) ) {
1127 $error = esc_html__( 'CSV file is broken or invalid. Please try again.', 'visualizer' );
1128 }
1129 $render->message = $error;
1130 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( '%s for chart %d.', $error, $chart_id ), 'error', __FILE__, __LINE__ );
1131 update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, $error );
1132 }
1133 } else {
1134 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Unknown internal error for chart %d.', $chart_id ), 'error', __FILE__, __LINE__ );
1135 }
1136 $render->render();
1137 if ( ! $can_die ) {
1138 return;
1139 }
1140 defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1141 }
1142
1143 /**
1144 * Clones the chart.
1145 *
1146 * @since 1.0.0
1147 *
1148 * @access public
1149 */
1150 public function cloneChart() {
1151 $chart_id = $success = false;
1152 $nonce = isset( $_GET['nonce'] ) && wp_verify_nonce( $_GET['nonce'], Visualizer_Plugin::ACTION_CLONE_CHART );
1153 $capable = current_user_can( 'edit_posts' );
1154 if ( $nonce && $capable ) {
1155 $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
1156 if ( $chart_id ) {
1157 $chart = get_post( $chart_id );
1158 $success = $chart && $chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
1159 }
1160 }
1161 $redirect = remove_query_arg( 'vaction', wp_get_referer() );
1162 if ( $success ) {
1163 $new_chart_id = wp_insert_post(
1164 array(
1165 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
1166 'post_title' => 'Visualization',
1167 'post_author' => get_current_user_id(),
1168 'post_status' => $chart->post_status,
1169 'post_content' => $chart->post_content,
1170 )
1171 );
1172 if ( is_wp_error( $new_chart_id ) ) {
1173 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__ );
1174 } else {
1175 $post_meta = get_post_meta( $chart_id );
1176 foreach ( $post_meta as $key => $value ) {
1177 if ( strpos( $key, 'visualizer-' ) !== false ) {
1178 add_post_meta( $new_chart_id, $key, maybe_unserialize( $value[0] ) );
1179 }
1180 }
1181 $redirect = add_query_arg(
1182 array(
1183 'page' => 'visualizer',
1184 'type' => filter_input( INPUT_GET, 'type' ),
1185 'vaction' => false,
1186 ),
1187 admin_url( 'admin.php' )
1188 );
1189 }
1190 }
1191
1192 if ( defined( 'WP_TESTS_DOMAIN' ) ) {
1193 wp_die();
1194 }
1195
1196 wp_redirect( $redirect );
1197 exit;
1198 }
1199
1200 /**
1201 * Exports the chart data
1202 *
1203 * @since 1.0.0
1204 *
1205 * @access public
1206 */
1207 public function exportData() {
1208 check_ajax_referer( Visualizer_Plugin::ACTION_EXPORT_DATA . Visualizer_Plugin::VERSION, 'security' );
1209 $capable = current_user_can( 'edit_posts' );
1210 if ( $capable ) {
1211 $chart_id = isset( $_GET['chart'] ) ? filter_var(
1212 $_GET['chart'],
1213 FILTER_VALIDATE_INT,
1214 array(
1215 'options' => array(
1216 'min_range' => 1,
1217 ),
1218 )
1219 ) : '';
1220 if ( $chart_id ) {
1221 $data = $this->_getDataAs( $chart_id, 'csv' );
1222 if ( $data ) {
1223 echo wp_send_json_success( $data );
1224 }
1225 }
1226 }
1227
1228 defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1229 }
1230
1231 /**
1232 * Handles chart data page.
1233 *
1234 * @since 1.0.0
1235 *
1236 * @access private
1237 */
1238 private function _handleDataPage() {
1239 $data = $this->_getChartArray();
1240 $render = new Visualizer_Render_Page_Data();
1241 $render->chart = $this->_chart;
1242 $render->type = $data['type'];
1243
1244 if ( $data && $data['settings'] ) {
1245 unset( $data['settings']['width'], $data['settings']['height'], $data['settings']['chartArea'] );
1246 }
1247 wp_enqueue_style( 'visualizer-frame' );
1248 wp_enqueue_script( 'visualizer-render' );
1249 wp_localize_script(
1250 'visualizer-render',
1251 'visualizer',
1252 array(
1253 'l10n' => array(
1254 'invalid_source' => esc_html__( 'You have entered an invalid URL. Please provide a valid URL.', 'visualizer' ),
1255 'loading' => esc_html__( 'Loading...', 'visualizer' ),
1256 ),
1257 'charts' => array(
1258 'canvas' => $data,
1259 ),
1260 )
1261 );
1262
1263 do_action( 'visualizer_enqueue_scripts_and_styles', $data, $this->_chart->ID );
1264
1265 if ( Visualizer_Module::is_pro() && Visualizer_Module::is_pro_older_than( '1.9.0' ) ) {
1266 global $Visualizer_Pro;
1267 $Visualizer_Pro->_enqueueScriptsAndStyles( $data, $this->_chart->ID );
1268 }
1269
1270 // Added by Ash/Upwork
1271 $this->_addAction( 'admin_head', 'renderFlattrScript' );
1272 wp_iframe( array( $render, 'render' ) );
1273 }
1274
1275 /**
1276 * Returns the data for the query.
1277 *
1278 * @access public
1279 */
1280 public function getQueryData() {
1281 check_ajax_referer( Visualizer_Plugin::ACTION_FETCH_DB_DATA . Visualizer_Plugin::VERSION, 'security' );
1282
1283 $params = wp_parse_args( $_POST['params'] );
1284 $chart_id = filter_var( $params['chart_id'], FILTER_VALIDATE_INT );
1285
1286 $source = new Visualizer_Source_Query( stripslashes( $params['query'] ), $chart_id, $params );
1287 $html = $source->fetch( true );
1288 $error = $source->get_error();
1289 if ( ! empty( $error ) ) {
1290 wp_send_json_error( array( 'msg' => $error ) );
1291 }
1292 wp_send_json_success( array( 'table' => $html ) );
1293 }
1294
1295 /**
1296 * Saves the query and the schedule.
1297 *
1298 * @access public
1299 */
1300 public function saveQuery() {
1301 check_ajax_referer( Visualizer_Plugin::ACTION_SAVE_DB_QUERY . Visualizer_Plugin::VERSION, 'security' );
1302
1303 $chart_id = filter_input(
1304 INPUT_GET,
1305 'chart',
1306 FILTER_VALIDATE_INT,
1307 array(
1308 'options' => array(
1309 'min_range' => 1,
1310 ),
1311 )
1312 );
1313
1314 $hours = filter_input(
1315 INPUT_POST,
1316 'refresh',
1317 FILTER_VALIDATE_INT,
1318 array(
1319 'options' => array(
1320 'min_range' => -1,
1321 'max_range' => apply_filters( 'visualizer_is_business', false ) ? PHP_INT_MAX : -1,
1322 ),
1323 )
1324 );
1325
1326 if ( ! is_int( $hours ) ) {
1327 $hours = -1;
1328 }
1329
1330 $render = new Visualizer_Render_Page_Update();
1331 if ( $chart_id ) {
1332 $params = wp_parse_args( $_POST['params'] );
1333 $source = new Visualizer_Source_Query( stripslashes( $params['query'] ), $chart_id, $params );
1334 $source->fetch( false );
1335 $error = $source->get_error();
1336 if ( empty( $error ) ) {
1337 update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY, stripslashes( $params['query'] ) );
1338 update_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
1339 update_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
1340 update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_SCHEDULE, $hours );
1341 update_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, 0 );
1342 if ( isset( $params['db_type'] ) && $params['db_type'] !== Visualizer_Plugin::WP_DB_NAME ) {
1343 $remote_db_params = $params;
1344 unset( $remote_db_params['query'] );
1345 unset( $remote_db_params['chart_id'] );
1346 update_post_meta( $chart_id, Visualizer_Plugin::CF_REMOTE_DB_PARAMS, $remote_db_params );
1347 }
1348
1349 $schedules = get_option( Visualizer_Plugin::CF_DB_SCHEDULE, array() );
1350 $schedules[ $chart_id ] = time() + $hours * HOUR_IN_SECONDS;
1351 update_option( Visualizer_Plugin::CF_DB_SCHEDULE, $schedules );
1352
1353 wp_update_post(
1354 array(
1355 'ID' => $chart_id,
1356 'post_content' => $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ),
1357 )
1358 );
1359 $render->data = json_encode( $source->getRawData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ) );
1360 $render->series = json_encode( $source->getSeries() );
1361 $render->id = $chart_id;
1362 } else {
1363 $render->message = $error;
1364 }
1365 }
1366 $render->render();
1367 if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
1368 defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1369 }
1370 }
1371
1372
1373 /**
1374 * Saves the filter query and the schedule.
1375 *
1376 * @access public
1377 */
1378 public function saveFilter() {
1379 check_ajax_referer( Visualizer_Plugin::ACTION_SAVE_FILTER_QUERY . Visualizer_Plugin::VERSION, 'security' );
1380
1381 $chart_id = filter_input(
1382 INPUT_GET,
1383 'chart',
1384 FILTER_VALIDATE_INT,
1385 array(
1386 'options' => array(
1387 'min_range' => 1,
1388 ),
1389 )
1390 );
1391
1392 $hours = filter_input(
1393 INPUT_POST,
1394 'refresh',
1395 FILTER_VALIDATE_INT,
1396 array(
1397 'options' => array(
1398 'min_range' => -1,
1399 'max_range' => apply_filters( 'visualizer_is_business', false ) ? PHP_INT_MAX : -1,
1400 ),
1401 )
1402 );
1403
1404 if ( 0 !== $hours && empty( $hours ) ) {
1405 $hours = -1;
1406 }
1407
1408 do_action( 'visualizer_save_filter', $chart_id, $hours );
1409
1410 if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
1411 defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1412 }
1413 }
1414 }
1415