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

Block.php in Visualizer – Tables & Charts Manager with Built-in AI Generator 3.7.0, at classes/Visualizer/Gutenberg/Block.php

840 lines 26.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 // +----------------------------------------------------------------------+
3 // | Copyright 2018 ThemeIsle (email : friends@themeisle.com) |
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: Hardeep Asrani <hardeep@themeisle.com> |
20 // +----------------------------------------------------------------------+
21 /**
22 * All the Gutenberg block related code.
23 *
24 * @category Visualizer
25 * @package Gutenberg
26 *
27 * @since 3.1.0
28 */
29 class Visualizer_Gutenberg_Block {
30
31 /**
32 * A reference to an instance of this class.
33 *
34 * @var Visualizer_Gutenberg_Block The one Visualizer_Gutenberg_Block instance.
35 */
36 private static $instance;
37
38 /**
39 * Visualizer plugin version.
40 *
41 * @var string $version The current version of the plugin.
42 */
43 protected $version;
44
45 /**
46 * Returns an instance of this class.
47 */
48 public static function get_instance() {
49 if ( null === self::$instance ) {
50 self::$instance = new Visualizer_Gutenberg_Block();
51 }
52 return self::$instance;
53 }
54
55 /**
56 * Initializes the plugin by setting filters and administration functions.
57 */
58 private function __construct() {
59 $this->version = Visualizer_Plugin::VERSION;
60 add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_gutenberg_scripts' ) );
61 add_action( 'init', array( $this, 'register_block_type' ) );
62 add_action( 'rest_api_init', array( $this, 'register_rest_endpoints' ) );
63 add_filter( 'rest_visualizer_query', array( $this, 'add_rest_query_vars' ), 9, 2 );
64 }
65
66 /**
67 * Enqueue front end and editor JavaScript and CSS
68 */
69 public function enqueue_gutenberg_scripts() {
70 global $wp_version;
71
72 $blockPath = VISUALIZER_ABSURL . 'classes/Visualizer/Gutenberg/build/block.js';
73 $handsontableJS = VISUALIZER_ABSURL . 'classes/Visualizer/Gutenberg/build/handsontable.js';
74 $stylePath = VISUALIZER_ABSURL . 'classes/Visualizer/Gutenberg/build/block.css';
75 $handsontableCSS = VISUALIZER_ABSURL . 'classes/Visualizer/Gutenberg/build/handsontable.css';
76
77 if ( VISUALIZER_TEST_JS_CUSTOMIZATION ) {
78 $version = filemtime( VISUALIZER_ABSPATH . '/classes/Visualizer/Gutenberg/build/block.js' );
79 } else {
80 $version = $this->version;
81 }
82
83 if ( ! wp_script_is( 'visualizer-datatables', 'registered' ) ) {
84 wp_register_script( 'visualizer-datatables', VISUALIZER_ABSURL . 'js/lib/datatables.min.js', array( 'jquery-ui-core' ), Visualizer_Plugin::VERSION );
85 }
86
87 if ( ! wp_style_is( 'visualizer-datatables', 'registered' ) ) {
88 wp_register_style( 'visualizer-datatables', VISUALIZER_ABSURL . 'css/lib/datatables.min.css', array(), Visualizer_Plugin::VERSION );
89 }
90
91 // Enqueue the bundled block JS file
92 wp_enqueue_script( 'handsontable', $handsontableJS );
93 wp_enqueue_script( 'visualizer-gutenberg-block', $blockPath, array( 'wp-api', 'handsontable', 'visualizer-datatables', 'moment' ), $version, true );
94
95 $type = 'community';
96
97 if ( Visualizer_Module::is_pro() ) {
98 $type = 'pro';
99 if ( apply_filters( 'visualizer_is_business', false ) ) {
100 $type = 'business';
101 }
102 }
103
104 $table_col_mapping = Visualizer_Source_Query_Params::get_all_db_tables_column_mapping( null, false );
105
106 $translation_array = array(
107 'isPro' => $type,
108 'proTeaser' => Visualizer_Plugin::PRO_TEASER_URL,
109 'absurl' => VISUALIZER_ABSURL,
110 'charts' => Visualizer_Module_Admin::_getChartTypesLocalized(),
111 'adminPage' => menu_page_url( 'visualizer', false ),
112 'sqlTable' => $table_col_mapping,
113 'chartsPerPage' => defined( 'TI_CYPRESS_TESTING' ) ? 20 : 6,
114 );
115 wp_localize_script( 'visualizer-gutenberg-block', 'visualizerLocalize', $translation_array );
116
117 // Enqueue frontend and editor block styles
118 wp_enqueue_style( 'handsontable', $handsontableCSS );
119 wp_enqueue_style( 'visualizer-gutenberg-block', $stylePath, array( 'visualizer-datatables' ), $version );
120
121 if ( version_compare( $wp_version, '4.9.0', '>' ) ) {
122
123 wp_enqueue_code_editor(
124 array(
125 'type' => 'sql',
126 'codemirror' => array(
127 'autofocus' => true,
128 'lineWrapping' => true,
129 'dragDrop' => false,
130 'matchBrackets' => true,
131 'autoCloseBrackets' => true,
132 'extraKeys' => array( 'Ctrl-Space' => 'autocomplete' ),
133 'hintOptions' => array( 'tables' => $table_col_mapping ),
134 ),
135 )
136 );
137 }
138 }
139 /**
140 * Hook server side rendering into render callback
141 */
142 public function register_block_type() {
143 register_block_type(
144 'visualizer/chart', array(
145 'render_callback' => array( $this, 'gutenberg_block_callback' ),
146 'attributes' => array(
147 'id' => array(
148 'type' => 'number',
149 ),
150 'lazy' => array(
151 'type' => 'string',
152 ),
153 ),
154 )
155 );
156 }
157
158 /**
159 * Gutenberg Block Callback Function
160 */
161 public function gutenberg_block_callback( $atts ) {
162 // no id, no fun.
163 if ( ! isset( $atts['id'] ) ) {
164 return '';
165 }
166
167 $atts = shortcode_atts(
168 array(
169 'id' => false,
170 'lazy' => apply_filters( 'visualizer_lazy_by_default', false, $atts['id'] ),
171 // we are deliberating excluding the class attribute from here
172 // as this will be handled by the custom class in Gutenberg
173 ),
174 $atts
175 );
176
177 // no id, no fun.
178 if ( ! $atts['id'] ) {
179 return '';
180 }
181
182 // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
183 if ( $atts['lazy'] == -1 || $atts['lazy'] == false ) {
184 $atts['lazy'] = 'no';
185 }
186
187 // we don't want the chart in the editor lazy-loading.
188 if ( is_admin() ) {
189 unset( $atts['lazy'] );
190 }
191
192 $shortcode = '[visualizer';
193 foreach ( $atts as $name => $value ) {
194 $shortcode .= sprintf( ' %s="%s"', $name, $value );
195 }
196 $shortcode .= ']';
197
198 return $shortcode;
199 }
200
201 /**
202 * Hook server side rendering into render callback
203 */
204 public function register_rest_endpoints() {
205 register_rest_field(
206 'visualizer',
207 'chart_data',
208 array(
209 'get_callback' => array( $this, 'get_visualizer_data' ),
210 )
211 );
212
213 register_rest_route(
214 'visualizer/v' . VISUALIZER_REST_VERSION,
215 '/get-query-data',
216 array(
217 'methods' => 'GET',
218 'callback' => array( $this, 'get_query_data' ),
219 'permission_callback' => function () {
220 return current_user_can( 'edit_posts' );
221 },
222 )
223 );
224
225 register_rest_route(
226 'visualizer/v' . VISUALIZER_REST_VERSION,
227 '/get-json-root',
228 array(
229 'methods' => 'GET',
230 'callback' => array( $this, 'get_json_root_data' ),
231 'args' => array(
232 'url' => array(
233 'sanitize_callback' => 'esc_url_raw',
234 ),
235 ),
236 'permission_callback' => function () {
237 return current_user_can( 'edit_posts' );
238 },
239 )
240 );
241
242 register_rest_route(
243 'visualizer/v' . VISUALIZER_REST_VERSION,
244 '/get-json-data',
245 array(
246 'methods' => 'GET',
247 'callback' => array( $this, 'get_json_data' ),
248 'args' => array(
249 'url' => array(
250 'sanitize_callback' => 'esc_url_raw',
251 ),
252 'chart' => array(
253 'sanitize_callback' => 'absint',
254 ),
255 ),
256 'permission_callback' => function () {
257 return current_user_can( 'edit_posts' );
258 },
259 )
260 );
261
262 register_rest_route(
263 'visualizer/v' . VISUALIZER_REST_VERSION,
264 '/set-json-data',
265 array(
266 'methods' => 'GET',
267 'callback' => array( $this, 'set_json_data' ),
268 'args' => array(
269 'url' => array(
270 'sanitize_callback' => 'esc_url_raw',
271 ),
272 ),
273 'permission_callback' => function () {
274 return current_user_can( 'edit_posts' );
275 },
276 )
277 );
278
279 register_rest_route(
280 'visualizer/v' . VISUALIZER_REST_VERSION,
281 '/update-chart',
282 array(
283 'methods' => 'POST',
284 'callback' => array( $this, 'update_chart_data' ),
285 'args' => array(
286 'id' => array(
287 'sanitize_callback' => 'absint',
288 ),
289 ),
290 'permission_callback' => function () {
291 return current_user_can( 'edit_posts' );
292 },
293 )
294 );
295
296 register_rest_route(
297 'visualizer/v' . VISUALIZER_REST_VERSION,
298 '/upload-data',
299 array(
300 'methods' => 'POST',
301 'callback' => array( $this, 'upload_csv_data' ),
302 'args' => array(
303 'url' => array(
304 'sanitize_callback' => 'esc_url_raw',
305 ),
306 ),
307 'permission_callback' => function () {
308 return current_user_can( 'edit_posts' );
309 },
310 )
311 );
312
313 register_rest_route(
314 'visualizer/v' . VISUALIZER_REST_VERSION,
315 '/get-permission-data',
316 array(
317 'methods' => 'GET',
318 'callback' => array( $this, 'get_permission_data' ),
319 'args' => array(
320 'type' => array(
321 'sanitize_callback' => 'sanitize_text_field',
322 ),
323 ),
324 'permission_callback' => function () {
325 return current_user_can( 'edit_posts' );
326 },
327 )
328 );
329 }
330
331 /**
332 * Get Post Meta Fields
333 */
334 public function get_visualizer_data( $post ) {
335 if ( ! current_user_can( 'edit_posts' ) ) {
336 return false;
337 }
338
339 $data = array();
340 $post_id = $post['id'];
341
342 $data['visualizer-chart-type'] = get_post_meta( $post_id, Visualizer_Plugin::CF_CHART_TYPE, true );
343
344 $library = get_post_meta( $post_id, Visualizer_Plugin::CF_CHART_LIBRARY, true );
345 $data['visualizer-chart-library'] = $library;
346
347 $data['visualizer-source'] = get_post_meta( $post_id, Visualizer_Plugin::CF_SOURCE, true );
348
349 $data['visualizer-default-data'] = get_post_meta( $post_id, Visualizer_Plugin::CF_DEFAULT_DATA, true );
350
351 // faetch and update settings
352 $data['visualizer-settings'] = get_post_meta( $post_id, Visualizer_Plugin::CF_SETTINGS, true );
353
354 // handle series filter hooks
355 $data['visualizer-series'] = apply_filters( Visualizer_Plugin::FILTER_GET_CHART_SERIES, get_post_meta( $post_id, Visualizer_Plugin::CF_SERIES, true ), $post_id, $data['visualizer-chart-type'] );
356
357 // handle settings filter hooks
358 $data['visualizer-settings'] = apply_filters( Visualizer_Plugin::FILTER_GET_CHART_SETTINGS, $data['visualizer-settings'], $post_id, $data['visualizer-chart-type'] );
359
360 // handle data filter hooks
361 $data['visualizer-data'] = apply_filters( Visualizer_Plugin::FILTER_GET_CHART_DATA, unserialize( html_entity_decode( get_the_content( $post_id ) ) ), $post_id, $data['visualizer-chart-type'] );
362
363 // we are going to format only for tabular charts, because we are not sure of the effect on others.
364 // this is to solve the case where boolean data shows up as all-ticks on gutenberg.
365 if ( in_array( $data['visualizer-chart-type'], array( 'tabular' ), true ) ) {
366 $data['visualizer-data'] = $this->format_chart_data( $data['visualizer-data'], $data['visualizer-series'] );
367 }
368
369 $data['visualizer-data-exploded'] = '';
370 // handle annotations for google charts
371 if ( 'GoogleCharts' === $library ) {
372 // this will contain the data of both axis.
373 $settings = $data['visualizer-settings'];
374 // this will contain data only of Y axis.
375 $series = $data['visualizer-series'];
376 $annotations = array();
377 if ( isset( $settings['series'] ) ) {
378 foreach ( $settings['series'] as $index => $serie ) {
379 // skip X axis data.
380 if ( $index === 0 ) {
381 continue;
382 }
383 if ( ! empty( $serie['role'] ) ) {
384 // this series is some kind of annotation, so let's collect its index.
385 // the index will be +1 because the X axis value is index 0, which is being ignored.
386 $annotations[ 'role' . ( intval( $index ) + 1 ) ] = $serie['role'];
387
388 if ( isset( $data['visualizer-series'][ intval( $index ) + 1 ] ) ) {
389 $data['visualizer-series'][ intval( $index ) + 1 ]['role'] = $serie['role'];
390 }
391 }
392 }
393 }
394 if ( ! empty( $annotations ) ) {
395 $exploded_data = array();
396 $series_names = array();
397 foreach ( $series as $index => $serie ) {
398 // skip X axis data.
399 if ( $index === 0 ) {
400 continue;
401 }
402 if ( array_key_exists( 'role' . $index, $annotations ) ) {
403 $series_names[] = (object) array( 'role' => $annotations[ 'role' . $index ], 'type' => $serie['type'] );
404 } else {
405 $series_names[] = $serie['label'];
406 }
407 }
408 $exploded_data[] = $series_names;
409
410 foreach ( $data['visualizer-data'] as $datum ) {
411 // skip X axis data.
412 unset( $datum[0] );
413 $exploded_data[] = $datum;
414 }
415 $data['visualizer-data-exploded'] = array( $exploded_data );
416 }
417 }
418
419 $import = get_post_meta( $post_id, Visualizer_Plugin::CF_CHART_URL, true );
420
421 $schedule = get_post_meta( $post_id, Visualizer_Plugin::CF_CHART_SCHEDULE, true );
422
423 $db_schedule = get_post_meta( $post_id, Visualizer_Plugin::CF_DB_SCHEDULE, true );
424
425 $db_query = get_post_meta( $post_id, Visualizer_Plugin::CF_DB_QUERY, true );
426
427 $json_url = get_post_meta( $post_id, Visualizer_Plugin::CF_JSON_URL, true );
428
429 $json_headers = get_post_meta( $post_id, Visualizer_Plugin::CF_JSON_HEADERS, true );
430
431 $json_schedule = get_post_meta( $post_id, Visualizer_Plugin::CF_JSON_SCHEDULE, true );
432
433 $json_root = get_post_meta( $post_id, Visualizer_Plugin::CF_JSON_ROOT, true );
434
435 $json_paging = get_post_meta( $post_id, Visualizer_Plugin::CF_JSON_PAGING, true );
436
437 if ( ! empty( $import ) && ! empty( $schedule ) ) {
438 $data['visualizer-chart-url'] = $import;
439 $data['visualizer-chart-schedule'] = $schedule;
440 }
441
442 if ( ! empty( $db_schedule ) && ! empty( $db_query ) ) {
443 $data['visualizer-db-schedule'] = $db_schedule;
444 $data['visualizer-db-query'] = $db_query;
445 }
446
447 if ( ! empty( $json_url ) ) {
448 $data['visualizer-json-schedule'] = $json_schedule;
449 $data['visualizer-json-url'] = $json_url;
450 $data['visualizer-json-headers'] = $json_headers;
451 $data['visualizer-json-root'] = $json_root;
452
453 if ( Visualizer_Module::is_pro() && ! empty( $json_paging ) ) {
454 $data['visualizer-json-paging'] = $json_paging;
455 }
456 }
457
458 if ( Visualizer_Module::is_pro() ) {
459 $permissions = get_post_meta( $post_id, Visualizer_PRO::CF_PERMISSIONS, true );
460
461 if ( empty( $permissions ) ) {
462 $permissions = array( 'permissions' => array(
463 'read' => 'all',
464 'edit' => 'roles',
465 'edit-specific' => array( 'administrator' ),
466 ),
467 );
468 }
469
470 $data['visualizer-permissions'] = $permissions;
471 }
472
473 return $data;
474 }
475
476 /**
477 * Returns the data for the query.
478 *
479 * @access public
480 */
481 public function get_query_data( $data ) {
482 if ( ! current_user_can( 'edit_posts' ) ) {
483 return false;
484 }
485
486 $source = new Visualizer_Source_Query( stripslashes( $data['query'] ) );
487 $html = $source->fetch( true );
488 $source->fetch( false );
489 $name = $source->getSourceName();
490 $series = $source->getSeries();
491 $data = $source->getRawData();
492 $error = '';
493 if ( empty( $html ) ) {
494 $error = $source->get_error();
495 wp_send_json_error( array( 'msg' => $error ) );
496 }
497 wp_send_json_success( array( 'table' => $html, 'name' => $name, 'series' => $series, 'data' => $data ) );
498 }
499
500 /**
501 * Returns the JSON root.
502 *
503 * @access public
504 */
505 public function get_json_root_data( $data ) {
506 if ( ! current_user_can( 'edit_posts' ) ) {
507 return false;
508 }
509
510 $source = new Visualizer_Source_Json( $data );
511
512 $roots = $source->fetchRoots();
513 if ( empty( $roots ) ) {
514 wp_send_json_error( array( 'msg' => $source->get_error() ) );
515 }
516
517 wp_send_json_success( array( 'url' => $data['url'], 'roots' => $roots ) );
518 }
519
520 /**
521 * Returns the JSON data.
522 *
523 * @access public
524 */
525 public function get_json_data( $data ) {
526 if ( ! current_user_can( 'edit_posts' ) ) {
527 return false;
528 }
529
530 $chart_id = $data['chart'];
531
532 if ( empty( $chart_id ) ) {
533 wp_die();
534 }
535
536 $source = new Visualizer_Source_Json( $data );
537 $source->fetch();
538 $table = $source->getRawData();
539
540 if ( empty( $table ) ) {
541 wp_send_json_error( array( 'msg' => esc_html__( 'Unable to fetch data from the endpoint. Please try again.', 'visualizer' ) ) );
542 }
543
544 $table = Visualizer_Render_Layout::show( 'editor-table', $table, $chart_id, 'viz-json-table', false, false );
545 wp_send_json_success( array( 'table' => $table, 'root' => $data['root'], 'url' => $data['url'], 'paging' => $source->getPaginationElements() ) );
546 }
547
548 /**
549 * Set the JSON data.
550 *
551 * @access public
552 */
553 public function set_json_data( $data ) {
554 if ( ! current_user_can( 'edit_posts' ) ) {
555 return false;
556 }
557
558 $source = new Visualizer_Source_Json( $data );
559
560 $table = $source->fetch();
561 if ( empty( $table ) ) {
562 wp_send_json_error( array( 'msg' => esc_html__( 'Unable to fetch data from the endpoint. Please try again.', 'visualizer' ) ) );
563 }
564
565 $source->fetchFromEditableTable();
566 $name = $source->getSourceName();
567 $series = json_encode( $source->getSeries() );
568 $data = json_encode( $source->getRawData() );
569 wp_send_json_success( array( 'name' => $name, 'series' => $series, 'data' => $data ) );
570 }
571
572 /**
573 * Rest Callback Method
574 */
575 public function update_chart_data( $data ) {
576 if ( ! current_user_can( 'edit_posts' ) ) {
577 return false;
578 }
579
580 if ( $data['id'] && ! is_wp_error( $data['id'] ) ) {
581 if ( get_post_type( $data['id'] ) !== Visualizer_Plugin::CPT_VISUALIZER ) {
582 return new WP_Error( 'invalid_post_type', 'Invalid post type.' );
583 }
584 $chart_type = sanitize_text_field( $data['visualizer-chart-type'] );
585 $source_type = sanitize_text_field( $data['visualizer-source'] );
586
587 update_post_meta( $data['id'], Visualizer_Plugin::CF_CHART_TYPE, $chart_type );
588 update_post_meta( $data['id'], Visualizer_Plugin::CF_SOURCE, $source_type );
589 update_post_meta( $data['id'], Visualizer_Plugin::CF_DEFAULT_DATA, $data['visualizer-default-data'] );
590 update_post_meta( $data['id'], Visualizer_Plugin::CF_SERIES, $data['visualizer-series'] );
591 update_post_meta( $data['id'], Visualizer_Plugin::CF_SETTINGS, $data['visualizer-settings'] );
592
593 if ( $data['visualizer-chart-url'] && $data['visualizer-chart-schedule'] ) {
594 $chart_url = esc_url_raw( $data['visualizer-chart-url'] );
595 $chart_schedule = intval( $data['visualizer-chart-schedule'] );
596 update_post_meta( $data['id'], Visualizer_Plugin::CF_CHART_URL, $chart_url );
597 apply_filters( 'visualizer_pro_chart_schedule', $data['id'], $chart_url, $chart_schedule );
598 } else {
599 delete_post_meta( $data['id'], Visualizer_Plugin::CF_CHART_URL );
600 apply_filters( 'visualizer_pro_remove_schedule', $data['id'] );
601 }
602
603 // let's check if this is not an external db chart
604 // as there is no support for that in the block editor interface
605 $external_params = get_post_meta( $data['id'], Visualizer_Plugin::CF_REMOTE_DB_PARAMS, true );
606 if ( empty( $external_params ) ) {
607 if ( $source_type === 'Visualizer_Source_Query' ) {
608 $db_schedule = intval( $data['visualizer-db-schedule'] );
609 $db_query = $data['visualizer-db-query'];
610 update_post_meta( $data['id'], Visualizer_Plugin::CF_DB_SCHEDULE, $db_schedule );
611 update_post_meta( $data['id'], Visualizer_Plugin::CF_DB_QUERY, stripslashes( $db_query ) );
612 } else {
613 delete_post_meta( $data['id'], Visualizer_Plugin::CF_DB_SCHEDULE );
614 delete_post_meta( $data['id'], Visualizer_Plugin::CF_DB_QUERY );
615 }
616 }
617
618 if ( $source_type === 'Visualizer_Source_Json' ) {
619 $json_schedule = intval( $data['visualizer-json-schedule'] );
620 $json_url = esc_url_raw( $data['visualizer-json-url'] );
621 $json_headers = esc_url_raw( $data['visualizer-json-headers'] );
622 $json_root = $data['visualizer-json-root'];
623 $json_paging = $data['visualizer-json-paging'];
624
625 update_post_meta( $data['id'], Visualizer_Plugin::CF_JSON_SCHEDULE, $json_schedule );
626 update_post_meta( $data['id'], Visualizer_Plugin::CF_JSON_URL, $json_url );
627 update_post_meta( $data['id'], Visualizer_Plugin::CF_JSON_HEADERS, $json_headers );
628 update_post_meta( $data['id'], Visualizer_Plugin::CF_JSON_ROOT, $json_root );
629
630 if ( ! empty( $json_paging ) ) {
631 update_post_meta( $data['id'], Visualizer_Plugin::CF_JSON_PAGING, $json_paging );
632 } else {
633 delete_post_meta( $data['id'], Visualizer_Plugin::CF_JSON_PAGING );
634 }
635 } else {
636 delete_post_meta( $data['id'], Visualizer_Plugin::CF_JSON_SCHEDULE );
637 delete_post_meta( $data['id'], Visualizer_Plugin::CF_JSON_URL );
638 delete_post_meta( $data['id'], Visualizer_Plugin::CF_JSON_HEADERS );
639 delete_post_meta( $data['id'], Visualizer_Plugin::CF_JSON_ROOT );
640 delete_post_meta( $data['id'], Visualizer_Plugin::CF_JSON_PAGING );
641 }
642
643 if ( Visualizer_Module::is_pro() ) {
644 update_post_meta( $data['id'], Visualizer_PRO::CF_PERMISSIONS, $data['visualizer-permissions'] );
645 }
646
647 if ( $data['visualizer-chart-url'] ) {
648 $chart_url = esc_url_raw( $data['visualizer-chart-url'] );
649 $content['source'] = $chart_url;
650 $content['data'] = $this->format_chart_data( $data['visualizer-data'], $data['visualizer-series'] );
651 } else {
652 $content = $this->format_chart_data( $data['visualizer-data'], $data['visualizer-series'] );
653 }
654
655 $chart = array(
656 'ID' => $data['id'],
657 'post_content' => serialize( $content ),
658 );
659
660 wp_update_post( $chart );
661
662 // Clear existing chart cache.
663 $cache_key = Visualizer_Plugin::CF_CHART_CACHE . '_' . $data['id'];
664 if ( get_transient( $cache_key ) ) {
665 delete_transient( $cache_key );
666 }
667
668 $revisions = wp_get_post_revisions( $data['id'], array( 'order' => 'ASC' ) );
669
670 if ( count( $revisions ) > 1 ) {
671 $revision_ids = array_keys( $revisions );
672
673 // delete all revisions.
674 foreach ( $revision_ids as $id ) {
675 wp_delete_post_revision( $id );
676 }
677 }
678
679 return new \WP_REST_Response( array( 'success' => sprintf( 'Chart updated' ) ) );
680 }
681 }
682
683 /**
684 * Format chart data.
685 *
686 * Note: No matter how tempted, don't use the similar method from Visualizer_Source. That works on a different structure.
687 */
688 public function format_chart_data( $data, $series ) {
689 foreach ( $series as $i => $row ) {
690 // if no value exists for the seires, then add null
691 if ( ! isset( $series[ $i ] ) ) {
692 $series[ $i ] = null;
693 }
694
695 if ( is_null( $series[ $i ] ) ) {
696 continue;
697 }
698
699 switch ( $row['type'] ) {
700 case 'number':
701 foreach ( $data as $o => $col ) {
702 $data[ $o ][ $i ] = ( is_numeric( $col[ $i ] ) ) ? floatval( $col[ $i ] ) : ( is_numeric( str_replace( ',', '', $col[ $i ] ) ) ? floatval( str_replace( ',', '', $col[ $i ] ) ) : null );
703 }
704 break;
705 case 'boolean':
706 foreach ( $data as $o => $col ) {
707 $data[ $o ][ $i ] = ! empty( $col[ $i ] ) ? filter_var( $col[ $i ], FILTER_VALIDATE_BOOLEAN ) : false;
708 }
709 break;
710 case 'timeofday':
711 foreach ( $data as $o => $col ) {
712 $date = new DateTime( '1984-03-16T' . $col[ $i ] );
713 if ( $date ) {
714 $data[ $o ][ $i ] = array(
715 intval( $date->format( 'H' ) ),
716 intval( $date->format( 'i' ) ),
717 intval( $date->format( 's' ) ),
718 0,
719 );
720 }
721 }
722 break;
723 case 'string':
724 foreach ( $data as $o => $col ) {
725 $data[ $o ][ $i ] = $this->toUTF8( $col[ $i ] );
726 }
727 break;
728 }
729 }
730
731 return $data;
732 }
733
734 /**
735 * Use toUTF8 function
736 */
737 public function toUTF8( $datum ) {
738 if ( ! function_exists( 'mb_detect_encoding' ) || mb_detect_encoding( $datum ) !== 'ASCII' ) {
739 $datum = \ForceUTF8\Encoding::toUTF8( $datum );
740 }
741 return $datum;
742 }
743
744 /**
745 * Handle remote CSV data
746 */
747 public function upload_csv_data( $data ) {
748 if ( ! current_user_can( 'edit_posts' ) ) {
749 return false;
750 }
751
752 if ( $data['url'] && ! is_wp_error( $data['url'] ) && filter_var( $data['url'], FILTER_VALIDATE_URL ) ) {
753 $source = new Visualizer_Source_Csv_Remote( $data['url'] );
754 if ( $source->fetch() ) {
755 $temp = $source->getData();
756 if ( is_string( $temp ) && is_array( unserialize( $temp ) ) ) {
757 $content['series'] = $source->getSeries();
758 $content['data'] = $source->getRawData();
759 return $content;
760 } else {
761 return new \WP_REST_Response( array( 'failed' => sprintf( 'Invalid CSV URL' ) ) );
762 }
763 } else {
764 return new \WP_REST_Response( array( 'failed' => sprintf( 'Invalid CSV URL' ) ) );
765 }
766 } else {
767 return new \WP_REST_Response( array( 'failed' => sprintf( 'Invalid CSV URL' ) ) );
768 }
769 }
770
771 /**
772 * Get permission data
773 */
774 public function get_permission_data( $data ) {
775 if ( ! current_user_can( 'edit_posts' ) ) {
776 return false;
777 }
778
779 $options = array();
780 switch ( $data['type'] ) {
781 case 'users':
782 $query = new WP_User_Query(
783 array(
784 'number' => 1000,
785 'orderby' => 'display_name',
786 'fields' => array( 'ID', 'display_name' ),
787 'count_total' => false,
788 )
789 );
790 $users = $query->get_results();
791 if ( ! empty( $users ) ) {
792 $i = 0;
793 foreach ( $users as $user ) {
794 $options[ $i ]['value'] = $user->ID;
795 $options[ $i ]['label'] = $user->display_name;
796 $i++;
797 }
798 }
799 break;
800 case 'roles':
801 if ( ! function_exists( 'get_editable_roles' ) ) {
802 require_once ABSPATH . 'wp-admin/includes/user.php';
803 }
804 $roles = get_editable_roles();
805 if ( ! empty( $roles ) ) {
806 $i = 0;
807 foreach ( get_editable_roles() as $name => $info ) {
808 $options[ $i ]['value'] = $name;
809 $options[ $i ]['label'] = $name;
810 $i++;
811 }
812 }
813 break;
814 }
815 return $options;
816 }
817
818 /**
819 * Filter Rest Query
820 */
821 public function add_rest_query_vars( $args, \WP_REST_Request $request ) {
822 if ( isset( $request['meta_key'] ) && isset( $request['meta_value'] ) ) {
823 $args['meta_query'] = array(
824 'relation' => 'OR',
825 array(
826 'key' => $request->get_param( 'meta_key' ),
827 'value' => $request->get_param( 'meta_value' ),
828 'compare' => '!=',
829 ),
830 array(
831 'key' => $request->get_param( 'meta_key' ),
832 'value' => $request->get_param( 'meta_value' ),
833 'compare' => 'NOT EXISTS',
834 ),
835 );
836 }
837 return $args;
838 }
839 }
840