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

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