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

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

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