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

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

1,387 lines 47.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 // +----------------------------------------------------------------------+
3 // | Copyright 2013 Madpixels (email : visualizer@madpixels.net) |
4 // +----------------------------------------------------------------------+
5 // | This program is free software; you can redistribute it and/or modify |
6 // | it under the terms of the GNU General Public License, version 2, as |
7 // | published by the Free Software Foundation. |
8 // | |
9 // | This program is distributed in the hope that it will be useful, |
10 // | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 // | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 // | GNU General Public License for more details. |
13 // | |
14 // | You should have received a copy of the GNU General Public License |
15 // | along with this program; if not, write to the Free Software |
16 // | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, |
17 // | MA 02110-1301 USA |
18 // +----------------------------------------------------------------------+
19 // | Author: Eugene Manuilov <eugene@manuilov.org> |
20 // +----------------------------------------------------------------------+
21 /**
22 * The module for all stuff related to getting, editing, creating and deleting charts.
23 *
24 * @category Visualizer
25 * @package Module
26 *
27 * @since 1.0.0
28 */
29 class Visualizer_Module_Chart extends Visualizer_Module {
30
31 const NAME = __CLASS__;
32
33 /**
34 * The chart object.
35 *
36 * @since 1.0.0
37 *
38 * @access private
39 * @var WP_Post
40 */
41 private $_chart;
42
43 /**
44 * Constructor.
45 *
46 * @since 1.0.0
47 *
48 * @access public
49 *
50 * @param Visualizer_Plugin $plugin The instance of the plugin.
51 */
52 public function __construct( Visualizer_Plugin $plugin ) {
53 parent::__construct( $plugin );
54 $this->_addAjaxAction( Visualizer_Plugin::ACTION_GET_CHARTS, 'getCharts' );
55 $this->_addAjaxAction( Visualizer_Plugin::ACTION_DELETE_CHART, 'deleteChart' );
56 $this->_addAjaxAction( Visualizer_Plugin::ACTION_CREATE_CHART, 'renderChartPages' );
57 $this->_addAjaxAction( Visualizer_Plugin::ACTION_EDIT_CHART, 'renderChartPages' );
58 $this->_addAjaxAction( Visualizer_Plugin::ACTION_UPLOAD_DATA, 'uploadData' );
59 $this->_addAjaxAction( Visualizer_Plugin::ACTION_CLONE_CHART, 'cloneChart' );
60 $this->_addAjaxAction( Visualizer_Plugin::ACTION_EXPORT_DATA, 'exportData' );
61
62 $this->_addAjaxAction( Visualizer_Plugin::ACTION_FETCH_DB_DATA, 'getQueryData' );
63 $this->_addAjaxAction( Visualizer_Plugin::ACTION_SAVE_DB_QUERY, 'saveQuery' );
64
65 $this->_addAjaxAction( Visualizer_Plugin::ACTION_JSON_GET_ROOTS, 'getJsonRoots' );
66 $this->_addAjaxAction( Visualizer_Plugin::ACTION_JSON_GET_DATA, 'getJsonData' );
67 $this->_addAjaxAction( Visualizer_Plugin::ACTION_JSON_SET_DATA, 'setJsonData' );
68 $this->_addAjaxAction( Visualizer_Plugin::ACTION_JSON_SET_SCHEDULE, 'setJsonSchedule' );
69
70 $this->_addAjaxAction( Visualizer_Plugin::ACTION_SAVE_FILTER_QUERY, 'saveFilter' );
71
72 $this->_addFilter( 'visualizer_get_sidebar', 'getSidebar', 10, 2 );
73
74 }
75
76 /**
77 * Generates the HTML of the sidebar for the chart.
78 *
79 * @since ?
80 *
81 * @access public
82 */
83 public function getSidebar( $sidebar, $chart_id ) {
84 $chart = get_post( $chart_id );
85 $data = $this->_getChartArray( $chart );
86 $sidebar = '';
87 $sidebar_class = $this->load_chart_class_name( $chart_id );
88 if ( class_exists( $sidebar_class, true ) ) {
89 $sidebar = new $sidebar_class( $data['settings'] );
90 $sidebar->__series = $data['series'];
91 $sidebar->__data = $data['data'];
92 } else {
93 $sidebar = apply_filters( 'visualizer_pro_chart_type_sidebar', '', $data );
94 if ( $sidebar !== '' ) {
95 $sidebar->__series = $data['series'];
96 $sidebar->__data = $data['data'];
97 }
98 }
99 return str_replace( "'", '"', $sidebar->__toString() );
100 }
101
102 /**
103 * Sets the schedule for how JSON-endpoint charts should be updated.
104 *
105 * @since ?
106 *
107 * @access public
108 */
109 public function setJsonSchedule() {
110 check_ajax_referer( Visualizer_Plugin::ACTION_JSON_SET_SCHEDULE . Visualizer_Plugin::VERSION, 'security' );
111
112 $chart_id = filter_input(
113 INPUT_POST,
114 'chart',
115 FILTER_VALIDATE_INT,
116 array(
117 'options' => array(
118 'min_range' => 1,
119 ),
120 )
121 );
122
123 if ( ! $chart_id ) {
124 wp_send_json_error();
125 }
126
127 $time = filter_input(
128 INPUT_POST,
129 'time',
130 FILTER_VALIDATE_INT,
131 array(
132 'options' => array(
133 'min_range' => -1,
134 ),
135 )
136 );
137
138 delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_SCHEDULE );
139
140 if ( -1 < $time ) {
141 add_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_SCHEDULE, $time );
142 }
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 'excel':
902 // data coming in from the excel editor.
903 $source = apply_filters( 'visualizer_pro_handle_chart_data', $data, '' );
904 break;
905 }
906 return $source;
907 }
908
909 /**
910 * Parses the data uploaded as an HTML table.
911 *
912 * @since 3.2.0
913 *
914 * @access private
915 */
916 private function handleTabularData() {
917 $csv = array();
918 // the datatable mentions the headers twice, so lets remove the duplicates.
919 $headers = array_unique( array_filter( $_POST['header'] ) );
920 $types = $_POST['type'];
921
922 // capture all the indexes that correspond to excluded columns.
923 $exclude = array();
924 $index = 0;
925 foreach ( $types as $type ) {
926 if ( empty( $type ) ) {
927 $exclude[] = $index;
928 }
929 $index++;
930 }
931
932 // when N headers are being renamed, the number of headers increases by N
933 // because of the way datatable duplicates header information
934 // so unset the headers that have been renamed.
935 if ( count( $headers ) !== count( $types ) ) {
936 $to = count( $headers );
937 for ( $i = count( $types ); $i < $to; $i++ ) {
938 unset( $headers[ $i + 1 ] );
939 }
940 }
941
942 $columns = array();
943 for ( $i = 0; $i < count( $headers ); $i++ ) {
944 if ( ! isset( $_POST[ 'data' . $i ] ) ) {
945 continue;
946 }
947 $columns[ $i ] = $_POST[ 'data' . $i ];
948 }
949
950 $csv[] = $headers;
951 $csv[] = $types;
952 for ( $j = 0; $j < count( $columns[0] ); $j++ ) {
953 $row = array();
954 for ( $i = 0; $i < count( $headers ); $i++ ) {
955 $row[] = sanitize_text_field( $columns[ $i ][ $j ] );
956 }
957 $csv[] = $row;
958 }
959
960 $tmpfile = tempnam( get_temp_dir(), Visualizer_Plugin::NAME );
961 $handle = fopen( $tmpfile, 'w' );
962
963 if ( $csv ) {
964 $index = 0;
965 foreach ( $csv as $row ) {
966 // remove all the cells corresponding to the excluded headers.
967 foreach ( $exclude as $j ) {
968 unset( $row[ $j ] );
969 }
970 fputcsv( $handle, $row );
971 }
972 }
973 $source = new Visualizer_Source_Csv( $tmpfile );
974 fclose( $handle );
975 return $source;
976 }
977
978 /**
979 * Parses uploaded CSV file and saves new data for the chart.
980 *
981 * @since 1.0.0
982 *
983 * @access public
984 */
985 public function uploadData() {
986 // if this is being called internally from pro and VISUALIZER_DO_NOT_DIE is set.
987 // otherwise, assume this is a normal web request.
988 $can_die = ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE );
989
990 // validate nonce
991 if ( ! isset( $_GET['nonce'] ) || ! wp_verify_nonce( $_GET['nonce'] ) ) {
992 if ( ! $can_die ) {
993 return;
994 }
995 status_header( 403 );
996 exit;
997 }
998
999 // check chart, if chart exists
1000 // do not use filter_input as it does not work for phpunit test cases, use filter_var instead
1001 $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
1002 if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ) {
1003 if ( ! $can_die ) {
1004 return;
1005 }
1006 status_header( 400 );
1007 exit;
1008 }
1009
1010 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__ );
1011
1012 if ( ! isset( $_POST['vz-import-time'] ) ) {
1013 apply_filters( 'visualizer_pro_remove_schedule', $chart_id );
1014 }
1015
1016 if ( ! isset( $_POST['chart_data_src'] ) || Visualizer_Plugin::CF_SOURCE_FILTER !== $_POST['chart_data_src'] ) {
1017 // delete the filters in case this chart is being uploaded from other data sources
1018 delete_post_meta( $chart_id, Visualizer_Plugin::CF_FILTER_CONFIG );
1019 delete_post_meta( $chart_id, '__transient-' . Visualizer_Plugin::CF_FILTER_CONFIG );
1020 delete_post_meta( $chart_id, '__transient-' . Visualizer_Plugin::CF_DB_QUERY );
1021
1022 // delete "import from db" specific parameters.
1023 delete_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY );
1024 delete_post_meta( $chart_id, Visualizer_Plugin::CF_DB_SCHEDULE );
1025 delete_post_meta( $chart_id, Visualizer_Plugin::CF_REMOTE_DB_PARAMS );
1026 }
1027
1028 // delete json related data.
1029 delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_URL );
1030 delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_ROOT );
1031 delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_PAGING );
1032 delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_HEADERS );
1033
1034 // delete last error
1035 delete_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR );
1036
1037 // delete editor related data.
1038 delete_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR );
1039
1040 $source = null;
1041 $render = new Visualizer_Render_Page_Update();
1042 if ( isset( $_POST['remote_data'] ) && filter_var( $_POST['remote_data'], FILTER_VALIDATE_URL ) ) {
1043 $source = new Visualizer_Source_Csv_Remote( $_POST['remote_data'] );
1044 if ( isset( $_POST['vz-import-time'] ) ) {
1045 apply_filters( 'visualizer_pro_chart_schedule', $chart_id, $_POST['remote_data'], $_POST['vz-import-time'] );
1046 }
1047 // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
1048 } elseif ( isset( $_FILES['local_data'] ) && $_FILES['local_data']['error'] == 0 ) {
1049 $source = new Visualizer_Source_Csv( $_FILES['local_data']['tmp_name'] );
1050 } elseif ( isset( $_POST['chart_data'] ) && strlen( $_POST['chart_data'] ) > 0 ) {
1051 $source = $this->handleCSVasString( $_POST['chart_data'], $_POST['editor-type'] );
1052 update_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR, $_POST['editor-type'] );
1053 } elseif ( isset( $_POST['table_data'] ) && 'yes' === $_POST['table_data'] ) {
1054 $source = $this->handleTabularData();
1055 update_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR, $_POST['editor-type'] );
1056 } else {
1057 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__ );
1058 $render->message = esc_html__( 'CSV file with chart data was not uploaded. Please try again.', 'visualizer' );
1059 update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, esc_html__( 'CSV file with chart data was not uploaded. Please try again.', 'visualizer' ) );
1060 }
1061
1062 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__ );
1063
1064 if ( $source ) {
1065 if ( $source->fetch() ) {
1066 $content = $source->getData();
1067 $populate = true;
1068 if ( is_string( $content ) && is_array( unserialize( $content ) ) ) {
1069 $json = unserialize( $content );
1070 // if source exists, so should data. if source exists but data is blank, do not populate the chart.
1071 // if we populate the data even if it is empty, the chart will show "Table has no columns".
1072 if ( array_key_exists( 'source', $json ) && ! empty( $json['source'] ) && ( ! array_key_exists( 'data', $json ) || empty( $json['data'] ) ) ) {
1073 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__ );
1074 update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, sprintf( 'Not populating chart data as source exists (%s) but data is empty!', $json['source'] ) );
1075 $populate = false;
1076 }
1077 }
1078 if ( $populate ) {
1079 $chart->post_content = $content;
1080 }
1081 wp_update_post( $chart->to_array() );
1082 update_post_meta( $chart->ID, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
1083 update_post_meta( $chart->ID, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
1084 update_post_meta( $chart->ID, Visualizer_Plugin::CF_DEFAULT_DATA, 0 );
1085
1086 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Updated post for chart %d', $chart_id ), 'debug', __FILE__, __LINE__ );
1087
1088 Visualizer_Module_Utility::set_defaults( $chart, null );
1089
1090 $settings = get_post_meta( $chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
1091
1092 $render->id = $chart->ID;
1093 $render->data = json_encode( $source->getRawData() );
1094 $render->series = json_encode( $source->getSeries() );
1095 $render->settings = json_encode( $settings );
1096 } else {
1097 $error = $source->get_error();
1098 if ( empty( $error ) ) {
1099 $error = esc_html__( 'CSV file is broken or invalid. Please try again.', 'visualizer' );
1100 }
1101 $render->message = $error;
1102 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( '%s for chart %d.', $error, $chart_id ), 'error', __FILE__, __LINE__ );
1103 update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, $error );
1104 }
1105 } else {
1106 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Unknown internal error for chart %d.', $chart_id ), 'error', __FILE__, __LINE__ );
1107 }
1108 $render->render();
1109 if ( ! $can_die ) {
1110 return;
1111 }
1112 defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1113 }
1114
1115 /**
1116 * Clones the chart.
1117 *
1118 * @since 1.0.0
1119 *
1120 * @access public
1121 */
1122 public function cloneChart() {
1123 $chart_id = $success = false;
1124 $nonce = isset( $_GET['nonce'] ) && wp_verify_nonce( $_GET['nonce'], Visualizer_Plugin::ACTION_CLONE_CHART );
1125 $capable = current_user_can( 'edit_posts' );
1126 if ( $nonce && $capable ) {
1127 $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
1128 if ( $chart_id ) {
1129 $chart = get_post( $chart_id );
1130 $success = $chart && $chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
1131 }
1132 }
1133 $redirect = remove_query_arg( 'vaction', wp_get_referer() );
1134 if ( $success ) {
1135 $new_chart_id = wp_insert_post(
1136 array(
1137 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
1138 'post_title' => 'Visualization',
1139 'post_author' => get_current_user_id(),
1140 'post_status' => $chart->post_status,
1141 'post_content' => $chart->post_content,
1142 )
1143 );
1144 if ( is_wp_error( $new_chart_id ) ) {
1145 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__ );
1146 } else {
1147 $post_meta = get_post_meta( $chart_id );
1148 foreach ( $post_meta as $key => $value ) {
1149 if ( strpos( $key, 'visualizer-' ) !== false ) {
1150 add_post_meta( $new_chart_id, $key, maybe_unserialize( $value[0] ) );
1151 }
1152 }
1153 $redirect = add_query_arg(
1154 array(
1155 'page' => 'visualizer',
1156 'type' => filter_input( INPUT_GET, 'type' ),
1157 'vaction' => false,
1158 ),
1159 admin_url( 'admin.php' )
1160 );
1161 }
1162 }
1163
1164 if ( defined( 'WP_TESTS_DOMAIN' ) ) {
1165 wp_die();
1166 }
1167
1168 wp_redirect( $redirect );
1169 exit;
1170 }
1171
1172 /**
1173 * Exports the chart data
1174 *
1175 * @since 1.0.0
1176 *
1177 * @access public
1178 */
1179 public function exportData() {
1180 check_ajax_referer( Visualizer_Plugin::ACTION_EXPORT_DATA . Visualizer_Plugin::VERSION, 'security' );
1181 $capable = current_user_can( 'edit_posts' );
1182 if ( $capable ) {
1183 $chart_id = isset( $_GET['chart'] ) ? filter_var(
1184 $_GET['chart'],
1185 FILTER_VALIDATE_INT,
1186 array(
1187 'options' => array(
1188 'min_range' => 1,
1189 ),
1190 )
1191 ) : '';
1192 if ( $chart_id ) {
1193 $data = $this->_getDataAs( $chart_id, 'csv' );
1194 if ( $data ) {
1195 echo wp_send_json_success( $data );
1196 }
1197 }
1198 }
1199
1200 defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1201 }
1202
1203 /**
1204 * Handles chart data page.
1205 *
1206 * @since 1.0.0
1207 *
1208 * @access private
1209 */
1210 private function _handleDataPage() {
1211 $data = $this->_getChartArray();
1212 $render = new Visualizer_Render_Page_Data();
1213 $render->chart = $this->_chart;
1214 $render->type = $data['type'];
1215
1216 if ( $data && $data['settings'] ) {
1217 unset( $data['settings']['width'], $data['settings']['height'], $data['settings']['chartArea'] );
1218 }
1219 wp_enqueue_style( 'visualizer-frame' );
1220 wp_enqueue_script( 'visualizer-render' );
1221 wp_localize_script(
1222 'visualizer-render',
1223 'visualizer',
1224 array(
1225 'l10n' => array(
1226 'invalid_source' => esc_html__( 'You have entered an invalid URL. Please provide a valid URL.', 'visualizer' ),
1227 'loading' => esc_html__( 'Loading...', 'visualizer' ),
1228 ),
1229 'charts' => array(
1230 'canvas' => $data,
1231 ),
1232 )
1233 );
1234
1235 do_action( 'visualizer_enqueue_scripts_and_styles', $data, $this->_chart->ID );
1236
1237 if ( Visualizer_Module::is_pro() && Visualizer_Module::is_pro_older_than( '1.9.0' ) ) {
1238 global $Visualizer_Pro;
1239 $Visualizer_Pro->_enqueueScriptsAndStyles( $data, $this->_chart->ID );
1240 }
1241
1242 // Added by Ash/Upwork
1243 $this->_addAction( 'admin_head', 'renderFlattrScript' );
1244 wp_iframe( array( $render, 'render' ) );
1245 }
1246
1247 /**
1248 * Returns the data for the query.
1249 *
1250 * @access public
1251 */
1252 public function getQueryData() {
1253 check_ajax_referer( Visualizer_Plugin::ACTION_FETCH_DB_DATA . Visualizer_Plugin::VERSION, 'security' );
1254
1255 $params = wp_parse_args( $_POST['params'] );
1256 $chart_id = filter_var( $params['chart_id'], FILTER_VALIDATE_INT );
1257
1258 $source = new Visualizer_Source_Query( stripslashes( $params['query'] ), $chart_id, $params );
1259 $html = $source->fetch( true );
1260 $error = $source->get_error();
1261 if ( ! empty( $error ) ) {
1262 wp_send_json_error( array( 'msg' => $error ) );
1263 }
1264 wp_send_json_success( array( 'table' => $html ) );
1265 }
1266
1267 /**
1268 * Saves the query and the schedule.
1269 *
1270 * @access public
1271 */
1272 public function saveQuery() {
1273 check_ajax_referer( Visualizer_Plugin::ACTION_SAVE_DB_QUERY . Visualizer_Plugin::VERSION, 'security' );
1274
1275 $chart_id = filter_input(
1276 INPUT_GET,
1277 'chart',
1278 FILTER_VALIDATE_INT,
1279 array(
1280 'options' => array(
1281 'min_range' => 1,
1282 ),
1283 )
1284 );
1285
1286 $hours = filter_input(
1287 INPUT_POST,
1288 'refresh',
1289 FILTER_VALIDATE_INT,
1290 array(
1291 'options' => array(
1292 'min_range' => -1,
1293 'max_range' => apply_filters( 'visualizer_is_business', false ) ? PHP_INT_MAX : -1,
1294 ),
1295 )
1296 );
1297
1298 if ( ! is_int( $hours ) ) {
1299 $hours = -1;
1300 }
1301
1302 $render = new Visualizer_Render_Page_Update();
1303 if ( $chart_id ) {
1304 $params = wp_parse_args( $_POST['params'] );
1305 $source = new Visualizer_Source_Query( stripslashes( $params['query'] ), $chart_id, $params );
1306 $source->fetch( false );
1307 $error = $source->get_error();
1308 if ( empty( $error ) ) {
1309 update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY, stripslashes( $params['query'] ) );
1310 update_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
1311 update_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
1312 update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_SCHEDULE, $hours );
1313 update_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, 0 );
1314 if ( isset( $params['db_type'] ) && $params['db_type'] !== Visualizer_Plugin::WP_DB_NAME ) {
1315 $remote_db_params = $params;
1316 unset( $remote_db_params['query'] );
1317 unset( $remote_db_params['chart_id'] );
1318 update_post_meta( $chart_id, Visualizer_Plugin::CF_REMOTE_DB_PARAMS, $remote_db_params );
1319 }
1320
1321 $schedules = get_option( Visualizer_Plugin::CF_DB_SCHEDULE, array() );
1322 $schedules[ $chart_id ] = time() + $hours * HOUR_IN_SECONDS;
1323 update_option( Visualizer_Plugin::CF_DB_SCHEDULE, $schedules );
1324
1325 wp_update_post(
1326 array(
1327 'ID' => $chart_id,
1328 'post_content' => $source->getData(),
1329 )
1330 );
1331 $render->data = json_encode( $source->getRawData() );
1332 $render->series = json_encode( $source->getSeries() );
1333 $render->id = $chart_id;
1334 } else {
1335 $render->message = $error;
1336 }
1337 }
1338 $render->render();
1339 if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
1340 defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1341 }
1342 }
1343
1344
1345 /**
1346 * Saves the filter query and the schedule.
1347 *
1348 * @access public
1349 */
1350 public function saveFilter() {
1351 check_ajax_referer( Visualizer_Plugin::ACTION_SAVE_FILTER_QUERY . Visualizer_Plugin::VERSION, 'security' );
1352
1353 $chart_id = filter_input(
1354 INPUT_GET,
1355 'chart',
1356 FILTER_VALIDATE_INT,
1357 array(
1358 'options' => array(
1359 'min_range' => 1,
1360 ),
1361 )
1362 );
1363
1364 $hours = filter_input(
1365 INPUT_POST,
1366 'refresh',
1367 FILTER_VALIDATE_INT,
1368 array(
1369 'options' => array(
1370 'min_range' => -1,
1371 'max_range' => apply_filters( 'visualizer_is_business', false ) ? PHP_INT_MAX : -1,
1372 ),
1373 )
1374 );
1375
1376 if ( ! $hours ) {
1377 $hours = -1;
1378 }
1379
1380 do_action( 'visualizer_save_filter', $chart_id, $hours );
1381
1382 if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
1383 defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1384 }
1385 }
1386 }
1387