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

862 lines 27.1 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', 'lodash' ), $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 ) && $schedule >= 0 ) {
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'] >= 0 ) {
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 if ( 'Visualizer_Source_Csv_Remote' === $source_type ) {
626 $schedule_url = $data['visualizer-chart-url'];
627 $schedule_id = $data['visualizer-chart-schedule'];
628 update_post_meta( $data['id'], Visualizer_Plugin::CF_CHART_URL, $schedule_url );
629 update_post_meta( $data['id'], Visualizer_Plugin::CF_CHART_SCHEDULE, $schedule_id );
630 } else {
631 delete_post_meta( $data['id'], Visualizer_Plugin::CF_CHART_URL );
632 delete_post_meta( $data['id'], Visualizer_Plugin::CF_CHART_SCHEDULE );
633 }
634 }
635
636 if ( $source_type === 'Visualizer_Source_Json' ) {
637 $json_schedule = intval( $data['visualizer-json-schedule'] );
638 $json_url = esc_url_raw( $data['visualizer-json-url'] );
639 $json_headers = esc_url_raw( $data['visualizer-json-headers'] );
640 $json_root = $data['visualizer-json-root'];
641 $json_paging = $data['visualizer-json-paging'];
642
643 update_post_meta( $data['id'], Visualizer_Plugin::CF_JSON_SCHEDULE, $json_schedule );
644 update_post_meta( $data['id'], Visualizer_Plugin::CF_JSON_URL, $json_url );
645 update_post_meta( $data['id'], Visualizer_Plugin::CF_JSON_HEADERS, $json_headers );
646 update_post_meta( $data['id'], Visualizer_Plugin::CF_JSON_ROOT, $json_root );
647
648 if ( ! empty( $json_paging ) ) {
649 update_post_meta( $data['id'], Visualizer_Plugin::CF_JSON_PAGING, $json_paging );
650 } else {
651 delete_post_meta( $data['id'], Visualizer_Plugin::CF_JSON_PAGING );
652 }
653 } else {
654 delete_post_meta( $data['id'], Visualizer_Plugin::CF_JSON_SCHEDULE );
655 delete_post_meta( $data['id'], Visualizer_Plugin::CF_JSON_URL );
656 delete_post_meta( $data['id'], Visualizer_Plugin::CF_JSON_HEADERS );
657 delete_post_meta( $data['id'], Visualizer_Plugin::CF_JSON_ROOT );
658 delete_post_meta( $data['id'], Visualizer_Plugin::CF_JSON_PAGING );
659 }
660
661 if ( Visualizer_Module::is_pro() ) {
662 update_post_meta( $data['id'], Visualizer_PRO::CF_PERMISSIONS, $data['visualizer-permissions'] );
663 }
664
665 if ( $data['visualizer-chart-url'] ) {
666 $chart_url = esc_url_raw( $data['visualizer-chart-url'] );
667 $content['source'] = $chart_url;
668 $content['data'] = $this->format_chart_data( $data['visualizer-data'], $data['visualizer-series'] );
669 } else {
670 $content = $this->format_chart_data( $data['visualizer-data'], $data['visualizer-series'] );
671 }
672
673 $chart = array(
674 'ID' => $data['id'],
675 'post_content' => serialize( $content ),
676 );
677
678 wp_update_post( $chart );
679
680 // Clear existing chart cache.
681 $cache_key = Visualizer_Plugin::CF_CHART_CACHE . '_' . $data['id'];
682 if ( get_transient( $cache_key ) ) {
683 delete_transient( $cache_key );
684 }
685
686 $revisions = wp_get_post_revisions( $data['id'], array( 'order' => 'ASC' ) );
687
688 if ( count( $revisions ) > 1 ) {
689 $revision_ids = array_keys( $revisions );
690
691 // delete all revisions.
692 foreach ( $revision_ids as $id ) {
693 wp_delete_post_revision( $id );
694 }
695 }
696
697 return new \WP_REST_Response( array( 'success' => sprintf( 'Chart updated' ) ) );
698 }
699 }
700
701 /**
702 * Format chart data.
703 *
704 * Note: No matter how tempted, don't use the similar method from Visualizer_Source. That works on a different structure.
705 */
706 public function format_chart_data( $data, $series ) {
707 foreach ( $series as $i => $row ) {
708 // if no value exists for the seires, then add null
709 if ( ! isset( $series[ $i ] ) ) {
710 $series[ $i ] = null;
711 }
712
713 if ( is_null( $series[ $i ] ) ) {
714 continue;
715 }
716
717 switch ( $row['type'] ) {
718 case 'number':
719 foreach ( $data as $o => $col ) {
720 $data[ $o ][ $i ] = ( is_numeric( $col[ $i ] ) ) ? floatval( $col[ $i ] ) : ( is_numeric( str_replace( ',', '', $col[ $i ] ) ) ? floatval( str_replace( ',', '', $col[ $i ] ) ) : null );
721 }
722 break;
723 case 'boolean':
724 foreach ( $data as $o => $col ) {
725 $data[ $o ][ $i ] = ! empty( $col[ $i ] ) ? filter_var( $col[ $i ], FILTER_VALIDATE_BOOLEAN ) : false;
726 }
727 break;
728 case 'timeofday':
729 foreach ( $data as $o => $col ) {
730 $date = new DateTime( '1984-03-16T' . $col[ $i ] );
731 if ( $date ) {
732 $data[ $o ][ $i ] = array(
733 intval( $date->format( 'H' ) ),
734 intval( $date->format( 'i' ) ),
735 intval( $date->format( 's' ) ),
736 0,
737 );
738 }
739 }
740 break;
741 case 'string':
742 foreach ( $data as $o => $col ) {
743 $data[ $o ][ $i ] = $this->toUTF8( $col[ $i ] );
744 }
745 break;
746 }
747 }
748
749 return $data;
750 }
751
752 /**
753 * Use toUTF8 function
754 */
755 public function toUTF8( $datum ) {
756 if ( ! function_exists( 'mb_detect_encoding' ) || mb_detect_encoding( $datum ) !== 'ASCII' ) {
757 $datum = \ForceUTF8\Encoding::toUTF8( $datum );
758 }
759 return $datum;
760 }
761
762 /**
763 * Handle remote CSV data
764 */
765 public function upload_csv_data( $data ) {
766 if ( ! current_user_can( 'edit_posts' ) ) {
767 return false;
768 }
769
770 $remote_data = false;
771 if ( isset( $data['url'] ) && function_exists( 'wp_http_validate_url' ) ) {
772 $remote_data = wp_http_validate_url( $data['url'] );
773 }
774 if ( false !== $remote_data && ! is_wp_error( $remote_data ) ) {
775 $source = new Visualizer_Source_Csv_Remote( $remote_data );
776 if ( $source->fetch() ) {
777 $temp = $source->getData();
778 if ( is_string( $temp ) && is_array( unserialize( $temp ) ) ) {
779 $content['series'] = $source->getSeries();
780 $content['data'] = $source->getRawData();
781 return $content;
782 } else {
783 return new \WP_REST_Response( array( 'failed' => sprintf( 'Invalid CSV URL' ) ) );
784 }
785 } else {
786 return new \WP_REST_Response( array( 'failed' => sprintf( 'Invalid CSV URL' ) ) );
787 }
788 } else {
789 return new \WP_REST_Response( array( 'failed' => sprintf( 'Invalid CSV URL' ) ) );
790 }
791 }
792
793 /**
794 * Get permission data
795 */
796 public function get_permission_data( $data ) {
797 if ( ! current_user_can( 'edit_posts' ) ) {
798 return false;
799 }
800
801 $options = array();
802 switch ( $data['type'] ) {
803 case 'users':
804 $query = new WP_User_Query(
805 array(
806 'number' => 1000,
807 'orderby' => 'display_name',
808 'fields' => array( 'ID', 'display_name' ),
809 'count_total' => false,
810 )
811 );
812 $users = $query->get_results();
813 if ( ! empty( $users ) ) {
814 $i = 0;
815 foreach ( $users as $user ) {
816 $options[ $i ]['value'] = $user->ID;
817 $options[ $i ]['label'] = $user->display_name;
818 $i++;
819 }
820 }
821 break;
822 case 'roles':
823 if ( ! function_exists( 'get_editable_roles' ) ) {
824 require_once ABSPATH . 'wp-admin/includes/user.php';
825 }
826 $roles = get_editable_roles();
827 if ( ! empty( $roles ) ) {
828 $i = 0;
829 foreach ( get_editable_roles() as $name => $info ) {
830 $options[ $i ]['value'] = $name;
831 $options[ $i ]['label'] = $name;
832 $i++;
833 }
834 }
835 break;
836 }
837 return $options;
838 }
839
840 /**
841 * Filter Rest Query
842 */
843 public function add_rest_query_vars( $args, \WP_REST_Request $request ) {
844 if ( isset( $request['meta_key'] ) && isset( $request['meta_value'] ) ) {
845 $args['meta_query'] = array(
846 'relation' => 'OR',
847 array(
848 'key' => $request->get_param( 'meta_key' ),
849 'value' => $request->get_param( 'meta_value' ),
850 'compare' => '!=',
851 ),
852 array(
853 'key' => $request->get_param( 'meta_key' ),
854 'value' => $request->get_param( 'meta_value' ),
855 'compare' => 'NOT EXISTS',
856 ),
857 );
858 }
859 return $args;
860 }
861 }
862