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

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