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

1,512 lines 52.9 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( 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( 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 = add_query_arg(
1230 array(
1231 'page' => 'visualizer',
1232 'type' => filter_input( INPUT_GET, 'type' ),
1233 'vaction' => false,
1234 ),
1235 admin_url( 'admin.php' )
1236 );
1237 }
1238 }
1239
1240 if ( defined( 'WP_TESTS_DOMAIN' ) ) {
1241 wp_die();
1242 }
1243
1244 wp_redirect( $redirect );
1245 exit;
1246 }
1247
1248 /**
1249 * Exports the chart data
1250 *
1251 * @since 1.0.0
1252 *
1253 * @access public
1254 */
1255 public function exportData() {
1256 check_ajax_referer( Visualizer_Plugin::ACTION_EXPORT_DATA . Visualizer_Plugin::VERSION, 'security' );
1257 $capable = current_user_can( 'edit_posts' );
1258 if ( $capable ) {
1259 $chart_id = isset( $_GET['chart'] ) ? filter_var(
1260 $_GET['chart'],
1261 FILTER_VALIDATE_INT,
1262 array(
1263 'options' => array(
1264 'min_range' => 1,
1265 ),
1266 )
1267 ) : '';
1268 if ( $chart_id ) {
1269 $data = $this->_getDataAs( $chart_id, 'csv' );
1270 if ( $data ) {
1271 echo wp_send_json_success( $data );
1272 }
1273 }
1274 }
1275
1276 defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1277 }
1278
1279 /**
1280 * Handles chart data page.
1281 *
1282 * @since 1.0.0
1283 *
1284 * @access private
1285 */
1286 private function _handleDataPage() {
1287 $data = $this->_getChartArray();
1288 $render = new Visualizer_Render_Page_Data();
1289 $render->chart = $this->_chart;
1290 $render->type = $data['type'];
1291
1292 if ( $data && $data['settings'] ) {
1293 unset( $data['settings']['width'], $data['settings']['height'], $data['settings']['chartArea'] );
1294 }
1295 wp_enqueue_style( 'visualizer-frame' );
1296 wp_enqueue_script( 'visualizer-render' );
1297 wp_localize_script(
1298 'visualizer-render',
1299 'visualizer',
1300 array(
1301 'l10n' => array(
1302 'invalid_source' => esc_html__( 'You have entered an invalid URL. Please provide a valid URL.', 'visualizer' ),
1303 'loading' => esc_html__( 'Loading...', 'visualizer' ),
1304 ),
1305 'charts' => array(
1306 'canvas' => $data,
1307 ),
1308 )
1309 );
1310
1311 do_action( 'visualizer_enqueue_scripts_and_styles', $data, $this->_chart->ID );
1312
1313 if ( Visualizer_Module::is_pro() && Visualizer_Module::is_pro_older_than( '1.9.0' ) ) {
1314 global $Visualizer_Pro;
1315 $Visualizer_Pro->_enqueueScriptsAndStyles( $data, $this->_chart->ID );
1316 }
1317
1318 // Added by Ash/Upwork
1319 $this->_addAction( 'admin_head', 'renderFlattrScript' );
1320 wp_iframe( array( $render, 'render' ) );
1321 }
1322
1323 /**
1324 * Returns the data for the query.
1325 *
1326 * @access public
1327 */
1328 public function getQueryData() {
1329 check_ajax_referer( Visualizer_Plugin::ACTION_FETCH_DB_DATA . Visualizer_Plugin::VERSION, 'security' );
1330
1331 $params = wp_parse_args( $_POST['params'] );
1332 $chart_id = filter_var( $params['chart_id'], FILTER_VALIDATE_INT );
1333
1334 $source = new Visualizer_Source_Query( stripslashes( $params['query'] ), $chart_id, $params );
1335 $html = $source->fetch( true );
1336 $error = $source->get_error();
1337 if ( ! empty( $error ) ) {
1338 wp_send_json_error( array( 'msg' => $error ) );
1339 }
1340 wp_send_json_success( array( 'table' => $html ) );
1341 }
1342
1343 /**
1344 * Saves the query and the schedule.
1345 *
1346 * @access public
1347 */
1348 public function saveQuery() {
1349 check_ajax_referer( Visualizer_Plugin::ACTION_SAVE_DB_QUERY . Visualizer_Plugin::VERSION, 'security' );
1350
1351 $chart_id = filter_input(
1352 INPUT_GET,
1353 'chart',
1354 FILTER_VALIDATE_INT,
1355 array(
1356 'options' => array(
1357 'min_range' => 1,
1358 ),
1359 )
1360 );
1361
1362 $hours = filter_input(
1363 INPUT_POST,
1364 'refresh',
1365 FILTER_VALIDATE_FLOAT,
1366 array(
1367 'options' => array(
1368 'min_range' => -1,
1369 'max_range' => apply_filters( 'visualizer_is_business', false ) ? PHP_INT_MAX : -1,
1370 ),
1371 )
1372 );
1373
1374 if ( ! is_numeric( $hours ) ) {
1375 $hours = -1;
1376 }
1377
1378 $render = new Visualizer_Render_Page_Update();
1379 if ( $chart_id ) {
1380 $params = wp_parse_args( $_POST['params'] );
1381 $source = new Visualizer_Source_Query( stripslashes( $params['query'] ), $chart_id, $params );
1382 $source->fetch( false );
1383 $error = $source->get_error();
1384 if ( empty( $error ) ) {
1385 update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY, stripslashes( $params['query'] ) );
1386 update_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
1387 update_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
1388 update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_SCHEDULE, $hours );
1389 update_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, 0 );
1390 if ( isset( $params['db_type'] ) && $params['db_type'] !== Visualizer_Plugin::WP_DB_NAME ) {
1391 $remote_db_params = $params;
1392 unset( $remote_db_params['query'] );
1393 unset( $remote_db_params['chart_id'] );
1394 update_post_meta( $chart_id, Visualizer_Plugin::CF_REMOTE_DB_PARAMS, $remote_db_params );
1395 }
1396
1397 $schedules = get_option( Visualizer_Plugin::CF_DB_SCHEDULE, array() );
1398 $schedules[ $chart_id ] = time() + $hours * HOUR_IN_SECONDS;
1399 update_option( Visualizer_Plugin::CF_DB_SCHEDULE, $schedules );
1400
1401 wp_update_post(
1402 array(
1403 'ID' => $chart_id,
1404 'post_content' => $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ),
1405 )
1406 );
1407 $render->data = json_encode( $source->getRawData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ) );
1408 $render->series = json_encode( $source->getSeries() );
1409 $render->id = $chart_id;
1410 } else {
1411 $render->message = $error;
1412 }
1413 }
1414 $render->render();
1415 if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
1416 defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1417 }
1418 }
1419
1420
1421 /**
1422 * Saves the filter query and the schedule.
1423 *
1424 * @access public
1425 */
1426 public function saveFilter() {
1427 check_ajax_referer( Visualizer_Plugin::ACTION_SAVE_FILTER_QUERY . Visualizer_Plugin::VERSION, 'security' );
1428
1429 $chart_id = filter_input(
1430 INPUT_GET,
1431 'chart',
1432 FILTER_VALIDATE_INT,
1433 array(
1434 'options' => array(
1435 'min_range' => 1,
1436 ),
1437 )
1438 );
1439
1440 $hours = filter_input(
1441 INPUT_POST,
1442 'refresh',
1443 FILTER_VALIDATE_INT,
1444 array(
1445 'options' => array(
1446 'min_range' => -1,
1447 'max_range' => apply_filters( 'visualizer_is_business', false ) ? PHP_INT_MAX : -1,
1448 ),
1449 )
1450 );
1451
1452 if ( 0 !== $hours && empty( $hours ) ) {
1453 $hours = -1;
1454 }
1455
1456 do_action( 'visualizer_save_filter', $chart_id, $hours );
1457
1458 if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
1459 defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1460 }
1461 }
1462
1463 /**
1464 * Save chart image.
1465 *
1466 * @param string $base64_img Chart image.
1467 * @param int $chart_id Chart ID.
1468 * @param bool $save_attachment Save attachment.
1469 * @return attachment ID
1470 */
1471 public function save_chart_image( $base64_img, $chart_id, $save_attachment = true ) {
1472 // Delete old chart image.
1473 $old_attachment_id = get_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_IMAGE, true );
1474 if ( $old_attachment_id ) {
1475 wp_delete_attachment( $old_attachment_id, true );
1476 }
1477
1478 if ( ! $save_attachment ) {
1479 return 0;
1480 }
1481
1482 // Upload dir.
1483 $upload_dir = wp_upload_dir();
1484 $upload_path = str_replace( '/', DIRECTORY_SEPARATOR, $upload_dir['path'] ) . DIRECTORY_SEPARATOR;
1485
1486 $img = str_replace( 'data:image/png;base64,', '', $base64_img );
1487 $img = str_replace( ' ', '+', $img );
1488 $decoded = base64_decode( $img );
1489 $filename = 'visualization-' . $chart_id . '.png';
1490 $file_type = 'image/png';
1491 $hashed_filename = $filename;
1492
1493 // Save the image in the uploads directory.
1494 require_once ABSPATH . '/wp-admin/includes/file.php';
1495 \WP_Filesystem();
1496 global $wp_filesystem;
1497 $upload_file = $wp_filesystem->put_contents( $upload_path . $hashed_filename, $decoded );
1498
1499 // Insert new chart image.
1500 $attachment = array(
1501 'post_mime_type' => $file_type,
1502 'post_title' => preg_replace( '/\.[^.]+$/', '', basename( $hashed_filename ) ),
1503 'post_content' => '',
1504 'post_status' => 'inherit',
1505 'guid' => $upload_dir['url'] . '/' . basename( $hashed_filename ),
1506 );
1507
1508 $attach_id = wp_insert_attachment( $attachment, $upload_dir['path'] . '/' . $hashed_filename );
1509 return $attach_id;
1510 }
1511 }
1512