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

481 lines 15.3 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-new', 'google-jsapi-old', '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 'methods' => array( 'GET', 'POST' ),
112 'callback' => array( $this, 'perform_action' ),
113 )
114 );
115 }
116
117 /**
118 * All possible actions and their labels
119 *
120 * @access private
121 */
122 private function get_actions() {
123 $actions = apply_filters(
124 'visualizer_action_buttons',
125 array(
126 'print' => array(
127 'label' => esc_html__( 'Print', 'visualizer' ),
128 'title' => esc_html__( 'Print Chart', 'visualizer' ),
129 ),
130 'csv' => array(
131 'label' => esc_html__( 'CSV', 'visualizer' ),
132 'title' => esc_html__( 'Download as a CSV', 'visualizer' ),
133 ),
134 'xls' => array(
135 'label' => esc_html__( 'Excel', 'visualizer' ),
136 'title' => esc_html__( 'Download as a spreadsheet', 'visualizer' ),
137 ),
138 'copy' => array(
139 'label' => esc_html__( 'Copy', 'visualizer' ),
140 'title' => esc_html__( 'Copy data', 'visualizer' ),
141 ),
142 'image' => array(
143 'label' => esc_html__( 'Download', 'visualizer' ),
144 'title' => esc_html__( 'Download as an image', 'visualizer' ),
145 ),
146 )
147 );
148
149 // backward compatibility before label and title attributes were introduced.
150 if ( isset( $actions['edit'] ) && is_string( $actions['edit'] ) ) {
151 $actions['edit'] = array(
152 'label' => esc_html__( 'Edit', 'visualizer' ),
153 'title' => esc_html__( 'Edit data', 'visualizer' ),
154 );
155 }
156 return $actions;
157 }
158
159 /**
160 * The print button
161 *
162 * @since 1.0.0
163 *
164 * @access public
165 */
166 public function perform_action( WP_REST_Request $params ) {
167 $chart_id = filter_var( sanitize_text_field( $params['chart'] ), FILTER_VALIDATE_INT );
168 $type = sanitize_text_field( $params['type'] );
169 $data = null;
170
171 if ( ! $chart_id ) {
172 return new WP_REST_Response( array( 'error' => new WP_Error( __( 'Invalid chart ID', 'visualizer' ) ) ) );
173 }
174
175 if ( ! $type ) {
176 return new WP_REST_Response( array( 'error' => new WP_Error( __( 'Invalid action', 'visualizer' ) ) ) );
177 }
178
179 switch ( $type ) {
180 case 'print':
181 $data = $this->_getDataAs( $chart_id, 'print' );
182 break;
183 case 'csv':
184 $data = $this->_getDataAs( $chart_id, 'csv' );
185 break;
186 case 'xls':
187 $data = $this->_getDataAs( $chart_id, 'xls' );
188 break;
189 case 'image':
190 $settings = get_post_meta( $chart_id, Visualizer_Plugin::CF_SETTINGS, true );
191 $title = 'visualizer#' . $chart_id;
192 if ( ! empty( $settings['title'] ) ) {
193 $title = $settings['title'];
194 }
195 // for ChartJS, title is an array.
196 if ( is_array( $title ) && isset( $title['text'] ) ) {
197 $title = $title['text'];
198 }
199 if ( empty( $title ) ) {
200 $title = 'visualizer#' . $chart_id;
201 }
202 $data = array( 'name' => $title );
203 break;
204 default:
205 $data = apply_filters( 'visualizer_action_data', $data, $chart_id, $type, $params, $this );
206 break;
207 }
208
209 return new WP_REST_Response( array( 'data' => $data ) );
210 }
211
212 /**
213 * Registers scripts for charts rendering.
214 *
215 * @since 1.0.0
216 * @uses wp_register_script To register javascript file.
217 *
218 * @access public
219 */
220 public function enqueueScripts() {
221 wp_register_script( 'visualizer-customization', $this->get_user_customization_js(), array(), null, true );
222 wp_register_script( 'visualizer-clipboardjs', VISUALIZER_ABSURL . 'js/lib/clipboardjs/clipboard.min.js', array( 'jquery' ), Visualizer_Plugin::VERSION, true );
223 wp_register_style( 'visualizer-front', VISUALIZER_ABSURL . 'css/front.css', array(), Visualizer_Plugin::VERSION );
224 do_action( 'visualizer_pro_frontend_load_resources' );
225 }
226
227 /**
228 * Returns placeholder for chart according to visualizer shortcode attributes.
229 *
230 * @since 1.0.0
231 * @uses shortcode_atts() To parse income shortocdes.
232 * @uses apply_filters() To filter chart's data and series arrays.
233 * @uses get_post_meta() To fetch chart's meta information.
234 * @uses wp_enqueue_script() To enqueue charts render script.
235 * @uses wp_localize_script() To add chart data to the page inline script.
236 *
237 * @access public
238 * @param array $atts The array of shortcode attributes.
239 */
240 public function renderChart( $atts ) {
241 global $wp_version;
242 $atts = shortcode_atts(
243 array(
244 // chart id
245 'id' => false,
246 // chart class
247 'class' => false,
248 // for lazy load
249 // set 'yes' to use the default intersection limit (300px)
250 // OR set a number (e.g. 700) to use 700px as the intersection limit
251 'lazy' => apply_filters( 'visualizer_lazy_by_default', false, $atts['id'] ),
252 ),
253 $atts
254 );
255
256 // if empty id or chart does not exists, then return empty string
257 if ( ! $atts['id'] || ! ( $chart = get_post( $atts['id'] ) ) || $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ) {
258 return '';
259 }
260
261 // do not show the chart?
262 if ( ! apply_filters( 'visualizer_pro_show_chart', true, $atts['id'] ) ) {
263 return '';
264 }
265
266 // in case revisions exist.
267 // phpcs:ignore WordPress.CodeAnalysis.AssignmentInCondition.Found
268 if ( true === ( $revisions = $this->undoRevisions( $chart->ID, true ) ) ) {
269 $chart = get_post( $chart->ID );
270 }
271
272 $id = 'visualizer-' . $atts['id'];
273 $defaultClass = 'visualizer-front';
274 $class = apply_filters( Visualizer_Plugin::FILTER_CHART_WRAPPER_CLASS, $atts['class'], $atts['id'] );
275
276 $lazyClass = $atts['lazy'] === 'yes' || ctype_digit( $atts['lazy'] ) ? 'visualizer-lazy' : '';
277
278 $class = sprintf( '%s %s %s %s', $defaultClass, $class, 'visualizer-front-' . $atts['id'], $lazyClass );
279 $attributes = array();
280 if ( ! empty( $class ) ) {
281 $attributes['class'] = trim( $class );
282 }
283
284 if ( ctype_digit( $atts['lazy'] ) ) {
285 $attributes['data-lazy-limit'] = $atts['lazy'];
286 }
287
288 $type = get_post_meta( $chart->ID, Visualizer_Plugin::CF_CHART_TYPE, true );
289
290 $chart = apply_filters( 'visualizer_schedule_refresh_chart', $chart, $chart->ID, false );
291
292 // fetch and update settings
293 $settings = get_post_meta( $chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
294 if ( empty( $settings['height'] ) ) {
295 $settings['height'] = '400';
296 }
297
298 // handle series filter hooks
299 $series = apply_filters( Visualizer_Plugin::FILTER_GET_CHART_SERIES, get_post_meta( $chart->ID, Visualizer_Plugin::CF_SERIES, true ), $chart->ID, $type );
300
301 // handle settings filter hooks
302 $settings = apply_filters( Visualizer_Plugin::FILTER_GET_CHART_SETTINGS, $settings, $chart->ID, $type );
303
304 // handle data filter hooks
305 $data = self::get_chart_data( $chart, $type );
306
307 $css = '';
308 $arguments = $this->get_inline_custom_css( $id, $settings );
309 if ( ! empty( $arguments ) ) {
310 $css = $arguments[0];
311 $settings = $arguments[1];
312 }
313
314 $library = $this->load_chart_type( $chart->ID );
315
316 $id = $id . '-' . rand();
317
318 $amp = Visualizer_Plugin::instance()->getModule( Visualizer_Module_AMP::NAME );
319 if ( $amp && $amp->is_amp() ) {
320 return '<div id="' . $id . '"' . $this->getHtmlAttributes( $attributes ) . '>' . $amp->get_chart( $chart, $data, $series, $settings ) . '</div>';
321 }
322
323 // add chart to the array
324 $this->_charts[ $id ] = array(
325 'type' => $type,
326 'series' => $series,
327 'settings' => $settings,
328 'data' => $data,
329 'library' => $library,
330 );
331
332 $actions_div = '';
333 $actions_visible = apply_filters( 'visualizer_pro_add_actions', isset( $settings['actions'] ) ? $settings['actions'] : array(), $atts['id'] );
334 if ( ! empty( $actions_visible ) ) {
335 $actions = $this->get_actions();
336 $actions_div = '<div class="visualizer-actions">';
337 foreach ( $actions_visible as $action_type ) {
338 $key = $action_type;
339 $mime = '';
340 if ( strpos( $action_type, ';' ) !== false ) {
341 $array = explode( ';', $action_type );
342 $key = $array[0];
343 $mime = end( $array );
344 }
345 $label = $actions[ $key ]['label'];
346 $title = $actions[ $key ]['title'];
347 $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 );
348
349 if ( 'copy' === $key ) {
350 $copy = $this->_getDataAs( $atts['id'], 'csv' );
351 $actions_div .= ' data-clipboard-text="' . esc_attr( $copy['csv'] ) . '"';
352 wp_enqueue_script( 'visualizer-clipboardjs' );
353 }
354
355 $actions_div .= apply_filters( 'visualizer_action_attributes', '', $key, $atts['id'] );
356 $actions_div .= '>' . $label . '</a> &nbsp;';
357 }
358
359 $actions_div .= '</div>';
360 }
361
362 $actions_div .= $css;
363
364 foreach ( $this->_charts as $id => $array ) {
365 $library = $array['library'];
366 wp_register_script(
367 "visualizer-render-$library",
368 VISUALIZER_ABSURL . 'js/render-facade.js',
369 apply_filters( 'visualizer_assets_render', array( 'jquery', 'visualizer-customization' ), true ),
370 Visualizer_Plugin::VERSION,
371 true
372 );
373
374 wp_enqueue_script( "visualizer-render-$library" );
375 wp_localize_script(
376 "visualizer-render-$library",
377 'visualizer',
378 array(
379 'charts' => $this->_charts,
380 'language' => $this->get_language(),
381 'map_api_key' => get_option( 'visualizer-map-api-key' ),
382 'rest_url' => version_compare( $wp_version, '4.7.0', '>=' ) ? rest_url( 'visualizer/v' . VISUALIZER_REST_VERSION . '/action/#id#/#type#/' ) : '',
383 'i10n' => array(
384 'copied' => __( 'The data has been copied to your clipboard. Hit Ctrl-V/Cmd-V in your spreadsheet editor to paste the data.', 'visualizer' ),
385 ),
386 'page_type' => 'frontend',
387 'is_front' => true,
388 )
389 );
390 wp_enqueue_style( 'visualizer-front' );
391 }
392
393 // return placeholder div
394 return $actions_div . '<div id="' . $id . '"' . $this->getHtmlAttributes( $attributes ) . '></div>' . $this->addSchema( $chart->ID );
395 }
396
397 /**
398 * Converts the array of attributes to a string of HTML attributes.
399 *
400 * @since ?
401 *
402 * @access private
403 */
404 private function getHtmlAttributes( $attributes ) {
405 $string = '';
406 if ( ! $attributes ) {
407 return $string;
408 }
409
410 foreach ( $attributes as $name => $value ) {
411 $string .= sprintf( '%s="%s"', esc_attr( $name ), esc_attr( $value ) );
412 }
413 return $string;
414 }
415
416 /**
417 * Adds the schema corresponding to the dataset.
418 *
419 * @since 3.3.2
420 *
421 * @access private
422 */
423 private function addSchema( $id ) {
424 // should we show informative errors as HTML comments?
425 $show_errors = apply_filters( 'visualizer_schema_show_errors', true, $id );
426
427 $settings = get_post_meta( $id, Visualizer_Plugin::CF_SETTINGS, true );
428 $title = '';
429 if ( isset( $settings['title'] ) && ! empty( $settings['title'] ) ) {
430 $title = $settings['title'];
431 if ( is_array( $title ) ) {
432 $title = $settings['title']['text'];
433 }
434 }
435 $title = apply_filters( 'visualizer_schema_name', $title, $id );
436 if ( empty( $title ) ) {
437 if ( $show_errors ) {
438 return "<!-- Not showing structured data for chart $id because title is empty -->";
439 }
440 return '';
441 }
442
443 $desc = '';
444 if ( isset( $settings['description'] ) && ! empty( $settings['description'] ) ) {
445 $desc = $settings['description'];
446 }
447 $desc = apply_filters( 'visualizer_schema_description', $desc, $id );
448 if ( empty( $desc ) ) {
449 if ( $show_errors ) {
450 return "<!-- Not showing structured data for chart $id because description is empty -->";
451 }
452 return '';
453 }
454
455 // descriptions below 50 chars are not allowed.
456 if ( strlen( $desc ) < 50 ) {
457 if ( $show_errors ) {
458 return "<!-- Not showing structured data for chart $id because description is shorter than 50 characters -->";
459 }
460 return '';
461 }
462
463 $schema = apply_filters(
464 'visualizer_schema',
465 '{
466 "@context":"https://schema.org/",
467 "@type":"Dataset",
468 "name":"' . esc_html( $title ) . '",
469 "description":"' . esc_html( $desc ) . '"
470 }',
471 $id
472 );
473
474 if ( empty( $schema ) ) {
475 return '';
476 }
477
478 return '<script type="application/ld+json">' . $schema . '</script>';
479 }
480 }
481