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

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