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

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

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