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

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