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

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