PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.9.6
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.9.6
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.9.6, at classes/Visualizer/Module/Frontend.php

745 lines 24.7 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 * Lazy render script.
46 *
47 * @access private
48 * @var bool
49 */
50 private $lazy_render_script = false;
51
52 /**
53 * Constructor.
54 *
55 * @since 1.0.0
56 * @uses add_filter() To add "do_shortcode" hook for "widget_text" and "term_description" filters.
57 *
58 * @access public
59 * @param Visualizer_Plugin $plugin The instance of the plugin.
60 */
61 public function __construct( Visualizer_Plugin $plugin ) {
62 parent::__construct( $plugin );
63
64 $this->_addAction( 'wp_print_footer_scripts', 'printFooterScripts' );
65 $this->_addAction( 'wp_enqueue_scripts', 'enqueueScripts' );
66 $this->_addAction( 'load-index.php', 'enqueueScripts' );
67 $this->_addAction( 'visualizer_enqueue_scripts', 'enqueueScripts' );
68 $this->_addFilter( 'visualizer_get_language', 'getLanguage' );
69 $this->_addShortcode( 'visualizer', 'renderChart' );
70
71 // add do_shortocde hook for widget_text filter
72 if ( ! has_filter( 'widget_text', 'do_shortcode' ) ) {
73 add_filter( 'widget_text', 'do_shortcode' );
74 }
75
76 // add do_shortcode hook for term_description filter
77 if ( ! has_filter( 'term_description', 'do_shortcode' ) ) {
78 add_filter( 'term_description', 'do_shortcode' );
79 }
80
81 add_action( 'rest_api_init', array( $this, 'endpoint_register' ) );
82
83 $this->_addFilter( 'script_loader_tag', 'script_loader_tag', null, 10, 3 );
84 }
85
86 /**
87 * Adds the async attribute to certain scripts.
88 */
89 function script_loader_tag( $tag, $handle, $src ) {
90 if ( is_admin() ) {
91 return $tag;
92 }
93 // Async scripts.
94 $async_scripts = array( 'google-jsapi', 'chartjs', 'visualizer-datatables' );
95 foreach ( $async_scripts as $async ) {
96 if ( $async === $handle ) {
97 $tag = str_replace( ' src', ' async src', $tag );
98 break;
99 }
100 };
101
102 // Async scripts.
103 $scripts = array( 'dom-to-image' );
104 foreach ( array( 'async', 'defer' ) as $attr ) {
105 if ( wp_scripts()->get_data( $handle, $attr ) ) {
106 break;
107 }
108 if ( in_array( $handle, $async_scripts, true ) || false === strpos( $handle, 'visualizer-' ) ) {
109 break;
110 }
111 if ( ! preg_match( ":\s$attr(=|>|\s):", $tag ) ) {
112 $tag = preg_replace( ':(?=></script>):', " $attr", $tag, 1 );
113 if ( $this->lazy_render_script ) {
114 $tag = str_replace( ' src', ' data-visualizer-script', $tag );
115 }
116 }
117 // Only allow async or defer, not both.
118 break;
119 }
120 return $tag;
121 }
122
123 /**
124 * Returns the language/locale.
125 */
126 function getLanguage( $dummy, $only_language ) {
127 return $this->get_language();
128 }
129
130
131 /**
132 * Registers the endpoints
133 */
134 function endpoint_register() {
135 register_rest_route(
136 'visualizer/v' . VISUALIZER_REST_VERSION,
137 '/action/(?P<chart>\d+)/(?P<type>.+)/',
138 array(
139 // POST is required for save/cancel, GET for all others
140 'methods' => array( 'POST', 'GET' ),
141 'args' => array(
142 'chart' => array(
143 'required' => true,
144 'sanitize_callback' => function( $param ) {
145 return is_numeric( $param ) ? $param : null;
146 },
147 ),
148 'type' => array(
149 'required' => true,
150 'type' => 'string',
151 'enum' => array_merge( array( 'save', 'cancel' ), array_keys( $this->get_actions() ) ),
152 ),
153 ),
154 'permission_callback' => function ( WP_REST_Request $request ) {
155 $chart_id = filter_var( sanitize_text_field( $request->get_param( 'chart' ), FILTER_VALIDATE_INT ) );
156 if ( ! empty( $chart_id ) && in_array( $request->get_param( 'type' ), array( 'save', 'cancel' ), true ) ) {
157 // let save and cancel go without any check as past version of pro
158 // did not send the X-WP-Nonce
159 // we can change this at a later date.
160 return true;
161 }
162 return ! empty( $chart_id ) && apply_filters( 'visualizer_pro_show_chart', true, $chart_id );
163 },
164 'callback' => array( $this, 'perform_action' ),
165 )
166 );
167 }
168
169 /**
170 * All possible actions and their labels
171 *
172 * @access private
173 */
174 private function get_actions() {
175 $actions = apply_filters(
176 'visualizer_action_buttons',
177 array(
178 'print' => array(
179 'label' => esc_html__( 'Print', 'visualizer' ),
180 'title' => esc_html__( 'Print Chart', 'visualizer' ),
181 ),
182 'csv' => array(
183 'label' => esc_html__( 'CSV', 'visualizer' ),
184 'title' => esc_html__( 'Download as a CSV', 'visualizer' ),
185 ),
186 'xls' => array(
187 'label' => esc_html__( 'Excel', 'visualizer' ),
188 'title' => esc_html__( 'Download as a spreadsheet', 'visualizer' ),
189 ),
190 'copy' => array(
191 'label' => esc_html__( 'Copy', 'visualizer' ),
192 'title' => esc_html__( 'Copy data', 'visualizer' ),
193 ),
194 'image' => array(
195 'label' => esc_html__( 'Download', 'visualizer' ),
196 'title' => esc_html__( 'Download as an image', 'visualizer' ),
197 ),
198 )
199 );
200
201 // backward compatibility before label and title attributes were introduced.
202 if ( isset( $actions['edit'] ) && is_string( $actions['edit'] ) ) {
203 $actions['edit'] = array(
204 'label' => esc_html__( 'Edit', 'visualizer' ),
205 'title' => esc_html__( 'Edit data', 'visualizer' ),
206 );
207 }
208 return $actions;
209 }
210
211 /**
212 * The print button
213 *
214 * @since 1.0.0
215 *
216 * @access public
217 */
218 public function perform_action( WP_REST_Request $params ) {
219 $chart_id = filter_var( sanitize_text_field( $params['chart'] ), FILTER_VALIDATE_INT );
220 $type = sanitize_text_field( $params['type'] );
221 $data = null;
222
223 if ( ! $chart_id ) {
224 return new WP_REST_Response( array( 'error' => new WP_Error( __( 'Invalid chart ID', 'visualizer' ) ) ) );
225 }
226
227 if ( ! $type ) {
228 return new WP_REST_Response( array( 'error' => new WP_Error( __( 'Invalid action', 'visualizer' ) ) ) );
229 }
230
231 switch ( $type ) {
232 case 'print':
233 $data = $this->_getDataAs( $chart_id, 'print' );
234 break;
235 case 'csv':
236 $data = $this->_getDataAs( $chart_id, 'csv' );
237 break;
238 case 'xls':
239 $data = $this->_getDataAs( $chart_id, 'xls' );
240 break;
241 case 'image':
242 $settings = get_post_meta( $chart_id, Visualizer_Plugin::CF_SETTINGS, true );
243 $title = 'visualizer#' . $chart_id;
244 if ( ! empty( $settings['title'] ) ) {
245 $title = $settings['title'];
246 }
247 // for ChartJS, title is an array.
248 if ( is_array( $title ) && isset( $title['text'] ) ) {
249 $title = $title['text'];
250 }
251 if ( empty( $title ) ) {
252 $title = 'visualizer#' . $chart_id;
253 }
254 $data = array( 'name' => $title );
255 break;
256 default:
257 $data = apply_filters( 'visualizer_action_data', $data, $chart_id, $type, $params, $this );
258 break;
259 }
260
261 return new WP_REST_Response( array( 'data' => $data ) );
262 }
263
264 /**
265 * Registers scripts for charts rendering.
266 *
267 * @since 1.0.0
268 * @uses wp_register_script To register javascript file.
269 *
270 * @access public
271 */
272 public function enqueueScripts() {
273 wp_register_script( 'visualizer-customization', $this->get_user_customization_js(), array(), null, true );
274 do_action( 'visualizer_pro_frontend_load_resources' );
275 }
276
277 /**
278 * Returns placeholder for chart according to visualizer shortcode attributes.
279 *
280 * @since 1.0.0
281 * @uses shortcode_atts() To parse income shortocdes.
282 * @uses apply_filters() To filter chart's data and series arrays.
283 * @uses get_post_meta() To fetch chart's meta information.
284 * @uses wp_enqueue_script() To enqueue charts render script.
285 * @uses wp_localize_script() To add chart data to the page inline script.
286 *
287 * @access public
288 * @param array $atts The array of shortcode attributes.
289 */
290 public function renderChart( $atts ) {
291 global $wp_version;
292 $atts = shortcode_atts(
293 array(
294 // chart id
295 'id' => false,
296 // chart class
297 'class' => false,
298 // for lazy load
299 // set 'yes' to use the default intersection limit (300px)
300 // OR set a number (e.g. 700) to use 700px as the intersection limit
301 'lazy' => apply_filters( 'visualizer_lazy_by_default', false, $atts['id'] ),
302 // Use image chart
303 'use_image' => 'no',
304 ),
305 $atts
306 );
307
308 $atts['id'] = (int) $atts['id'];
309 $atts['class'] = esc_attr( $atts['class'] );
310 $atts['lazy'] = esc_attr( $atts['lazy'] );
311 $atts['use_image'] = esc_attr( $atts['use_image'] );
312
313 global $sitepress;
314 if ( Visualizer_Module::is_pro() && ( function_exists( 'icl_get_languages' ) && $sitepress instanceof \SitePress ) ) {
315 global $sitepress;
316 $locale = icl_get_current_language();
317 $locale = strtolower( str_replace( '_', '-', $locale ) );
318 $trid = $sitepress->get_element_trid( $atts['id'], 'post_' . Visualizer_Plugin::CPT_VISUALIZER );
319 $translations = $sitepress->get_element_translations( $trid );
320 if ( isset( $translations[ $locale ] ) && is_object( $translations[ $locale ] ) ) {
321 $atts['id'] = $translations[ $locale ]->element_id;
322 }
323 }
324
325 $chart_data = $this->getChartData( Visualizer_Plugin::CF_CHART_CACHE, $atts['id'] );
326 // if empty chart does not exists, then return empty string.
327 if ( ! $chart_data ) {
328 return '';
329 }
330
331 $chart = $chart_data['chart'];
332 $type = $chart_data['type'];
333 $series = $chart_data['series'];
334 // do not show the chart?
335 if ( ! apply_filters( 'visualizer_pro_show_chart', true, $atts['id'] ) ) {
336 return '';
337 }
338
339 if ( ! is_admin() && ! empty( $chart_data['is_woocommerce_report'] ) ) {
340 return '';
341 }
342
343 // in case revisions exist.
344 // phpcs:ignore WordPress.CodeAnalysis.AssignmentInCondition.Found
345 if ( true === ( $revisions = $this->undoRevisions( $chart->ID, true ) ) ) {
346 $chart = get_post( $chart->ID );
347 }
348
349 $id = 'visualizer-' . $atts['id'];
350 $defaultClass = 'visualizer-front';
351 $class = apply_filters( Visualizer_Plugin::FILTER_CHART_WRAPPER_CLASS, $atts['class'], $atts['id'] );
352
353 $lazyClass = $atts['lazy'] === 'yes' || ctype_digit( $atts['lazy'] ) ? 'visualizer-lazy' : '';
354
355 $class = sprintf( '%s %s %s %s', $defaultClass, $class, 'visualizer-front-' . $atts['id'], $lazyClass );
356 $attributes = array();
357 if ( ! empty( $class ) ) {
358 $attributes['class'] = trim( $class );
359 }
360
361 if ( ctype_digit( $atts['lazy'] ) ) {
362 $attributes['data-lazy-limit'] = $atts['lazy'];
363 }
364
365 $attributes = apply_filters( 'visualizer_container_attributes', $attributes, $chart->ID );
366
367 $chart = apply_filters( 'visualizer_schedule_refresh_chart', $chart, $chart->ID, false );
368
369 // Get and update settings.
370 $settings = $chart_data['settings'];
371 if ( empty( $settings['height'] ) ) {
372 $settings['height'] = '400';
373 }
374
375 // handle series filter hooks
376 $series = apply_filters( Visualizer_Plugin::FILTER_GET_CHART_SERIES, $series, $chart->ID, $type );
377
378 // handle settings filter hooks
379 $settings = apply_filters( Visualizer_Plugin::FILTER_GET_CHART_SETTINGS, $settings, $chart->ID, $type );
380
381 $lazy_load = isset( $settings['lazy_load_chart'] ) ? $settings['lazy_load_chart'] : false;
382 $lazy_load = apply_filters( 'visualizer_lazy_load_chart', $lazy_load, $chart->ID );
383 $container_class = 'visualizer-front-container';
384 if ( $lazy_load ) {
385 $this->lazy_render_script = true;
386 $container_class .= ' visualizer-lazy-render';
387 }
388
389 // handle data filter hooks
390 $data = self::get_chart_data( $chart, $type );
391
392 $css = '';
393 $arguments = $this->get_inline_custom_css( $id, $settings );
394 if ( ! empty( $arguments ) ) {
395 $css = $arguments[0];
396 $settings = $arguments[1];
397 }
398
399 $library = $this->load_chart_type( $chart->ID );
400
401 $id = $id . '-' . rand();
402
403 $amp = Visualizer_Plugin::instance()->getModule( Visualizer_Module_AMP::NAME );
404 if ( $amp && $amp->is_amp() ) {
405 return '<div data-amp-auto-lightbox-disable id="' . $id . '"' . $this->getHtmlAttributes( $attributes ) . '>' . $amp->get_chart( $chart, $data, $series, $settings ) . '</div>';
406 }
407
408 if ( 'yes' === $atts['use_image'] ) {
409 $chart_image = $chart_data['chart_image'];
410 if ( $chart_image ) {
411 return '<div id="' . $id . '"' . $this->getHtmlAttributes( $attributes ) . '>' . wp_get_attachment_image( $chart_image, 'full' ) . '</div>';
412 }
413 }
414 // Unset chart image base64 string.
415 if ( isset( $settings['chart-img'] ) ) {
416 unset( $settings['chart-img'] );
417 }
418
419 $enable_controls = false;
420 if ( isset( $settings['controls'] ) && ! empty( $settings['controls']['controlType'] ) ) {
421 $column_index = $settings['controls']['filterColumnIndex'];
422 $column_label = $settings['controls']['filterColumnLabel'];
423 if ( 'false' !== $column_index || 'false' !== $column_label ) {
424 $enable_controls = true;
425 }
426 }
427
428 if ( ! $enable_controls ) {
429 unset( $settings['controls'] );
430 }
431
432 // add chart to the array
433 $this->_charts[ $id ] = array(
434 'type' => $type,
435 'series' => $series,
436 'settings' => $settings,
437 'data' => $data,
438 'library' => $library,
439 );
440
441 $actions_div = '';
442 $actions_visible = apply_filters( 'visualizer_pro_add_actions', isset( $settings['actions'] ) ? $settings['actions'] : array(), $atts['id'] );
443 if ( ! empty( $actions_visible ) ) {
444 $actions = $this->get_actions();
445 $actions_div = '<div class="visualizer-actions">';
446 foreach ( $actions_visible as $action_type ) {
447 $key = $action_type;
448 $mime = '';
449 if ( strpos( $action_type, ';' ) !== false ) {
450 $array = explode( ';', $action_type );
451 $key = $array[0];
452 $mime = end( $array );
453 }
454 $label = $actions[ $key ]['label'];
455 $title = $actions[ $key ]['title'];
456 $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 );
457
458 if ( 'copy' === $key ) {
459 $copy = $this->_getDataAs( $atts['id'], 'csv' );
460 $actions_div .= ' data-clipboard-text="' . esc_attr( $copy['csv'] ) . '"';
461 wp_enqueue_script( 'clipboard' );
462 }
463
464 $actions_div .= apply_filters( 'visualizer_action_attributes', '', $key, $atts['id'] );
465 $actions_div .= '>' . $label . '</a> &nbsp;';
466 }
467
468 $actions_div .= '</div>';
469 }
470
471 $actions_div .= $css;
472
473 $_charts = array();
474 $_charts_type = '';
475 $count = 0;
476 foreach ( $this->_charts as $id => $array ) {
477 $_charts = $this->_charts;
478 $library = $array['library'];
479 $_charts_type = $library;
480 if ( ! wp_script_is( "visualizer-render-$library", 'registered' ) ) {
481 wp_register_script(
482 "visualizer-render-$library",
483 VISUALIZER_ABSURL . 'js/render-facade.js',
484 apply_filters( 'visualizer_assets_render', array( 'jquery', 'visualizer-customization' ), true ),
485 Visualizer_Plugin::VERSION,
486 true
487 );
488 wp_enqueue_script( "visualizer-render-$library" );
489 }
490
491 if ( wp_script_is( "visualizer-render-$_charts_type" ) && 0 === $count ) {
492 wp_localize_script(
493 "visualizer-render-$_charts_type",
494 'visualizer',
495 array(
496 'charts' => $this->_charts,
497 'language' => $this->get_language(),
498 'map_api_key' => get_option( 'visualizer-map-api-key' ),
499 'rest_url' => version_compare( $wp_version, '4.7.0', '>=' ) ? rest_url( 'visualizer/v' . VISUALIZER_REST_VERSION . '/action/#id#/#type#/' ) : '',
500 'wp_nonce' => wp_create_nonce( 'wp_rest' ),
501 'i10n' => array(
502 'copied' => __( 'The data has been copied to your clipboard. Hit Ctrl-V/Cmd-V in your spreadsheet editor to paste the data.', 'visualizer' ),
503 ),
504 'page_type' => 'frontend',
505 'is_front' => true,
506 )
507 );
508 }
509 $count++;
510 }
511 $prefix = 'C' . 'h' . 'a' . 'rt';
512 if ( $type === 'tabular' ) {
513 $prefix = 'T' . 'a' . 'bl' . 'e';
514 }
515 if ( Visualizer_Module::is_pro() && $enable_controls ) {
516 $actions_div .= '<div id="control_wrapper_' . $id . '"></div>';
517 }
518 // return placeholder div
519 return '<div class="' . $container_class . '" id="chart_wrapper_' . $id . '">' . $actions_div . '<div id="' . $id . '"' . $this->getHtmlAttributes( $attributes ) . '></div>' . $this->addSchema( $chart->ID ) . ( ! Visualizer_Module::is_pro() ? ( '<' . 'di' . 'v st' . 'yl' . 'e="' . 'op' . 'a' . 'ci' . 't' . 'y:' . '0' . '.7' . ';t' . 'ex' . 't-a' . 'li' . 'gn:' . 'ri' . 'gh' . 't;b' . 'o' . 'tto' . 'm: 1' . '0px; z-i' . 'nd' . 'ex:1' . '00' . '0; ' . 'le' . 'ft' . ':2' . '0px' . '; fo' . 'nt-si' . 'ze: 1' . '4px">' . $prefix . ' b' . 'y' . ' <a ' . 'h' . 're' . 'f="ht' . 'tp' . 's:/' . '/t' . 'he' . 'me' . 'i' . 'sl' . 'e' . '.c' . 'om' . '/p' . 'lu' . 'gi' . 'ns' . '/v' . 'i' . 'su' . 'al' . 'iz' . 'er' . '-c' . 'ha' . 'rts' . '-a' . 'nd' . '-gr' . 'ap' . 'hs' . '/" t' . 'arg' . 'et="' . '_bl' . 'an' . 'k" re' . 'l=' . '"no' . 'fol' . 'l' . 'ow"' . '>V' . 'is' . 'u' . 'a' . 'l' . 'i' . 'z' . 'e' . 'r' . '</' . 'a' . '>' . '<' . '/' . 'd' . 'i' . 'v' . '>' ) : '' ) . '</div>';
520 }
521
522 /**
523 * Converts the array of attributes to a string of HTML attributes.
524 *
525 * @since ?
526 *
527 * @access private
528 */
529 private function getHtmlAttributes( $attributes ) {
530 $string = '';
531 if ( ! $attributes ) {
532 return $string;
533 }
534
535 foreach ( $attributes as $name => $value ) {
536 $string .= sprintf( ' %s="%s"', esc_attr( $name ), esc_attr( $value ) );
537 }
538 return $string;
539 }
540
541 /**
542 * Adds the schema corresponding to the dataset.
543 *
544 * @since 3.3.2
545 *
546 * @access private
547 */
548 private function addSchema( $id ) {
549 // should we show informative errors as HTML comments?
550 $show_errors = apply_filters( 'visualizer_schema_show_errors', true, $id );
551
552 $settings = get_post_meta( $id, Visualizer_Plugin::CF_SETTINGS, true );
553 $title = '';
554 if ( isset( $settings['title'] ) && ! empty( $settings['title'] ) ) {
555 $title = $settings['title'];
556 if ( is_array( $title ) ) {
557 $title = $settings['title']['text'];
558 }
559 }
560 $title = apply_filters( 'visualizer_schema_name', $title, $id );
561 if ( empty( $title ) ) {
562 if ( $show_errors ) {
563 return "<!-- Not showing structured data for chart $id because title is empty -->";
564 }
565 return '';
566 }
567
568 $desc = '';
569 if ( isset( $settings['description'] ) && ! empty( $settings['description'] ) ) {
570 $desc = $settings['description'];
571 }
572 $desc = apply_filters( 'visualizer_schema_description', $desc, $id );
573 if ( empty( $desc ) ) {
574 if ( $show_errors ) {
575 return "<!-- Not showing structured data for chart $id because description is empty -->";
576 }
577 return '';
578 }
579
580 // descriptions below 50 chars are not allowed.
581 if ( strlen( $desc ) < 50 ) {
582 if ( $show_errors ) {
583 return "<!-- Not showing structured data for chart $id because description is shorter than 50 characters -->";
584 }
585 return '';
586 }
587
588 $license = '';
589 if ( isset( $settings['license'] ) && ! empty( $settings['license'] ) ) {
590 $license = $settings['license'];
591 if ( is_array( $license ) ) {
592 $license = $settings['license']['text'];
593 }
594 }
595 $license = apply_filters( 'visualizer_schema_license', $license, $id );
596 if ( empty( $license ) ) {
597 if ( $show_errors ) {
598 return "<!-- Not showing structured data for chart $id because license is empty -->";
599 }
600 return '';
601 }
602
603 $creator = '';
604 if ( isset( $settings['creator'] ) && ! empty( $settings['creator'] ) ) {
605 $creator = $settings['creator'];
606 if ( is_array( $creator ) ) {
607 $creator = $settings['creator']['text'];
608 }
609 }
610 $creator = apply_filters( 'visualizer_schema_creator', $creator, $id );
611 if ( empty( $creator ) ) {
612 if ( $show_errors ) {
613 return "<!-- Not showing structured data for chart $id because creator is empty -->";
614 }
615 return '';
616 }
617
618 $schema = apply_filters(
619 'visualizer_schema',
620 '{
621 "@context":"https://schema.org/",
622 "@type":"Dataset",
623 "name":"' . esc_html( $title ) . '",
624 "description":"' . esc_html( $desc ) . '",
625 "license": "' . esc_html( $license ) . '",
626 "creator": {
627 "@type": "Person",
628 "name": "' . esc_html( $creator ) . '"
629 }
630 }',
631 $id
632 );
633
634 if ( empty( $schema ) ) {
635 return '';
636 }
637
638 return '<script type="application/ld+json">' . $schema . '</script>';
639 }
640
641 /**
642 * Get chart by ID.
643 *
644 * @param string $cache_key Cache key.
645 * @param int $chart_id Chart ID.
646 * @return mixed
647 */
648 private function getChartData( $cache_key = '', $chart_id = 0 ) {
649 if ( ! $chart_id ) {
650 return false;
651 }
652 // Create unique cache key of each chart.
653 $cache_key .= '_' . $chart_id;
654 // Get chart from cache.
655 $chart = get_transient( $cache_key );
656 if ( $chart ) {
657 return $chart;
658 }
659
660 // Get chart by ID.
661 $chart = get_post( $chart_id );
662 if ( $chart && Visualizer_Plugin::CPT_VISUALIZER === $chart->post_type ) {
663 $settings = get_post_meta( $chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
664 $series = get_post_meta( $chart->ID, Visualizer_Plugin::CF_SERIES, true );
665 $is_woocommerce_report = get_post_meta( $chart->ID, Visualizer_Plugin::CF_IS_WOOCOMMERCE_SOURCE, true );
666
667 if ( isset( $settings['series'] ) && ! ( count( $settings['series'] ) - count( $series ) > 1 ) ) {
668 $diff_total_series = abs( count( $settings['series'] ) - count( $series ) );
669 if ( $diff_total_series ) {
670 foreach ( range( 1, $diff_total_series ) as $k => $diff_series ) {
671 $settings['series'][] = end( $settings['series'] );
672 }
673 }
674 }
675 $chart_data = array(
676 'chart' => $chart,
677 'type' => get_post_meta( $chart->ID, Visualizer_Plugin::CF_CHART_TYPE, true ),
678 'settings' => $settings,
679 'series' => $series,
680 'chart_image' => get_post_meta( $chart->ID, Visualizer_Plugin::CF_CHART_IMAGE, true ),
681 'is_woocommerce_report' => $is_woocommerce_report,
682 );
683
684 // Put the results in a transient. Expire after 12 hours.
685 set_transient( $cache_key, $chart_data, apply_filters( Visualizer_Plugin::FILTER_HANDLE_CACHE_EXPIRATION_TIME, 12 * HOUR_IN_SECONDS ) );
686 return $chart_data;
687 }
688
689 return false;
690 }
691
692 /**
693 * Print footer script.
694 */
695 public function printFooterScripts() {
696 if ( $this->lazy_render_script ) {
697 ?>
698 <script type="text/javascript">
699 var visualizerUserInteractionEvents = [
700 "mouseover",
701 "keydown",
702 "touchmove",
703 "touchstart"
704 ];
705
706 visualizerUserInteractionEvents.forEach(function(event) {
707 window.addEventListener(event, visualizerTriggerScriptLoader, { passive: true });
708 });
709
710 function visualizerTriggerScriptLoader() {
711 visualizerLoadScripts();
712 visualizerUserInteractionEvents.forEach(function(event) {
713 window.removeEventListener(event, visualizerTriggerScriptLoader, { passive: true });
714 });
715 }
716
717 function visualizerLoadScripts() {
718 document.querySelectorAll("script[data-visualizer-script]").forEach(function(elem) {
719 jQuery.getScript( elem.getAttribute("data-visualizer-script") )
720 .done( function( script, textStatus ) {
721 elem.setAttribute("src", elem.getAttribute("data-visualizer-script"));
722 elem.removeAttribute("data-visualizer-script");
723 setTimeout( function() {
724 visualizerRefreshChart();
725 } );
726 } );
727 });
728 }
729
730 function visualizerRefreshChart() {
731 jQuery( '.visualizer-front:not(.visualizer-chart-loaded)' ).resize();
732 if ( jQuery( 'div.viz-facade-loaded:not(.visualizer-lazy):empty' ).length > 0 ) {
733 visualizerUserInteractionEvents.forEach( function( event ) {
734 window.addEventListener( event, function() {
735 jQuery( '.visualizer-front:not(.visualizer-chart-loaded)' ).resize();
736 }, { passive: true } );
737 } );
738 }
739 }
740 </script>
741 <?php
742 }
743 }
744 }
745