PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.7.12
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.7.12
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.12, at classes/Visualizer/Gutenberg/Block.php

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