PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.11.6
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.11.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.php

Module.php in Visualizer – Tables & Charts Manager with Built-in AI Generator 3.11.6, at classes/Visualizer/Module.php

848 lines 26.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 * Base class for all modules. Implements routine methods required by all modules.
24 *
25 * @category Visualizer
26 * @package Module
27 *
28 * @since 1.0.0
29 */
30 class Visualizer_Module {
31
32 /**
33 * The instance of wpdb class.
34 *
35 * @since 1.0.0
36 *
37 * @access protected
38 * @var wpdb
39 */
40 protected $_wpdb = null;
41
42 /**
43 * The plugin instance.
44 *
45 * @since 1.0.0
46 *
47 * @access protected
48 * @var Visualizer_Plugin
49 */
50 protected $_plugin = null;
51
52 /**
53 * Constructor.
54 *
55 * @since 1.0.0
56 * @global wpdb $wpdb Current database connection.
57 *
58 * @access public
59 * @param Visualizer_Plugin $plugin The instance of the plugin.
60 */
61 public function __construct( Visualizer_Plugin $plugin ) {
62 global $wpdb;
63
64 $this->_wpdb = $wpdb;
65 $this->_plugin = $plugin;
66
67 $this->_addFilter( Visualizer_Plugin::FILTER_UNDO_REVISIONS, 'undoRevisions', 10, 2 );
68 $this->_addFilter( Visualizer_Plugin::FILTER_HANDLE_REVISIONS, 'handleExistingRevisions', 10, 2 );
69 $this->_addFilter( Visualizer_Plugin::FILTER_GET_CHART_DATA_AS, 'getDataAs', 10, 3 );
70 $this->_addAction( 'pre_get_posts', 'PreGetPosts' );
71 register_shutdown_function( array($this, 'onShutdown') );
72
73 }
74
75 /**
76 * Register a shutdown hook to catch fatal errors.
77 *
78 * @since ?
79 *
80 * @access public
81 */
82 public function onShutdown() {
83 $error = error_get_last();
84 if ( $error && $error['type'] === E_ERROR && false !== strpos( $error['file'], 'Visualizer/' ) ) {
85 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Critical error %s', print_r( $error, true ) ), 'error', __FILE__, __LINE__ );
86 }
87 }
88
89 /**
90 * Registers an action hook.
91 *
92 * @since 1.0.0
93 * @uses add_action() To register action hook.
94 *
95 * @access protected
96 * @param string $tag The name of the action to which the $method is hooked.
97 * @param string $method The name of the method to be called.
98 * @param bool $methodClass The root of the method.
99 * @param int $priority optional. Used to specify the order in which the functions associated with a particular action are executed (default: 10). Lower numbers correspond with earlier execution, and functions with the same priority are executed in the order in which they were added to the action.
100 * @param int $accepted_args optional. The number of arguments the function accept (default 1).
101 * @return Visualizer_Module
102 */
103 protected function _addAction( $tag, $method, $methodClass = null, $priority = 10, $accepted_args = 1 ) {
104 add_action( $tag, array( $methodClass ? $methodClass : $this, $method ), $priority, $accepted_args );
105 return $this;
106 }
107
108 /**
109 * Registers AJAX action hook.
110 *
111 * @since 1.0.0
112 *
113 * @access public
114 * @param string $tag The name of the AJAX action to which the $method is hooked.
115 * @param string $method Optional. The name of the method to be called. If the name of the method is not provided, tag name will be used as method name.
116 * @param bool $methodClass The root of the method.
117 * @param boolean $private Optional. Determines if we should register hook for logged in users.
118 * @param boolean $public Optional. Determines if we should register hook for not logged in users.
119 * @return Visualizer_Module
120 */
121 protected function _addAjaxAction( $tag, $method = '', $methodClass = null, $private = true, $public = false ) {
122 if ( $private ) {
123 $this->_addAction( 'wp_ajax_' . $tag, $method, $methodClass );
124 }
125
126 if ( $public ) {
127 $this->_addAction( 'wp_ajax_nopriv_' . $tag, $method, $methodClass );
128 }
129
130 return $this;
131 }
132
133 /**
134 * Registers a filter hook.
135 *
136 * @since 1.0.0
137 * @uses add_filter() To register filter hook.
138 *
139 * @access protected
140 * @param string $tag The name of the filter to hook the $method to.
141 * @param string $method The name of the method to be called when the filter is applied.
142 * @param int $priority optional. Used to specify the order in which the functions associated with a particular action are executed (default: 10). Lower numbers correspond with earlier execution, and functions with the same priority are executed in the order in which they were added to the action.
143 * @param int $accepted_args optional. The number of arguments the function accept (default 1).
144 * @return Visualizer_Module
145 */
146 protected function _addFilter( $tag, $method, $priority = 10, $accepted_args = 1 ) {
147 add_filter( $tag, array( $this, $method ), $priority, $accepted_args );
148 return $this;
149 }
150
151 /**
152 * Registers a hook for shortcode tag.
153 *
154 * @since 1.0.0
155 * @uses add_shortcode() To register shortcode hook.
156 *
157 * @access protected
158 * @param string $tag Shortcode tag to be searched in post content.
159 * @param string $method Hook to run when shortcode is found.
160 * @return Visualizer_Module
161 */
162 protected function _addShortcode( $tag, $method ) {
163 add_shortcode( $tag, array( $this, $method ) );
164 return $this;
165 }
166
167 /**
168 * A wrapper around the actual function _getDataAs. This function is invoked as a filter.
169 *
170 * @since 3.2.0
171 */
172 public function getDataAs( $final, $chart_id, $type ) {
173 return $this->_getDataAs( $chart_id, $type );
174 }
175
176 /**
177 * Extracts the data for a chart and prepares it for the given type.
178 *
179 * @access public
180 * @param int $chart_id The chart id.
181 * @param string $type The exported type.
182 */
183 public function _getDataAs( $chart_id, $type ) {
184 $final = null;
185 $success = false;
186 if ( $chart_id ) {
187 $chart = get_post( $chart_id );
188 $success = $chart && $chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
189 }
190 if ( $success ) {
191 $settings = get_post_meta( $chart_id, Visualizer_Plugin::CF_SETTINGS, true );
192 $rows = array();
193 $series = get_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, true );
194 $data = self::get_chart_data( $chart, $type, false );
195 if ( ! empty( $series ) ) {
196 $row = array();
197 foreach ( $series as $array ) {
198 $row[] = $array['label'];
199 }
200 $rows[] = $row;
201 $row = array();
202 foreach ( $series as $array ) {
203 $row[] = $array['type'];
204 }
205 $rows[] = $row;
206 }
207 if ( ! empty( $data ) ) {
208 foreach ( $data as $array ) {
209 // ignore strings
210 if ( ! is_array( $array ) ) {
211 continue;
212 }
213 // if this is an array of arrays...
214 if ( is_array( $array[0] ) ) {
215 foreach ( $array as $arr ) {
216 $rows[] = $arr;
217 }
218 } else {
219 // just an array
220 $rows[] = $array;
221 }
222 }
223 }
224
225 $title = 'visualizer#' . $chart_id;
226 if ( ! empty( $settings['title'] ) ) {
227 $title = $settings['title'];
228 }
229 // for ChartJS, title is an array.
230 if ( is_array( $title ) && isset( $title['text'] ) ) {
231 $title = $title['text'];
232 }
233 if ( empty( $title ) ) {
234 $title = 'visualizer#' . $chart_id;
235 }
236
237 $filename = $title;
238
239 switch ( $type ) {
240 case 'csv':
241 $final = $this->_getCSV( $rows, $filename, false );
242 break;
243 case 'csv-display':
244 $final = $this->_getCSV( $rows, $filename, true );
245 break;
246 case 'xls':
247 $final = $this->_getExcel( $rows, $filename );
248 break;
249 case 'print':
250 $final = $this->_getHTML( $rows );
251 break;
252 case 'image':
253 $final = $this->_getImage( $chart );
254 break;
255 }
256 }
257 return $final;
258 }
259
260 /**
261 * Prepares a CSV.
262 *
263 * @access private
264 * @param array $rows The array of data.
265 * @param string $filename The name of the file to use.
266 * @param bool $enclose Enclose strings that have commas in them in double quotes.
267 */
268 private function _getCSV( $rows, $filename, $enclose ) {
269 $filename .= '.csv';
270
271 $bom = chr( 0xEF ) . chr( 0xBB ) . chr( 0xBF );
272 // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
273 $fp = function_exists( 'tmpfile' ) ? @tmpfile() : null;
274 if ( null === $fp ) {
275 if ( ! function_exists( 'wp_tempnam' ) ) {
276 require_once ABSPATH . 'wp-admin/includes/file.php';
277 }
278 $fp = fopen( wp_tempnam(), 'w+' );
279 }
280 if ( ! apply_filters( 'vizualizer_export_include_series_type', true ) ) {
281 unset( $rows[1] );
282 $rows = array_values( $rows );
283 }
284 // support for MS Excel
285 fprintf( $fp, $bom );
286 foreach ( $rows as $row ) {
287 fputcsv( $fp, $row );
288 }
289 rewind( $fp );
290 $csv = '';
291 // phpcs:ignore WordPress.CodeAnalysis.AssignmentInCondition.FoundInWhileCondition
292 while ( ( $array = fgetcsv( $fp ) ) !== false ) {
293 if ( strlen( $csv ) > 0 ) {
294 $csv .= PHP_EOL;
295 }
296 // if enclosure is required, check every item of this line
297 // if a comma exists in the item, add enclosure.
298 if ( $enclose ) {
299 $temp_array = array();
300 foreach ( $array as $item ) {
301 if ( strpos( $item, ',' ) !== false ) {
302 $item = VISUALIZER_CSV_ENCLOSURE . $item . VISUALIZER_CSV_ENCLOSURE;
303 }
304 $temp_array[] = $item;
305 }
306 $array = $temp_array;
307 }
308 $csv .= implode( ',', $array );
309 }
310 fclose( $fp );
311
312 return array(
313 'csv' => $csv,
314 'name' => $filename,
315 'string' => str_replace( $bom, '', $csv ),
316 );
317 }
318
319 /**
320 * Prepares an Excel file.
321 *
322 * @access private
323 * @param array $rows The array of data.
324 * @param string $filename The name of the file to use.
325 */
326 private function _getExcel( $rows, $filename ) {
327 // PHPExcel did not like sheet names longer than 31 characters and we will assume the same with PhpSpreadsheet
328 $chart = substr( $filename, 0, 30 );
329 $filename .= '.xlsx';
330 if ( ! apply_filters( 'vizualizer_export_include_series_type', true ) ) {
331 unset( $rows[1] );
332 $rows = array_values( $rows );
333 $rows = array_map(
334 function( $r ) {
335 return array_map( 'strval', $r );
336 },
337 $rows
338 );
339 }
340 $vendor_file = VISUALIZER_ABSPATH . '/vendor/autoload.php';
341 if ( is_readable( $vendor_file ) ) {
342 include_once( $vendor_file );
343 }
344 $xlsData = '';
345 if ( class_exists( 'PhpOffice\PhpSpreadsheet\Spreadsheet' ) ) {
346 $doc = new PhpOffice\PhpSpreadsheet\Spreadsheet();
347 $doc->getActiveSheet()->fromArray( $rows, null, 'A1' );
348 $doc->getActiveSheet()->setTitle( sanitize_title( $chart ) );
349 $doc = apply_filters( 'visualizer_excel_doc', $doc );
350 $writer = PhpOffice\PhpSpreadsheet\IOFactory::createWriter( $doc, 'Xlsx' );
351 ob_start();
352 $writer->save( 'php://output' );
353 $xlsData = ob_get_contents();
354 ob_end_clean();
355 } else {
356 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, 'Class PhpOffice\PhpSpreadsheet\Spreadsheet does not exist!', 'error', __FILE__, __LINE__ );
357 error_log( 'Class PhpOffice\PhpSpreadsheet\Spreadsheet does not exist!' );
358 }
359 return array(
360 'csv' => 'data:application/vnd.ms-excel;base64,' . base64_encode( $xlsData ),
361 'name' => $filename,
362 'raw' => base64_encode( $xlsData ),
363 );
364 }
365
366 /**
367 * Prepares an HTML table.
368 *
369 * @access private
370 * @param array $rows The array of data.
371 */
372 private function _getHTML( $rows ) {
373 $css = '
374 table.visualizer-print {
375 border-collapse: collapse;
376 }
377 table.visualizer-print, table.visualizer-print th, table.visualizer-print td {
378 border: 1px solid #000;
379 }
380 ';
381 $html = '';
382 $html .= '
383 <html>
384 <head>
385 <style>
386 ' . apply_filters( 'visualizer_print_css', $css ) . '
387 </style>
388 </head>
389 <body>';
390
391 $table = '<table class="visualizer-print">';
392 $index = 0;
393 foreach ( $rows as $row ) {
394 // skip the data type row.
395 if ( 1 === $index ) {
396 $index++;
397 continue;
398 }
399
400 $table .= '<tr>';
401 foreach ( $row as $col ) {
402 if ( $index === 0 ) {
403 $table .= '<th>' . $col . '</th>';
404 } else {
405 $table .= '<td>' . $col . '</td>';
406 }
407 }
408 $table .= '</tr>';
409 $index++;
410 }
411 $table .= '</table>';
412
413 $html .= apply_filters( 'visualizer_print_table', $table ) . '
414 </body>
415 </html>';
416 return array(
417 'csv' => $html,
418 );
419 }
420
421 /**
422 * Disable revisions temporarily for visualizer post type.
423 */
424 protected final function disableRevisionsTemporarily() {
425 add_filter(
426 'wp_revisions_to_keep', function( $num, $post ) {
427 if ( $post->post_type === Visualizer_Plugin::CPT_VISUALIZER ) {
428 return 0;
429 }
430 return $num;
431 }, 10, 2
432 );
433 }
434
435 /**
436 * Undo revisions for the chart, and if necessary, restore the earliest version.
437 *
438 * @return bool If any revisions were found.
439 */
440 public final function undoRevisions( $chart_id, $restore = false ) {
441 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'undoRevisions for %d with%s restore', $chart_id, ( $restore ? '' : 'out' ) ), 'debug', __FILE__, __LINE__ );
442 if ( get_post_type( $chart_id ) !== Visualizer_Plugin::CPT_VISUALIZER ) {
443 return false;
444 }
445 $revisions = wp_get_post_revisions( $chart_id, array( 'order' => 'ASC' ) );
446 if ( count( $revisions ) > 1 ) {
447 $revision_ids = array_keys( $revisions );
448
449 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'found %d revisions = %s', count( $revisions ), print_r( $revision_ids, true ) ), 'debug', __FILE__, __LINE__ );
450
451 // when we restore, a new revision is likely to be created. so, let's disable revisions for the time being.
452 $this->disableRevisionsTemporarily();
453
454 if ( $restore ) {
455 // restore to the oldest one i.e. the first one.
456 wp_restore_post_revision( array_shift( $revision_ids ) );
457 }
458
459 // delete all revisions.
460 foreach ( $revision_ids as $id ) {
461 wp_delete_post_revision( $id );
462 }
463
464 return true;
465 }
466 return false;
467 }
468
469 /**
470 * If existing revisions exist for the chart, restore the earliest version and then create a new revision to initiate editing.
471 */
472 public final function handleExistingRevisions( $chart_id, $chart ) {
473
474 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'handleExistingRevisions for %d', $chart_id ), 'debug', __FILE__, __LINE__ );
475 if ( get_post_type( $chart_id ) !== Visualizer_Plugin::CPT_VISUALIZER ) {
476 return $chart_id;
477 }
478 // undo revisions.
479 $revisions_found = $this->undoRevisions( $chart_id, true );
480
481 // create revision for the edit action.
482 wp_save_post_revision( $chart_id );
483
484 if ( $revisions_found ) {
485 // fetch chart data again in case it was updated by an earlier revision.
486 $chart = get_post( $chart_id );
487 }
488 return $chart;
489 }
490
491 /**
492 * Returns the language of the locale.
493 *
494 * @access protected
495 */
496 protected function get_language() {
497 $locale = get_locale();
498 if ( empty( $locale ) ) {
499 return '';
500 }
501 $array = explode( '_', $locale );
502 if ( count( $array ) < 2 ) {
503 return '';
504 }
505 return reset( $array );
506 }
507
508 /**
509 * Gets/creates the JS where user-specific customizations can be/have been added.
510 */
511 protected function get_user_customization_js() {
512 // use this as the JS file in case we are not able to create the file in uploads.
513 $default = VISUALIZER_ABSURL . 'js/customization.js';
514
515 $uploads = wp_get_upload_dir();
516 $specific = $uploads['baseurl'] . '/visualizer/customization.js';
517
518 // for testing on user sites (before we send them the correctly customized file).
519 if ( VISUALIZER_TEST_JS_CUSTOMIZATION ) {
520 return $default;
521 }
522
523 require_once( ABSPATH . 'wp-admin/includes/file.php' );
524 WP_Filesystem();
525 global $wp_filesystem;
526 if ( ! is_a( $wp_filesystem, 'WP_Filesystem_Base' ) ) {
527 $creds = request_filesystem_credentials( site_url() );
528 wp_filesystem( $creds );
529 }
530
531 $multisite_arg = '/';
532 if ( is_multisite() && ! is_main_site() ) {
533 $multisite_arg = '/sites/' . get_current_blog_id() . '/';
534 }
535
536 $dir = $wp_filesystem->wp_content_dir() . 'uploads' . $multisite_arg . 'visualizer';
537 $file = $wp_filesystem->wp_content_dir() . 'uploads' . $multisite_arg . 'visualizer/customization.js';
538
539 if ( $wp_filesystem->is_readable( $file ) ) {
540 return $specific;
541 }
542
543 if ( $wp_filesystem->exists( $file ) && ! $wp_filesystem->is_readable( $file ) ) {
544 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Unable to read file %s', $file ), 'error', __FILE__, __LINE__ );
545 return $default;
546 }
547
548 if ( ! $wp_filesystem->exists( $dir ) ) {
549 // phpcs:ignore WordPress.CodeAnalysis.AssignmentInCondition.Found
550 if ( ( $done = $wp_filesystem->mkdir( $dir ) ) === false ) {
551 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Unable to create directory %s', $dir ), 'error', __FILE__, __LINE__ );
552 return $default;
553 }
554 }
555
556 // if file does not exist, copy.
557 if ( ! $wp_filesystem->exists( $file ) ) {
558 $src = str_replace( ABSPATH, $wp_filesystem->abspath(), VISUALIZER_ABSPATH . '/js/customization.js' );
559 // phpcs:ignore WordPress.CodeAnalysis.AssignmentInCondition.Found
560 if ( ( $done = $wp_filesystem->copy( $src, $file ) ) === false ) {
561 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Unable to copy file %s to %s', $src, $file ), 'error', __FILE__, __LINE__ );
562 return $default;
563 }
564 }
565
566 return $specific;
567 }
568
569 /**
570 * Load the class for the given chart's chart type so that its assets can be loaded.
571 */
572 protected function load_chart_type( $chart_id ) {
573 $name = $this->load_chart_class_name( $chart_id );
574 $class = null;
575 if ( class_exists( $name ) || true === apply_filters( 'visualizer_load_chart', false, $name ) ) {
576 if ( 'Visualizer_Render_Sidebar_Type_DataTable_DataTable' === $name ) {
577 $name = 'Visualizer_Render_Sidebar_Type_DataTable_Tabular';
578 }
579 $class = new $name;
580 }
581
582 if ( is_null( $class ) && Visualizer_Module::is_pro() ) {
583 // lets see if this type exists in pro. New Lite(3.1.0+) & old Pro(1.8.0-).
584 $type = get_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_TYPE, true );
585 $class = apply_filters( 'visualizer_pro_chart_type_sidebar', null, array( 'id' => $chart_id, 'type' => $type, 'settings' => get_post_meta( $chart_id, Visualizer_Plugin::CF_SETTINGS, true ) ) );
586 }
587
588 return is_null( $class ) ? null : $class->getLibrary();
589 }
590
591 /**
592 * Returns the class name for the given chart's chart type.
593 */
594 protected function load_chart_class_name( $chart_id ) {
595 $type = get_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_TYPE, true );
596 $lib = get_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_LIBRARY, true );
597
598 // backward compatibility.
599 if ( empty( $lib ) ) {
600 $lib = 'GoogleCharts';
601 if ( 'dataTable' === $type ) {
602 $lib = 'DataTable';
603 }
604 }
605 $name = 'Visualizer_Render_Sidebar_Type_' . $lib . '_' . ucwords( $type );
606 return $name;
607 }
608
609 /**
610 * Generates the inline CSS to apply to the chart and adds these classes to the settings.
611 *
612 * @access public
613 * @param int $id The id of the chart.
614 * @param array $settings The settings of the chart.
615 */
616 protected function get_inline_custom_css( $id, $settings ) {
617 $css = '';
618
619 $classes = array();
620 $css = '<style type="text/css" name="visualizer-custom-css" id="customcss-' . $id . '">';
621 if ( ! empty( $settings['customcss'] ) ) {
622 foreach ( $settings['customcss'] as $name => $element ) {
623 $attributes = array();
624 foreach ( $element as $property => $value ) {
625 $attributes[] = $this->handle_css_property( $property, $value );
626 }
627 $class_name = $id . $name;
628 $properties = implode( ' !important; ', array_filter( $attributes ) );
629 if ( ! empty( $properties ) ) {
630 $css .= '.' . $class_name . ' {' . $properties . ' !important;}';
631 $classes[ $name ] = $class_name;
632 }
633 }
634 $settings['cssClassNames'] = $classes;
635 }
636
637 $img_path = VISUALIZER_ABSURL . 'images';
638 $css .= ".locker,.locker-loader{position:absolute;top:0;left:0;width:100%;height:100%}.locker{z-index:1000;opacity:.8;background-color:#fff;-ms-filter:\"progid:DXImageTransform.Microsoft.Alpha(Opacity=80)\";filter:alpha(opacity=80)}.locker-loader{z-index:1001;background:url($img_path/ajax-loader.gif) no-repeat center center}.dt-button{display:none!important}.visualizer-front-container.visualizer-lazy-render{content-visibility: auto;}.google-visualization-controls-categoryfilter label.google-visualization-controls-label {vertical-align: middle;}.google-visualization-controls-categoryfilter li.goog-inline-block {margin: 0 0.2em;}.google-visualization-controls-categoryfilter li {padding: 0 0.2em;}.visualizer-front-container .dataTables_scrollHeadInner{margin: 0 auto;}";
639 $css .= '</style>';
640
641 $arguments = array( $css, $settings );
642 apply_filters_ref_array( 'visualizer_inline_css', array( &$arguments ) );
643
644 return $arguments;
645 }
646
647 /**
648 * Handles CSS properties that might need special syntax.
649 *
650 * @access private
651 * @param string $property The name of the css property.
652 * @param string $value The value of the css property.
653 */
654 private function handle_css_property( $property, $value ) {
655 if ( empty( $property ) || empty( $value ) ) {
656 return '';
657 }
658
659 switch ( $property ) {
660 case 'transform':
661 $value = 'rotate(' . $value . 'deg)';
662 break;
663 }
664 return $property . ': ' . $value;
665 }
666
667 /**
668 * Determines if charts have been created of the particular chart type.
669 */
670 protected static function hasChartType( $type ) {
671 $args = array(
672 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
673 'fields' => 'ids',
674 'post_status' => 'publish',
675 'meta_query' => array(
676 array(
677 'key' => Visualizer_Plugin::CF_CHART_TYPE,
678 'value' => $type,
679 ),
680 ),
681 );
682
683 $q = new WP_Query( $args );
684 return $q->found_posts > 0;
685 }
686
687 /**
688 * Determines how many charts have been created.
689 */
690 protected static function numberOfCharts() {
691 $args = array(
692 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
693 'fields' => 'ids',
694 'post_status' => 'publish',
695 'posts_per_page' => 300,
696 );
697
698 $q = new WP_Query( $args );
699 return $q->found_posts;
700 }
701
702 /**
703 * Checks if the PRO version is active.
704 *
705 * @since 3.3.0
706 */
707 public static function is_pro() {
708 // versions of pro before 1.9.0 will use the constant VISUALIZER_PRO
709 // versions of pro 1.9.0 onwards will use the filter
710 return apply_filters( 'visualizer_is_pro', VISUALIZER_PRO );
711 }
712
713 /**
714 * Checks if the PRO version is older than a particular version.
715 *
716 * @since 3.3.0
717 */
718 public static function is_pro_older_than( $version ) {
719 return version_compare( VISUALIZER_PRO_VERSION, $version, '<' );
720 }
721
722 /**
723 * Should we show some specific feature on the basis of the version?
724 *
725 * @since 3.4.0
726 */
727 public static function can_show_feature( $feature ) {
728 switch ( $feature ) {
729 case 'simple-editor':
730 // if user has pro but an older version, then don't load the simple editor functionality
731 // as the select box will not behave as expected because the pro editor's functionality will supercede.
732 return ! Visualizer_Module::is_pro() || ! Visualizer_Module::is_pro_older_than( '1.9.2' );
733 }
734 return false;
735 }
736
737 /**
738 * Gets the features for the provided license type.
739 */
740 public static final function get_features_for_license( $plan ) {
741 $is_new_personal = apply_filters( 'visualizer_is_new_personal', false );
742 switch ( $plan ) {
743 case 1:
744 $features = array( 'import-wp', 'import-wc-report', 'import-file', 'import-url' );
745 if ( ! $is_new_personal ) {
746 $features[] = 'db-query';
747 }
748 return $features;
749 case 2:
750 $features = array( 'schedule-chart', 'chart-permissions', 'import-chart', 'data-filter-configuration', 'frontend-actions' );
751 if ( $is_new_personal ) {
752 $features[] = 'db-query';
753 }
754 return $features;
755 }
756 }
757
758 /**
759 * Gets the chart content after common manipulations.
760 */
761 public static function get_chart_data( $chart, $type, $run_filter = true ) {
762 // change HTML entities
763 $post_content = html_entity_decode( htmlentities( $chart->post_content ) );
764 $post_content = preg_replace_callback(
765 '!s:(\d+):"(.*?)";!s',
766 function ( $matches ) {
767 if ( isset( $matches[2] ) ) {
768 return 's:' . strlen( $matches[2] ) . ':"' . $matches[2] . '";';
769 }
770 },
771 $post_content
772 );
773 $data = unserialize( $post_content );
774 $altered = array();
775 if ( ! empty( $data ) ) {
776 foreach ( $data as $index => $array ) {
777 if ( ! is_array( $index ) && is_array( $array ) ) {
778 foreach ( $array as &$datum ) {
779 if ( is_string( $datum ) ) {
780 $datum = stripslashes( $datum );
781 }
782 }
783 $altered[ $index ] = $array;
784 }
785 }
786 }
787 // if something goes wrong and the end result is empty, be safe and use the original data
788 if ( empty( $altered ) ) {
789 $altered = $data;
790 }
791 if ( $run_filter ) {
792 return apply_filters( Visualizer_Plugin::FILTER_GET_CHART_DATA, $altered, $chart->ID, $type );
793 }
794 return $altered;
795 }
796
797 /**
798 * Get chart image.
799 *
800 * @param object $chart Chart data.
801 * @return string
802 */
803 public function _getImage( $chart = null ) {
804 $image = '';
805 if ( $chart ) {
806 $chart_image = get_post_meta( $chart->ID, Visualizer_Plugin::CF_CHART_IMAGE, true );
807 if ( ! empty( $chart_image ) ) {
808 $image = wp_get_attachment_image( $chart_image, 'full' );
809 }
810 }
811 return array(
812 'csv' => $image,
813 );
814 }
815
816 /**
817 * Filter chart title if visualizer post type.
818 *
819 * @param object $query WP Query object.
820 * @return void
821 */
822 public function PreGetPosts( $query ) {
823 if ( ! $query->is_main_query() ) {
824 $post_type = $query->get( 'post_type' );
825 if ( 'visualizer' === $post_type ) {
826 $this->_addFilter( Visualizer_Plugin::FILTER_CHART_TITLE, 'filterChartTitle', 10, 2 );
827 }
828 }
829 }
830
831 /**
832 * Filter chart title.
833 *
834 * @access public
835 * @param string $post_title Post title.
836 * @param int $post_id Post ID.
837 * @return string
838 */
839 public function filterChartTitle( $post_title, $post_id ) {
840 $post_type = get_post_type( $post_id );
841 $post_title = trim( $post_title );
842 if ( 'visualizer' === $post_type && 'Visualization' === $post_title ) {
843 return sprintf( '%s #%d', $post_title, $post_id );
844 }
845 return $post_title;
846 }
847 }
848