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

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