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

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