PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.7.1
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.7.1
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 / Module / Frontend.php

Frontend.php in Visualizer – Tables & Charts Manager with Built-in AI Generator 3.7.1, at classes/Visualizer/Module/Frontend.php

612 lines 19.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 // +----------------------------------------------------------------------+
4 // | Copyright 2013 Madpixels (email : visualizer@madpixels.net) |
5 // +----------------------------------------------------------------------+
6 // | This program is free software; you can redistribute it and/or modify |
7 // | it under the terms of the GNU General Public License, version 2, as |
8 // | published by the Free Software Foundation. |
9 // | |
10 // | This program is distributed in the hope that it will be useful, |
11 // | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 // | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 // | GNU General Public License for more details. |
14 // | |
15 // | You should have received a copy of the GNU General Public License |
16 // | along with this program; if not, write to the Free Software |
17 // | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, |
18 // | MA 02110-1301 USA |
19 // +----------------------------------------------------------------------+
20 // | Author: Eugene Manuilov <eugene@manuilov.org> |
21 // +----------------------------------------------------------------------+
22 /**
23 * Frontend module class.
24 *
25 * @category Visualizer
26 * @package Module
27 *
28 * @since 1.0.0
29 */
30 class Visualizer_Module_Frontend extends Visualizer_Module {
31
32 const NAME = __CLASS__;
33
34 /**
35 * The array of charts to render.
36 *
37 * @since 1.0.0
38 *
39 * @access private
40 * @var array
41 */
42 private $_charts = array();
43
44 /**
45 * Constructor.
46 *
47 * @since 1.0.0
48 * @uses add_filter() To add "do_shortcode" hook for "widget_text" and "term_description" filters.
49 *
50 * @access public
51 * @param Visualizer_Plugin $plugin The instance of the plugin.
52 */
53 public function __construct( Visualizer_Plugin $plugin ) {
54 parent::__construct( $plugin );
55
56 $this->_addAction( 'wp_enqueue_scripts', 'enqueueScripts' );
57 $this->_addAction( 'load-index.php', 'enqueueScripts' );
58 $this->_addAction( 'visualizer_enqueue_scripts', 'enqueueScripts' );
59 $this->_addFilter( 'visualizer_get_language', 'getLanguage' );
60 $this->_addShortcode( 'visualizer', 'renderChart' );
61
62 // add do_shortocde hook for widget_text filter
63 if ( ! has_filter( 'widget_text', 'do_shortcode' ) ) {
64 add_filter( 'widget_text', 'do_shortcode' );
65 }
66
67 // add do_shortcode hook for term_description filter
68 if ( ! has_filter( 'term_description', 'do_shortcode' ) ) {
69 add_filter( 'term_description', 'do_shortcode' );
70 }
71
72 add_action( 'rest_api_init', array( $this, 'endpoint_register' ) );
73
74 $this->_addFilter( 'script_loader_tag', 'script_loader_tag', null, 10, 3 );
75 }
76
77 /**
78 * Adds the async attribute to certain scripts.
79 */
80 function script_loader_tag( $tag, $handle, $src ) {
81 if ( is_admin() ) {
82 return $tag;
83 }
84
85 $scripts = array( 'google-jsapi', 'visualizer-render-google-lib', 'visualizer-render-google' );
86
87 foreach ( $scripts as $async ) {
88 if ( $async === $handle ) {
89 $tag = str_replace( ' src', ' defer="defer" src', $tag );
90 break;
91 }
92 }
93 return $tag;
94 }
95
96 /**
97 * Returns the language/locale.
98 */
99 function getLanguage( $dummy, $only_language ) {
100 return $this->get_language();
101 }
102
103
104 /**
105 * Registers the endpoints
106 */
107 function endpoint_register() {
108 register_rest_route(
109 'visualizer/v' . VISUALIZER_REST_VERSION,
110 '/action/(?P<chart>\d+)/(?P<type>.+)/',
111 array(
112 // POST is required for save/cancel, GET for all others
113 'methods' => array( 'POST', 'GET' ),
114 'args' => array(
115 'chart' => array(
116 'required' => true,
117 'sanitize_callback' => function( $param ) {
118 return is_numeric( $param ) ? $param : null;
119 },
120 ),
121 'type' => array(
122 'required' => true,
123 'type' => 'string',
124 'enum' => array_merge( array( 'save', 'cancel' ), array_keys( $this->get_actions() ) ),
125 ),
126 ),
127 'permission_callback' => function ( WP_REST_Request $request ) {
128 $chart_id = filter_var( sanitize_text_field( $request->get_param( 'chart' ), FILTER_VALIDATE_INT ) );
129 if ( ! empty( $chart_id ) && in_array( $request->get_param( 'type' ), array( 'save', 'cancel' ), true ) ) {
130 // let save and cancel go without any check as past version of pro
131 // did not send the X-WP-Nonce
132 // we can change this at a later date.
133 return true;
134 }
135 return ! empty( $chart_id ) && apply_filters( 'visualizer_pro_show_chart', true, $chart_id );
136 },
137 'callback' => array( $this, 'perform_action' ),
138 )
139 );
140 }
141
142 /**
143 * All possible actions and their labels
144 *
145 * @access private
146 */
147 private function get_actions() {
148 $actions = apply_filters(
149 'visualizer_action_buttons',
150 array(
151 'print' => array(
152 'label' => esc_html__( 'Print', 'visualizer' ),
153 'title' => esc_html__( 'Print Chart', 'visualizer' ),
154 ),
155 'csv' => array(
156 'label' => esc_html__( 'CSV', 'visualizer' ),
157 'title' => esc_html__( 'Download as a CSV', 'visualizer' ),
158 ),
159 'xls' => array(
160 'label' => esc_html__( 'Excel', 'visualizer' ),
161 'title' => esc_html__( 'Download as a spreadsheet', 'visualizer' ),
162 ),
163 'copy' => array(
164 'label' => esc_html__( 'Copy', 'visualizer' ),
165 'title' => esc_html__( 'Copy data', 'visualizer' ),
166 ),
167 'image' => array(
168 'label' => esc_html__( 'Download', 'visualizer' ),
169 'title' => esc_html__( 'Download as an image', 'visualizer' ),
170 ),
171 )
172 );
173
174 // backward compatibility before label and title attributes were introduced.
175 if ( isset( $actions['edit'] ) && is_string( $actions['edit'] ) ) {
176 $actions['edit'] = array(
177 'label' => esc_html__( 'Edit', 'visualizer' ),
178 'title' => esc_html__( 'Edit data', 'visualizer' ),
179 );
180 }
181 return $actions;
182 }
183
184 /**
185 * The print button
186 *
187 * @since 1.0.0
188 *
189 * @access public
190 */
191 public function perform_action( WP_REST_Request $params ) {
192 $chart_id = filter_var( sanitize_text_field( $params['chart'] ), FILTER_VALIDATE_INT );
193 $type = sanitize_text_field( $params['type'] );
194 $data = null;
195
196 if ( ! $chart_id ) {
197 return new WP_REST_Response( array( 'error' => new WP_Error( __( 'Invalid chart ID', 'visualizer' ) ) ) );
198 }
199
200 if ( ! $type ) {
201 return new WP_REST_Response( array( 'error' => new WP_Error( __( 'Invalid action', 'visualizer' ) ) ) );
202 }
203
204 switch ( $type ) {
205 case 'print':
206 $data = $this->_getDataAs( $chart_id, 'print' );
207 break;
208 case 'csv':
209 $data = $this->_getDataAs( $chart_id, 'csv' );
210 break;
211 case 'xls':
212 $data = $this->_getDataAs( $chart_id, 'xls' );
213 break;
214 case 'image':
215 $settings = get_post_meta( $chart_id, Visualizer_Plugin::CF_SETTINGS, true );
216 $title = 'visualizer#' . $chart_id;
217 if ( ! empty( $settings['title'] ) ) {
218 $title = $settings['title'];
219 }
220 // for ChartJS, title is an array.
221 if ( is_array( $title ) && isset( $title['text'] ) ) {
222 $title = $title['text'];
223 }
224 if ( empty( $title ) ) {
225 $title = 'visualizer#' . $chart_id;
226 }
227 $data = array( 'name' => $title );
228 break;
229 default:
230 $data = apply_filters( 'visualizer_action_data', $data, $chart_id, $type, $params, $this );
231 break;
232 }
233
234 return new WP_REST_Response( array( 'data' => $data ) );
235 }
236
237 /**
238 * Registers scripts for charts rendering.
239 *
240 * @since 1.0.0
241 * @uses wp_register_script To register javascript file.
242 *
243 * @access public
244 */
245 public function enqueueScripts() {
246 wp_register_script( 'visualizer-customization', $this->get_user_customization_js(), array(), null, true );
247 wp_register_style( 'visualizer-front', VISUALIZER_ABSURL . 'css/front.css', array(), Visualizer_Plugin::VERSION );
248 do_action( 'visualizer_pro_frontend_load_resources' );
249 }
250
251 /**
252 * Returns placeholder for chart according to visualizer shortcode attributes.
253 *
254 * @since 1.0.0
255 * @uses shortcode_atts() To parse income shortocdes.
256 * @uses apply_filters() To filter chart's data and series arrays.
257 * @uses get_post_meta() To fetch chart's meta information.
258 * @uses wp_enqueue_script() To enqueue charts render script.
259 * @uses wp_localize_script() To add chart data to the page inline script.
260 *
261 * @access public
262 * @param array $atts The array of shortcode attributes.
263 */
264 public function renderChart( $atts ) {
265 global $wp_version;
266 $atts = shortcode_atts(
267 array(
268 // chart id
269 'id' => false,
270 // chart class
271 'class' => false,
272 // for lazy load
273 // set 'yes' to use the default intersection limit (300px)
274 // OR set a number (e.g. 700) to use 700px as the intersection limit
275 'lazy' => apply_filters( 'visualizer_lazy_by_default', false, $atts['id'] ),
276 // Use image chart
277 'use_image' => 'no',
278 ),
279 $atts
280 );
281
282 $chart_data = $this->getChartData( Visualizer_Plugin::CF_CHART_CACHE, $atts['id'] );
283 // if empty chart does not exists, then return empty string.
284 if ( ! $chart_data ) {
285 return '';
286 }
287
288 $chart = $chart_data['chart'];
289 $type = $chart_data['type'];
290 $series = $chart_data['series'];
291 // do not show the chart?
292 if ( ! apply_filters( 'visualizer_pro_show_chart', true, $atts['id'] ) ) {
293 return '';
294 }
295
296 // in case revisions exist.
297 // phpcs:ignore WordPress.CodeAnalysis.AssignmentInCondition.Found
298 if ( true === ( $revisions = $this->undoRevisions( $chart->ID, true ) ) ) {
299 $chart = get_post( $chart->ID );
300 }
301
302 $id = 'visualizer-' . $atts['id'];
303 $defaultClass = 'visualizer-front';
304 $class = apply_filters( Visualizer_Plugin::FILTER_CHART_WRAPPER_CLASS, $atts['class'], $atts['id'] );
305
306 $lazyClass = $atts['lazy'] === 'yes' || ctype_digit( $atts['lazy'] ) ? 'visualizer-lazy' : '';
307
308 $class = sprintf( '%s %s %s %s', $defaultClass, $class, 'visualizer-front-' . $atts['id'], $lazyClass );
309 $attributes = array();
310 if ( ! empty( $class ) ) {
311 $attributes['class'] = trim( $class );
312 }
313
314 if ( ctype_digit( $atts['lazy'] ) ) {
315 $attributes['data-lazy-limit'] = $atts['lazy'];
316 }
317
318 $chart = apply_filters( 'visualizer_schedule_refresh_chart', $chart, $chart->ID, false );
319
320 // Get and update settings.
321 $settings = $chart_data['settings'];
322 if ( empty( $settings['height'] ) ) {
323 $settings['height'] = '400';
324 }
325
326 // handle series filter hooks
327 $series = apply_filters( Visualizer_Plugin::FILTER_GET_CHART_SERIES, $series, $chart->ID, $type );
328
329 // handle settings filter hooks
330 $settings = apply_filters( Visualizer_Plugin::FILTER_GET_CHART_SETTINGS, $settings, $chart->ID, $type );
331
332 // handle data filter hooks
333 $data = self::get_chart_data( $chart, $type );
334
335 $css = '';
336 $arguments = $this->get_inline_custom_css( $id, $settings );
337 if ( ! empty( $arguments ) ) {
338 $css = $arguments[0];
339 $settings = $arguments[1];
340 }
341
342 $library = $this->load_chart_type( $chart->ID );
343
344 $id = $id . '-' . rand();
345
346 $amp = Visualizer_Plugin::instance()->getModule( Visualizer_Module_AMP::NAME );
347 if ( $amp && $amp->is_amp() ) {
348 return '<div data-amp-auto-lightbox-disable id="' . $id . '"' . $this->getHtmlAttributes( $attributes ) . '>' . $amp->get_chart( $chart, $data, $series, $settings ) . '</div>';
349 }
350
351 if ( 'yes' === $atts['use_image'] ) {
352 $chart_image = $chart_data['chart_image'];
353 if ( $chart_image ) {
354 return '<div id="' . $id . '"' . $this->getHtmlAttributes( $attributes ) . '>' . wp_get_attachment_image( $chart_image, 'full' ) . '</div>';
355 }
356 }
357 // Unset chart image base64 string.
358 if ( isset( $settings['chart-img'] ) ) {
359 unset( $settings['chart-img'] );
360 }
361
362 // add chart to the array
363 $this->_charts[ $id ] = array(
364 'type' => $type,
365 'series' => $series,
366 'settings' => $settings,
367 'data' => $data,
368 'library' => $library,
369 );
370
371 $actions_div = '';
372 $actions_visible = apply_filters( 'visualizer_pro_add_actions', isset( $settings['actions'] ) ? $settings['actions'] : array(), $atts['id'] );
373 if ( ! empty( $actions_visible ) ) {
374 $actions = $this->get_actions();
375 $actions_div = '<div class="visualizer-actions">';
376 foreach ( $actions_visible as $action_type ) {
377 $key = $action_type;
378 $mime = '';
379 if ( strpos( $action_type, ';' ) !== false ) {
380 $array = explode( ';', $action_type );
381 $key = $array[0];
382 $mime = end( $array );
383 }
384 $label = $actions[ $key ]['label'];
385 $title = $actions[ $key ]['title'];
386 $actions_div .= sprintf( '<a href="#" class="visualizer-action visualizer-action-%s" data-visualizer-type="%s" data-visualizer-chart-id="%s" data-visualizer-container-id="%s" data-visualizer-mime="%s" title="%s"', $key, $key, $atts['id'], $id, $mime, $title );
387
388 if ( 'copy' === $key ) {
389 $copy = $this->_getDataAs( $atts['id'], 'csv' );
390 $actions_div .= ' data-clipboard-text="' . esc_attr( $copy['csv'] ) . '"';
391 wp_enqueue_script( 'clipboard' );
392 }
393
394 $actions_div .= apply_filters( 'visualizer_action_attributes', '', $key, $atts['id'] );
395 $actions_div .= '>' . $label . '</a> &nbsp;';
396 }
397
398 $actions_div .= '</div>';
399 }
400
401 $actions_div .= $css;
402
403 $_charts = array();
404 $_charts_type = '';
405 foreach ( $this->_charts as $id => $array ) {
406 $_charts = $this->_charts;
407 $library = $array['library'];
408 $_charts_type = $library;
409 if ( ! wp_script_is( "visualizer-render-$library", 'registered' ) ) {
410 wp_register_script(
411 "visualizer-render-$library",
412 VISUALIZER_ABSURL . 'js/render-facade.js',
413 apply_filters( 'visualizer_assets_render', array( 'jquery', 'visualizer-customization' ), true ),
414 Visualizer_Plugin::VERSION,
415 true
416 );
417 wp_enqueue_script( "visualizer-render-$library" );
418 }
419 }
420 if ( wp_script_is( "visualizer-render-$_charts_type" ) ) {
421 wp_localize_script(
422 "visualizer-render-$_charts_type",
423 'visualizer',
424 array(
425 'charts' => $_charts,
426 'language' => $this->get_language(),
427 'map_api_key' => get_option( 'visualizer-map-api-key' ),
428 'rest_url' => version_compare( $wp_version, '4.7.0', '>=' ) ? rest_url( 'visualizer/v' . VISUALIZER_REST_VERSION . '/action/#id#/#type#/' ) : '',
429 'wp_nonce' => wp_create_nonce( 'wp_rest' ),
430 'i10n' => array(
431 'copied' => __( 'The data has been copied to your clipboard. Hit Ctrl-V/Cmd-V in your spreadsheet editor to paste the data.', 'visualizer' ),
432 ),
433 'page_type' => 'frontend',
434 'is_front' => true,
435 )
436 );
437 }
438 wp_enqueue_style( 'visualizer-front' );
439
440 // return placeholder div
441 return $actions_div . '<div id="' . $id . '"' . $this->getHtmlAttributes( $attributes ) . '></div>' . $this->addSchema( $chart->ID );
442 }
443
444 /**
445 * Converts the array of attributes to a string of HTML attributes.
446 *
447 * @since ?
448 *
449 * @access private
450 */
451 private function getHtmlAttributes( $attributes ) {
452 $string = '';
453 if ( ! $attributes ) {
454 return $string;
455 }
456
457 foreach ( $attributes as $name => $value ) {
458 $string .= sprintf( '%s="%s"', esc_attr( $name ), esc_attr( $value ) );
459 }
460 return $string;
461 }
462
463 /**
464 * Adds the schema corresponding to the dataset.
465 *
466 * @since 3.3.2
467 *
468 * @access private
469 */
470 private function addSchema( $id ) {
471 // should we show informative errors as HTML comments?
472 $show_errors = apply_filters( 'visualizer_schema_show_errors', true, $id );
473
474 $settings = get_post_meta( $id, Visualizer_Plugin::CF_SETTINGS, true );
475 $title = '';
476 if ( isset( $settings['title'] ) && ! empty( $settings['title'] ) ) {
477 $title = $settings['title'];
478 if ( is_array( $title ) ) {
479 $title = $settings['title']['text'];
480 }
481 }
482 $title = apply_filters( 'visualizer_schema_name', $title, $id );
483 if ( empty( $title ) ) {
484 if ( $show_errors ) {
485 return "<!-- Not showing structured data for chart $id because title is empty -->";
486 }
487 return '';
488 }
489
490 $desc = '';
491 if ( isset( $settings['description'] ) && ! empty( $settings['description'] ) ) {
492 $desc = $settings['description'];
493 }
494 $desc = apply_filters( 'visualizer_schema_description', $desc, $id );
495 if ( empty( $desc ) ) {
496 if ( $show_errors ) {
497 return "<!-- Not showing structured data for chart $id because description is empty -->";
498 }
499 return '';
500 }
501
502 // descriptions below 50 chars are not allowed.
503 if ( strlen( $desc ) < 50 ) {
504 if ( $show_errors ) {
505 return "<!-- Not showing structured data for chart $id because description is shorter than 50 characters -->";
506 }
507 return '';
508 }
509
510 $license = '';
511 if ( isset( $settings['license'] ) && ! empty( $settings['license'] ) ) {
512 $license = $settings['license'];
513 if ( is_array( $license ) ) {
514 $license = $settings['license']['text'];
515 }
516 }
517 $license = apply_filters( 'visualizer_schema_license', $license, $id );
518 if ( empty( $license ) ) {
519 if ( $show_errors ) {
520 return "<!-- Not showing structured data for chart $id because license is empty -->";
521 }
522 return '';
523 }
524
525 $creator = '';
526 if ( isset( $settings['creator'] ) && ! empty( $settings['creator'] ) ) {
527 $creator = $settings['creator'];
528 if ( is_array( $creator ) ) {
529 $creator = $settings['creator']['text'];
530 }
531 }
532 $creator = apply_filters( 'visualizer_schema_creator', $creator, $id );
533 if ( empty( $creator ) ) {
534 if ( $show_errors ) {
535 return "<!-- Not showing structured data for chart $id because creator is empty -->";
536 }
537 return '';
538 }
539
540 $schema = apply_filters(
541 'visualizer_schema',
542 '{
543 "@context":"https://schema.org/",
544 "@type":"Dataset",
545 "name":"' . esc_html( $title ) . '",
546 "description":"' . esc_html( $desc ) . '",
547 "license": "' . esc_html( $license ) . '",
548 "creator": {
549 "@type": "Person",
550 "name": "' . esc_html( $creator ) . '"
551 }
552 }',
553 $id
554 );
555
556 if ( empty( $schema ) ) {
557 return '';
558 }
559
560 return '<script type="application/ld+json">' . $schema . '</script>';
561 }
562
563 /**
564 * Get chart by ID.
565 *
566 * @param string $cache_key Cache key.
567 * @param int $chart_id Chart ID.
568 * @return mixed
569 */
570 private function getChartData( $cache_key = '', $chart_id = 0 ) {
571 if ( ! $chart_id ) {
572 return false;
573 }
574 // Create unique cache key of each chart.
575 $cache_key .= '_' . $chart_id;
576 // Get chart from cache.
577 $chart = get_transient( $cache_key );
578 if ( $chart ) {
579 return $chart;
580 }
581
582 // Get chart by ID.
583 $chart = get_post( $chart_id );
584 if ( $chart && Visualizer_Plugin::CPT_VISUALIZER === $chart->post_type ) {
585 $settings = get_post_meta( $chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
586 $series = get_post_meta( $chart->ID, Visualizer_Plugin::CF_SERIES, true );
587
588 if ( isset( $settings['series'] ) && ! ( count( $settings['series'] ) - count( $series ) > 1 ) ) {
589 $diff_total_series = abs( count( $settings['series'] ) - count( $series ) );
590 if ( $diff_total_series ) {
591 foreach ( range( 1, $diff_total_series ) as $k => $diff_series ) {
592 $settings['series'][] = end( $settings['series'] );
593 }
594 }
595 }
596 $chart_data = array(
597 'chart' => $chart,
598 'type' => get_post_meta( $chart->ID, Visualizer_Plugin::CF_CHART_TYPE, true ),
599 'settings' => $settings,
600 'series' => $series,
601 'chart_image' => get_post_meta( $chart->ID, Visualizer_Plugin::CF_CHART_IMAGE, true ),
602 );
603
604 // Put the results in a transient. Expire after 12 hours.
605 set_transient( $cache_key, $chart_data, apply_filters( Visualizer_Plugin::FILTER_HANDLE_CACHE_EXPIRATION_TIME, 12 * HOUR_IN_SECONDS ) );
606 return $chart_data;
607 }
608
609 return false;
610 }
611 }
612