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

548 lines 17.5 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( 'visualizer_enqueue_scripts', 'enqueueScripts' );
58 $this->_addFilter( 'visualizer_get_language', 'getLanguage' );
59 $this->_addShortcode( 'visualizer', 'renderChart' );
60
61 // add do_shortocde hook for widget_text filter
62 if ( ! has_filter( 'widget_text', 'do_shortcode' ) ) {
63 add_filter( 'widget_text', 'do_shortcode' );
64 }
65
66 // add do_shortcode hook for term_description filter
67 if ( ! has_filter( 'term_description', 'do_shortcode' ) ) {
68 add_filter( 'term_description', 'do_shortcode' );
69 }
70
71 add_action( 'rest_api_init', array( $this, 'endpoint_register' ) );
72
73 $this->_addFilter( 'script_loader_tag', 'script_loader_tag', null, 10, 3 );
74 }
75
76 /**
77 * Adds the async attribute to certain scripts.
78 */
79 function script_loader_tag( $tag, $handle, $src ) {
80 if ( is_admin() ) {
81 return $tag;
82 }
83
84 $scripts = array( 'google-jsapi', 'visualizer-render-google-lib', 'visualizer-render-google' );
85
86 foreach ( $scripts as $async ) {
87 if ( $async === $handle ) {
88 $tag = str_replace( ' src', ' defer="defer" src', $tag );
89 break;
90 }
91 }
92 return $tag;
93 }
94
95 /**
96 * Returns the language/locale.
97 */
98 function getLanguage( $dummy, $only_language ) {
99 return $this->get_language();
100 }
101
102
103 /**
104 * Registers the endpoints
105 */
106 function endpoint_register() {
107 register_rest_route(
108 'visualizer/v' . VISUALIZER_REST_VERSION,
109 '/action/(?P<chart>\d+)/(?P<type>.+)/',
110 array(
111 // POST is required for save/cancel, GET for all others
112 'methods' => array( 'POST', 'GET' ),
113 'args' => array(
114 'chart' => array(
115 'required' => true,
116 'sanitize_callback' => function( $param ) {
117 return is_numeric( $param ) ? $param : null;
118 },
119 ),
120 'type' => array(
121 'required' => true,
122 'type' => 'string',
123 'enum' => array_merge( array( 'save', 'cancel' ), array_keys( $this->get_actions() ) ),
124 ),
125 ),
126 'permission_callback' => function ( WP_REST_Request $request ) {
127 $chart_id = filter_var( sanitize_text_field( $request->get_param( 'chart' ), FILTER_VALIDATE_INT ) );
128 if ( ! empty( $chart_id ) && in_array( $request->get_param( 'type' ), array( 'save', 'cancel' ), true ) ) {
129 // let save and cancel go without any check as past version of pro
130 // did not send the X-WP-Nonce
131 // we can change this at a later date.
132 return true;
133 }
134 return ! empty( $chart_id ) && apply_filters( 'visualizer_pro_show_chart', true, $chart_id );
135 },
136 'callback' => array( $this, 'perform_action' ),
137 )
138 );
139 }
140
141 /**
142 * All possible actions and their labels
143 *
144 * @access private
145 */
146 private function get_actions() {
147 $actions = apply_filters(
148 'visualizer_action_buttons',
149 array(
150 'print' => array(
151 'label' => esc_html__( 'Print', 'visualizer' ),
152 'title' => esc_html__( 'Print Chart', 'visualizer' ),
153 ),
154 'csv' => array(
155 'label' => esc_html__( 'CSV', 'visualizer' ),
156 'title' => esc_html__( 'Download as a CSV', 'visualizer' ),
157 ),
158 'xls' => array(
159 'label' => esc_html__( 'Excel', 'visualizer' ),
160 'title' => esc_html__( 'Download as a spreadsheet', 'visualizer' ),
161 ),
162 'copy' => array(
163 'label' => esc_html__( 'Copy', 'visualizer' ),
164 'title' => esc_html__( 'Copy data', 'visualizer' ),
165 ),
166 'image' => array(
167 'label' => esc_html__( 'Download', 'visualizer' ),
168 'title' => esc_html__( 'Download as an image', 'visualizer' ),
169 ),
170 )
171 );
172
173 // backward compatibility before label and title attributes were introduced.
174 if ( isset( $actions['edit'] ) && is_string( $actions['edit'] ) ) {
175 $actions['edit'] = array(
176 'label' => esc_html__( 'Edit', 'visualizer' ),
177 'title' => esc_html__( 'Edit data', 'visualizer' ),
178 );
179 }
180 return $actions;
181 }
182
183 /**
184 * The print button
185 *
186 * @since 1.0.0
187 *
188 * @access public
189 */
190 public function perform_action( WP_REST_Request $params ) {
191 $chart_id = filter_var( sanitize_text_field( $params['chart'] ), FILTER_VALIDATE_INT );
192 $type = sanitize_text_field( $params['type'] );
193 $data = null;
194
195 if ( ! $chart_id ) {
196 return new WP_REST_Response( array( 'error' => new WP_Error( __( 'Invalid chart ID', 'visualizer' ) ) ) );
197 }
198
199 if ( ! $type ) {
200 return new WP_REST_Response( array( 'error' => new WP_Error( __( 'Invalid action', 'visualizer' ) ) ) );
201 }
202
203 switch ( $type ) {
204 case 'print':
205 $data = $this->_getDataAs( $chart_id, 'print' );
206 break;
207 case 'csv':
208 $data = $this->_getDataAs( $chart_id, 'csv' );
209 break;
210 case 'xls':
211 $data = $this->_getDataAs( $chart_id, 'xls' );
212 break;
213 case 'image':
214 $settings = get_post_meta( $chart_id, Visualizer_Plugin::CF_SETTINGS, true );
215 $title = 'visualizer#' . $chart_id;
216 if ( ! empty( $settings['title'] ) ) {
217 $title = $settings['title'];
218 }
219 // for ChartJS, title is an array.
220 if ( is_array( $title ) && isset( $title['text'] ) ) {
221 $title = $title['text'];
222 }
223 if ( empty( $title ) ) {
224 $title = 'visualizer#' . $chart_id;
225 }
226 $data = array( 'name' => $title );
227 break;
228 default:
229 $data = apply_filters( 'visualizer_action_data', $data, $chart_id, $type, $params, $this );
230 break;
231 }
232
233 return new WP_REST_Response( array( 'data' => $data ) );
234 }
235
236 /**
237 * Registers scripts for charts rendering.
238 *
239 * @since 1.0.0
240 * @uses wp_register_script To register javascript file.
241 *
242 * @access public
243 */
244 public function enqueueScripts() {
245 wp_register_script( 'visualizer-customization', $this->get_user_customization_js(), array(), null, true );
246 wp_register_style( 'visualizer-front', VISUALIZER_ABSURL . 'css/front.css', array(), Visualizer_Plugin::VERSION );
247 do_action( 'visualizer_pro_frontend_load_resources' );
248 }
249
250 /**
251 * Returns placeholder for chart according to visualizer shortcode attributes.
252 *
253 * @since 1.0.0
254 * @uses shortcode_atts() To parse income shortocdes.
255 * @uses apply_filters() To filter chart's data and series arrays.
256 * @uses get_post_meta() To fetch chart's meta information.
257 * @uses wp_enqueue_script() To enqueue charts render script.
258 * @uses wp_localize_script() To add chart data to the page inline script.
259 *
260 * @access public
261 * @param array $atts The array of shortcode attributes.
262 */
263 public function renderChart( $atts ) {
264 global $wp_version;
265 $atts = shortcode_atts(
266 array(
267 // chart id
268 'id' => false,
269 // chart class
270 'class' => false,
271 // for lazy load
272 // set 'yes' to use the default intersection limit (300px)
273 // OR set a number (e.g. 700) to use 700px as the intersection limit
274 'lazy' => apply_filters( 'visualizer_lazy_by_default', false, $atts['id'] ),
275 // Use image chart
276 'use_image' => 'no',
277 ),
278 $atts
279 );
280 // if empty id or chart does not exists, then return empty string
281 if ( ! $atts['id'] || ! ( $chart = get_post( $atts['id'] ) ) || $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ) {
282 return '';
283 }
284
285 // do not show the chart?
286 if ( ! apply_filters( 'visualizer_pro_show_chart', true, $atts['id'] ) ) {
287 return '';
288 }
289
290 // in case revisions exist.
291 // phpcs:ignore WordPress.CodeAnalysis.AssignmentInCondition.Found
292 if ( true === ( $revisions = $this->undoRevisions( $chart->ID, true ) ) ) {
293 $chart = get_post( $chart->ID );
294 }
295
296 $id = 'visualizer-' . $atts['id'];
297 $defaultClass = 'visualizer-front';
298 $class = apply_filters( Visualizer_Plugin::FILTER_CHART_WRAPPER_CLASS, $atts['class'], $atts['id'] );
299
300 $lazyClass = $atts['lazy'] === 'yes' || ctype_digit( $atts['lazy'] ) ? 'visualizer-lazy' : '';
301
302 $class = sprintf( '%s %s %s %s', $defaultClass, $class, 'visualizer-front-' . $atts['id'], $lazyClass );
303 $attributes = array();
304 if ( ! empty( $class ) ) {
305 $attributes['class'] = trim( $class );
306 }
307
308 if ( ctype_digit( $atts['lazy'] ) ) {
309 $attributes['data-lazy-limit'] = $atts['lazy'];
310 }
311
312 $type = get_post_meta( $chart->ID, Visualizer_Plugin::CF_CHART_TYPE, true );
313
314 $chart = apply_filters( 'visualizer_schedule_refresh_chart', $chart, $chart->ID, false );
315
316 // fetch and update settings
317 $settings = get_post_meta( $chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
318 if ( empty( $settings['height'] ) ) {
319 $settings['height'] = '400';
320 }
321
322 // handle series filter hooks
323 $series = apply_filters( Visualizer_Plugin::FILTER_GET_CHART_SERIES, get_post_meta( $chart->ID, Visualizer_Plugin::CF_SERIES, true ), $chart->ID, $type );
324
325 // handle settings filter hooks
326 $settings = apply_filters( Visualizer_Plugin::FILTER_GET_CHART_SETTINGS, $settings, $chart->ID, $type );
327
328 // handle data filter hooks
329 $data = self::get_chart_data( $chart, $type );
330
331 $css = '';
332 $arguments = $this->get_inline_custom_css( $id, $settings );
333 if ( ! empty( $arguments ) ) {
334 $css = $arguments[0];
335 $settings = $arguments[1];
336 }
337
338 $library = $this->load_chart_type( $chart->ID );
339
340 $id = $id . '-' . rand();
341
342 $amp = Visualizer_Plugin::instance()->getModule( Visualizer_Module_AMP::NAME );
343 if ( $amp && $amp->is_amp() ) {
344 return '<div data-amp-auto-lightbox-disable id="' . $id . '"' . $this->getHtmlAttributes( $attributes ) . '>' . $amp->get_chart( $chart, $data, $series, $settings ) . '</div>';
345 }
346
347 if ( 'yes' === $atts['use_image'] ) {
348 $chart_image = get_post_meta( $chart->ID, Visualizer_Plugin::CF_CHART_IMAGE, true );
349 if ( $chart_image ) {
350 return '<div id="' . $id . '"' . $this->getHtmlAttributes( $attributes ) . '>' . wp_get_attachment_image( $chart_image, 'full' ) . '</div>';
351 }
352 }
353
354 // add chart to the array
355 $this->_charts[ $id ] = array(
356 'type' => $type,
357 'series' => $series,
358 'settings' => $settings,
359 'data' => $data,
360 'library' => $library,
361 );
362
363 $actions_div = '';
364 $actions_visible = apply_filters( 'visualizer_pro_add_actions', isset( $settings['actions'] ) ? $settings['actions'] : array(), $atts['id'] );
365 if ( ! empty( $actions_visible ) ) {
366 $actions = $this->get_actions();
367 $actions_div = '<div class="visualizer-actions">';
368 foreach ( $actions_visible as $action_type ) {
369 $key = $action_type;
370 $mime = '';
371 if ( strpos( $action_type, ';' ) !== false ) {
372 $array = explode( ';', $action_type );
373 $key = $array[0];
374 $mime = end( $array );
375 }
376 $label = $actions[ $key ]['label'];
377 $title = $actions[ $key ]['title'];
378 $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 );
379
380 if ( 'copy' === $key ) {
381 $copy = $this->_getDataAs( $atts['id'], 'csv' );
382 $actions_div .= ' data-clipboard-text="' . esc_attr( $copy['csv'] ) . '"';
383 wp_enqueue_script( 'clipboard' );
384 }
385
386 $actions_div .= apply_filters( 'visualizer_action_attributes', '', $key, $atts['id'] );
387 $actions_div .= '>' . $label . '</a> &nbsp;';
388 }
389
390 $actions_div .= '</div>';
391 }
392
393 $actions_div .= $css;
394
395 foreach ( $this->_charts as $id => $array ) {
396 $library = $array['library'];
397 wp_register_script(
398 "visualizer-render-$library",
399 VISUALIZER_ABSURL . 'js/render-facade.js',
400 apply_filters( 'visualizer_assets_render', array( 'jquery', 'visualizer-customization' ), true ),
401 Visualizer_Plugin::VERSION,
402 true
403 );
404
405 wp_enqueue_script( "visualizer-render-$library" );
406 wp_localize_script(
407 "visualizer-render-$library",
408 'visualizer',
409 array(
410 'charts' => $this->_charts,
411 'language' => $this->get_language(),
412 'map_api_key' => get_option( 'visualizer-map-api-key' ),
413 'rest_url' => version_compare( $wp_version, '4.7.0', '>=' ) ? rest_url( 'visualizer/v' . VISUALIZER_REST_VERSION . '/action/#id#/#type#/' ) : '',
414 'wp_nonce' => wp_create_nonce( 'wp_rest' ),
415 'i10n' => array(
416 'copied' => __( 'The data has been copied to your clipboard. Hit Ctrl-V/Cmd-V in your spreadsheet editor to paste the data.', 'visualizer' ),
417 ),
418 'page_type' => 'frontend',
419 'is_front' => true,
420 )
421 );
422 wp_enqueue_style( 'visualizer-front' );
423 }
424
425 // return placeholder div
426 return $actions_div . '<div id="' . $id . '"' . $this->getHtmlAttributes( $attributes ) . '></div>' . $this->addSchema( $chart->ID );
427 }
428
429 /**
430 * Converts the array of attributes to a string of HTML attributes.
431 *
432 * @since ?
433 *
434 * @access private
435 */
436 private function getHtmlAttributes( $attributes ) {
437 $string = '';
438 if ( ! $attributes ) {
439 return $string;
440 }
441
442 foreach ( $attributes as $name => $value ) {
443 $string .= sprintf( '%s="%s"', esc_attr( $name ), esc_attr( $value ) );
444 }
445 return $string;
446 }
447
448 /**
449 * Adds the schema corresponding to the dataset.
450 *
451 * @since 3.3.2
452 *
453 * @access private
454 */
455 private function addSchema( $id ) {
456 // should we show informative errors as HTML comments?
457 $show_errors = apply_filters( 'visualizer_schema_show_errors', true, $id );
458
459 $settings = get_post_meta( $id, Visualizer_Plugin::CF_SETTINGS, true );
460 $title = '';
461 if ( isset( $settings['title'] ) && ! empty( $settings['title'] ) ) {
462 $title = $settings['title'];
463 if ( is_array( $title ) ) {
464 $title = $settings['title']['text'];
465 }
466 }
467 $title = apply_filters( 'visualizer_schema_name', $title, $id );
468 if ( empty( $title ) ) {
469 if ( $show_errors ) {
470 return "<!-- Not showing structured data for chart $id because title is empty -->";
471 }
472 return '';
473 }
474
475 $desc = '';
476 if ( isset( $settings['description'] ) && ! empty( $settings['description'] ) ) {
477 $desc = $settings['description'];
478 }
479 $desc = apply_filters( 'visualizer_schema_description', $desc, $id );
480 if ( empty( $desc ) ) {
481 if ( $show_errors ) {
482 return "<!-- Not showing structured data for chart $id because description is empty -->";
483 }
484 return '';
485 }
486
487 // descriptions below 50 chars are not allowed.
488 if ( strlen( $desc ) < 50 ) {
489 if ( $show_errors ) {
490 return "<!-- Not showing structured data for chart $id because description is shorter than 50 characters -->";
491 }
492 return '';
493 }
494
495 $license = '';
496 if ( isset( $settings['license'] ) && ! empty( $settings['license'] ) ) {
497 $license = $settings['license'];
498 if ( is_array( $license ) ) {
499 $license = $settings['license']['text'];
500 }
501 }
502 $license = apply_filters( 'visualizer_schema_license', $license, $id );
503 if ( empty( $license ) ) {
504 if ( $show_errors ) {
505 return "<!-- Not showing structured data for chart $id because license is empty -->";
506 }
507 return '';
508 }
509
510 $creator = '';
511 if ( isset( $settings['creator'] ) && ! empty( $settings['creator'] ) ) {
512 $creator = $settings['creator'];
513 if ( is_array( $creator ) ) {
514 $creator = $settings['creator']['text'];
515 }
516 }
517 $creator = apply_filters( 'visualizer_schema_creator', $creator, $id );
518 if ( empty( $creator ) ) {
519 if ( $show_errors ) {
520 return "<!-- Not showing structured data for chart $id because creator is empty -->";
521 }
522 return '';
523 }
524
525 $schema = apply_filters(
526 'visualizer_schema',
527 '{
528 "@context":"https://schema.org/",
529 "@type":"Dataset",
530 "name":"' . esc_html( $title ) . '",
531 "description":"' . esc_html( $desc ) . '",
532 "license": "' . esc_html( $license ) . '",
533 "creator": {
534 "@type": "Person",
535 "name": "' . esc_html( $creator ) . '"
536 }
537 }',
538 $id
539 );
540
541 if ( empty( $schema ) ) {
542 return '';
543 }
544
545 return '<script type="application/ld+json">' . $schema . '</script>';
546 }
547 }
548