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

505 lines 16.1 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 ),
276 $atts
277 );
278
279 // if empty id or chart does not exists, then return empty string
280 if ( ! $atts['id'] || ! ( $chart = get_post( $atts['id'] ) ) || $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ) {
281 return '';
282 }
283
284 // do not show the chart?
285 if ( ! apply_filters( 'visualizer_pro_show_chart', true, $atts['id'] ) ) {
286 return '';
287 }
288
289 // in case revisions exist.
290 // phpcs:ignore WordPress.CodeAnalysis.AssignmentInCondition.Found
291 if ( true === ( $revisions = $this->undoRevisions( $chart->ID, true ) ) ) {
292 $chart = get_post( $chart->ID );
293 }
294
295 $id = 'visualizer-' . $atts['id'];
296 $defaultClass = 'visualizer-front';
297 $class = apply_filters( Visualizer_Plugin::FILTER_CHART_WRAPPER_CLASS, $atts['class'], $atts['id'] );
298
299 $lazyClass = $atts['lazy'] === 'yes' || ctype_digit( $atts['lazy'] ) ? 'visualizer-lazy' : '';
300
301 $class = sprintf( '%s %s %s %s', $defaultClass, $class, 'visualizer-front-' . $atts['id'], $lazyClass );
302 $attributes = array();
303 if ( ! empty( $class ) ) {
304 $attributes['class'] = trim( $class );
305 }
306
307 if ( ctype_digit( $atts['lazy'] ) ) {
308 $attributes['data-lazy-limit'] = $atts['lazy'];
309 }
310
311 $type = get_post_meta( $chart->ID, Visualizer_Plugin::CF_CHART_TYPE, true );
312
313 $chart = apply_filters( 'visualizer_schedule_refresh_chart', $chart, $chart->ID, false );
314
315 // fetch and update settings
316 $settings = get_post_meta( $chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
317 if ( empty( $settings['height'] ) ) {
318 $settings['height'] = '400';
319 }
320
321 // handle series filter hooks
322 $series = apply_filters( Visualizer_Plugin::FILTER_GET_CHART_SERIES, get_post_meta( $chart->ID, Visualizer_Plugin::CF_SERIES, true ), $chart->ID, $type );
323
324 // handle settings filter hooks
325 $settings = apply_filters( Visualizer_Plugin::FILTER_GET_CHART_SETTINGS, $settings, $chart->ID, $type );
326
327 // handle data filter hooks
328 $data = self::get_chart_data( $chart, $type );
329
330 $css = '';
331 $arguments = $this->get_inline_custom_css( $id, $settings );
332 if ( ! empty( $arguments ) ) {
333 $css = $arguments[0];
334 $settings = $arguments[1];
335 }
336
337 $library = $this->load_chart_type( $chart->ID );
338
339 $id = $id . '-' . rand();
340
341 $amp = Visualizer_Plugin::instance()->getModule( Visualizer_Module_AMP::NAME );
342 if ( $amp && $amp->is_amp() ) {
343 return '<div id="' . $id . '"' . $this->getHtmlAttributes( $attributes ) . '>' . $amp->get_chart( $chart, $data, $series, $settings ) . '</div>';
344 }
345
346 // add chart to the array
347 $this->_charts[ $id ] = array(
348 'type' => $type,
349 'series' => $series,
350 'settings' => $settings,
351 'data' => $data,
352 'library' => $library,
353 );
354
355 $actions_div = '';
356 $actions_visible = apply_filters( 'visualizer_pro_add_actions', isset( $settings['actions'] ) ? $settings['actions'] : array(), $atts['id'] );
357 if ( ! empty( $actions_visible ) ) {
358 $actions = $this->get_actions();
359 $actions_div = '<div class="visualizer-actions">';
360 foreach ( $actions_visible as $action_type ) {
361 $key = $action_type;
362 $mime = '';
363 if ( strpos( $action_type, ';' ) !== false ) {
364 $array = explode( ';', $action_type );
365 $key = $array[0];
366 $mime = end( $array );
367 }
368 $label = $actions[ $key ]['label'];
369 $title = $actions[ $key ]['title'];
370 $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 );
371
372 if ( 'copy' === $key ) {
373 $copy = $this->_getDataAs( $atts['id'], 'csv' );
374 $actions_div .= ' data-clipboard-text="' . esc_attr( $copy['csv'] ) . '"';
375 wp_enqueue_script( 'clipboard' );
376 }
377
378 $actions_div .= apply_filters( 'visualizer_action_attributes', '', $key, $atts['id'] );
379 $actions_div .= '>' . $label . '</a> &nbsp;';
380 }
381
382 $actions_div .= '</div>';
383 }
384
385 $actions_div .= $css;
386
387 foreach ( $this->_charts as $id => $array ) {
388 $library = $array['library'];
389 wp_register_script(
390 "visualizer-render-$library",
391 VISUALIZER_ABSURL . 'js/render-facade.js',
392 apply_filters( 'visualizer_assets_render', array( 'jquery', 'visualizer-customization' ), true ),
393 Visualizer_Plugin::VERSION,
394 true
395 );
396
397 wp_enqueue_script( "visualizer-render-$library" );
398 wp_localize_script(
399 "visualizer-render-$library",
400 'visualizer',
401 array(
402 'charts' => $this->_charts,
403 'language' => $this->get_language(),
404 'map_api_key' => get_option( 'visualizer-map-api-key' ),
405 'rest_url' => version_compare( $wp_version, '4.7.0', '>=' ) ? rest_url( 'visualizer/v' . VISUALIZER_REST_VERSION . '/action/#id#/#type#/' ) : '',
406 'wp_nonce' => wp_create_nonce( 'wp_rest' ),
407 'i10n' => array(
408 'copied' => __( 'The data has been copied to your clipboard. Hit Ctrl-V/Cmd-V in your spreadsheet editor to paste the data.', 'visualizer' ),
409 ),
410 'page_type' => 'frontend',
411 'is_front' => true,
412 )
413 );
414 wp_enqueue_style( 'visualizer-front' );
415 }
416
417 // return placeholder div
418 return $actions_div . '<div id="' . $id . '"' . $this->getHtmlAttributes( $attributes ) . '></div>' . $this->addSchema( $chart->ID );
419 }
420
421 /**
422 * Converts the array of attributes to a string of HTML attributes.
423 *
424 * @since ?
425 *
426 * @access private
427 */
428 private function getHtmlAttributes( $attributes ) {
429 $string = '';
430 if ( ! $attributes ) {
431 return $string;
432 }
433
434 foreach ( $attributes as $name => $value ) {
435 $string .= sprintf( '%s="%s"', esc_attr( $name ), esc_attr( $value ) );
436 }
437 return $string;
438 }
439
440 /**
441 * Adds the schema corresponding to the dataset.
442 *
443 * @since 3.3.2
444 *
445 * @access private
446 */
447 private function addSchema( $id ) {
448 // should we show informative errors as HTML comments?
449 $show_errors = apply_filters( 'visualizer_schema_show_errors', true, $id );
450
451 $settings = get_post_meta( $id, Visualizer_Plugin::CF_SETTINGS, true );
452 $title = '';
453 if ( isset( $settings['title'] ) && ! empty( $settings['title'] ) ) {
454 $title = $settings['title'];
455 if ( is_array( $title ) ) {
456 $title = $settings['title']['text'];
457 }
458 }
459 $title = apply_filters( 'visualizer_schema_name', $title, $id );
460 if ( empty( $title ) ) {
461 if ( $show_errors ) {
462 return "<!-- Not showing structured data for chart $id because title is empty -->";
463 }
464 return '';
465 }
466
467 $desc = '';
468 if ( isset( $settings['description'] ) && ! empty( $settings['description'] ) ) {
469 $desc = $settings['description'];
470 }
471 $desc = apply_filters( 'visualizer_schema_description', $desc, $id );
472 if ( empty( $desc ) ) {
473 if ( $show_errors ) {
474 return "<!-- Not showing structured data for chart $id because description is empty -->";
475 }
476 return '';
477 }
478
479 // descriptions below 50 chars are not allowed.
480 if ( strlen( $desc ) < 50 ) {
481 if ( $show_errors ) {
482 return "<!-- Not showing structured data for chart $id because description is shorter than 50 characters -->";
483 }
484 return '';
485 }
486
487 $schema = apply_filters(
488 'visualizer_schema',
489 '{
490 "@context":"https://schema.org/",
491 "@type":"Dataset",
492 "name":"' . esc_html( $title ) . '",
493 "description":"' . esc_html( $desc ) . '"
494 }',
495 $id
496 );
497
498 if ( empty( $schema ) ) {
499 return '';
500 }
501
502 return '<script type="application/ld+json">' . $schema . '</script>';
503 }
504 }
505