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

857 lines 26.9 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 switch ( $type ) {
239 case 'csv':
240 $final = $this->_getCSV( $rows, $filename, false );
241 break;
242 case 'csv-display':
243 $final = $this->_getCSV( $rows, $filename, true );
244 break;
245 case 'xls':
246 $final = $this->_getExcel( $rows, $filename );
247 break;
248 case 'print':
249 $final = $this->_getHTML( $rows );
250 break;
251 case 'image':
252 $final = $this->_getImage( $chart );
253 break;
254 }
255 }
256 return $final;
257 }
258
259 /**
260 * Prepares a CSV.
261 *
262 * @access private
263 * @param array $rows The array of data.
264 * @param string $filename The name of the file to use.
265 * @param bool $enclose Enclose strings that have commas in them in double quotes.
266 */
267 private function _getCSV( $rows, $filename, $enclose ) {
268 $filename .= '.csv';
269
270 $bom = chr( 0xEF ) . chr( 0xBB ) . chr( 0xBF );
271 // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
272 $fp = function_exists( 'tmpfile' ) ? @tmpfile() : null;
273 if ( null === $fp ) {
274 if ( ! function_exists( 'wp_tempnam' ) ) {
275 require_once ABSPATH . 'wp-admin/includes/file.php';
276 }
277 $fp = fopen( wp_tempnam(), 'w+' );
278 }
279 if ( ! apply_filters( 'vizualizer_export_include_series_type', true ) ) {
280 unset( $rows[1] );
281 $rows = array_values( $rows );
282 }
283 // support for MS Excel
284 fprintf( $fp, $bom );
285 foreach ( $rows as $row ) {
286 fputcsv( $fp, $row );
287 }
288 rewind( $fp );
289 $csv = '';
290 // phpcs:ignore WordPress.CodeAnalysis.AssignmentInCondition.FoundInWhileCondition
291 while ( ( $array = fgetcsv( $fp ) ) !== false ) {
292 if ( strlen( $csv ) > 0 ) {
293 $csv .= PHP_EOL;
294 }
295 // if enclosure is required, check every item of this line
296 // if a comma exists in the item, add enclosure.
297 if ( $enclose ) {
298 $temp_array = array();
299 foreach ( $array as $item ) {
300 if ( strpos( $item, ',' ) !== false ) {
301 $item = VISUALIZER_CSV_ENCLOSURE . $item . VISUALIZER_CSV_ENCLOSURE;
302 }
303 $temp_array[] = $item;
304 }
305 $array = $temp_array;
306 }
307 $csv .= implode( ',', $array );
308 }
309 fclose( $fp );
310
311 return array(
312 'csv' => $csv,
313 'name' => $filename,
314 'string' => str_replace( $bom, '', $csv ),
315 );
316 }
317
318 /**
319 * Prepares an Excel file.
320 *
321 * @access private
322 * @param array $rows The array of data.
323 * @param string $filename The name of the file to use.
324 */
325 private function _getExcel( $rows, $filename ) {
326 // OpenSpout allows for long sheet names, but let's keep the same limit for compatibility.
327 $chart = substr( $filename, 0, 30 );
328 $filename .= '.xlsx';
329 if ( ! apply_filters( 'vizualizer_export_include_series_type', true ) ) {
330 unset( $rows[1] );
331 $rows = array_values( $rows );
332 $rows = array_map(
333 function( $r ) {
334 return array_map( 'strval', $r );
335 },
336 $rows
337 );
338 }
339 $vendor_file = VISUALIZER_ABSPATH . '/vendor/autoload.php';
340 if ( is_readable( $vendor_file ) ) {
341 include_once $vendor_file;
342 }
343 $xlsData = '';
344 if ( class_exists( 'OpenSpout\Writer\Common\Creator\WriterEntityFactory' ) ) {
345 try {
346 // Use OpenSpout to create the XLSX file in memory.
347 $writer = \OpenSpout\Writer\Common\Creator\WriterEntityFactory::createXLSXWriter();
348 $writer->openToFile( 'php://output' ); // Open to output instead of a file.
349 $writer->getCurrentSheet()->setName( sanitize_title( $chart ) );
350
351 // Write rows.
352 foreach ( $rows as $row ) {
353 $rowFromValues = \OpenSpout\Writer\Common\Creator\WriterEntityFactory::createRowFromArray( $row );
354 $writer->addRow( $rowFromValues );
355 }
356
357 ob_start();
358 $writer->close(); // Saves and closes the file in the output buffer.
359 $xlsData = ob_get_clean();
360 } catch ( Exception $e ) {
361 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, 'OpenSpout writer error: ' . $e->getMessage(), 'error', __FILE__, __LINE__ );
362 error_log( 'OpenSpout writer error: ' . $e->getMessage() );
363 }
364 } else {
365 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, 'Class OpenSpout\Writer\Common\Creator\WriterEntityFactory does not exist!', 'error', __FILE__, __LINE__ );
366 error_log( 'Class OpenSpout\Writer\Common\Creator\WriterEntityFactory does not exist!' );
367 }
368 return array(
369 'csv' => 'data:application/vnd.ms-excel;base64,' . base64_encode( $xlsData ),
370 'name' => $filename,
371 'raw' => base64_encode( $xlsData ),
372 );
373 }
374
375 /**
376 * Prepares an HTML table.
377 *
378 * @access private
379 * @param array $rows The array of data.
380 */
381 private function _getHTML( $rows ) {
382 $css = '
383 table.visualizer-print {
384 border-collapse: collapse;
385 }
386 table.visualizer-print, table.visualizer-print th, table.visualizer-print td {
387 border: 1px solid #000;
388 }
389 ';
390 $html = '';
391 $html .= '
392 <html>
393 <head>
394 <style>
395 ' . apply_filters( 'visualizer_print_css', $css ) . '
396 </style>
397 </head>
398 <body>';
399
400 $table = '<table class="visualizer-print">';
401 $index = 0;
402 foreach ( $rows as $row ) {
403 // skip the data type row.
404 if ( 1 === $index ) {
405 $index++;
406 continue;
407 }
408
409 $table .= '<tr>';
410 foreach ( $row as $col ) {
411 if ( $index === 0 ) {
412 $table .= '<th>' . $col . '</th>';
413 } else {
414 $table .= '<td>' . $col . '</td>';
415 }
416 }
417 $table .= '</tr>';
418 $index++;
419 }
420 $table .= '</table>';
421
422 $html .= apply_filters( 'visualizer_print_table', $table ) . '
423 </body>
424 </html>';
425 return array(
426 'csv' => $html,
427 );
428 }
429
430 /**
431 * Disable revisions temporarily for visualizer post type.
432 */
433 protected final function disableRevisionsTemporarily() {
434 add_filter(
435 'wp_revisions_to_keep', function( $num, $post ) {
436 if ( $post->post_type === Visualizer_Plugin::CPT_VISUALIZER ) {
437 return 0;
438 }
439 return $num;
440 }, 10, 2
441 );
442 }
443
444 /**
445 * Undo revisions for the chart, and if necessary, restore the earliest version.
446 *
447 * @return bool If any revisions were found.
448 */
449 public final function undoRevisions( $chart_id, $restore = false ) {
450 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'undoRevisions for %d with%s restore', $chart_id, ( $restore ? '' : 'out' ) ), 'debug', __FILE__, __LINE__ );
451 if ( get_post_type( $chart_id ) !== Visualizer_Plugin::CPT_VISUALIZER ) {
452 return false;
453 }
454 $revisions = wp_get_post_revisions( $chart_id, array( 'order' => 'ASC' ) );
455 if ( count( $revisions ) > 1 ) {
456 $revision_ids = array_keys( $revisions );
457
458 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'found %d revisions = %s', count( $revisions ), print_r( $revision_ids, true ) ), 'debug', __FILE__, __LINE__ );
459
460 // when we restore, a new revision is likely to be created. so, let's disable revisions for the time being.
461 $this->disableRevisionsTemporarily();
462
463 if ( $restore ) {
464 // restore to the oldest one i.e. the first one.
465 wp_restore_post_revision( array_shift( $revision_ids ) );
466 }
467
468 // delete all revisions.
469 foreach ( $revision_ids as $id ) {
470 wp_delete_post_revision( $id );
471 }
472
473 return true;
474 }
475 return false;
476 }
477
478 /**
479 * If existing revisions exist for the chart, restore the earliest version and then create a new revision to initiate editing.
480 */
481 public final function handleExistingRevisions( $chart_id, $chart ) {
482
483 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'handleExistingRevisions for %d', $chart_id ), 'debug', __FILE__, __LINE__ );
484 if ( get_post_type( $chart_id ) !== Visualizer_Plugin::CPT_VISUALIZER ) {
485 return $chart_id;
486 }
487 // undo revisions.
488 $revisions_found = $this->undoRevisions( $chart_id, true );
489
490 // create revision for the edit action.
491 wp_save_post_revision( $chart_id );
492
493 if ( $revisions_found ) {
494 // fetch chart data again in case it was updated by an earlier revision.
495 $chart = get_post( $chart_id );
496 }
497 return $chart;
498 }
499
500 /**
501 * Returns the language of the locale.
502 *
503 * @access protected
504 */
505 protected function get_language() {
506 $locale = get_locale();
507 if ( empty( $locale ) ) {
508 return '';
509 }
510 $array = explode( '_', $locale );
511 if ( count( $array ) < 2 ) {
512 return '';
513 }
514 return reset( $array );
515 }
516
517 /**
518 * Gets/creates the JS where user-specific customizations can be/have been added.
519 */
520 protected function get_user_customization_js() {
521 // use this as the JS file in case we are not able to create the file in uploads.
522 $default = VISUALIZER_ABSURL . 'js/customization.js';
523
524 $uploads = wp_get_upload_dir();
525 $specific = $uploads['baseurl'] . '/visualizer/customization.js';
526
527 // for testing on user sites (before we send them the correctly customized file).
528 if ( VISUALIZER_TEST_JS_CUSTOMIZATION ) {
529 return $default;
530 }
531
532 require_once( ABSPATH . 'wp-admin/includes/file.php' );
533 WP_Filesystem();
534 global $wp_filesystem;
535 if ( ! is_a( $wp_filesystem, 'WP_Filesystem_Base' ) ) {
536 $creds = request_filesystem_credentials( site_url() );
537 wp_filesystem( $creds );
538 }
539
540 $multisite_arg = '/';
541 if ( is_multisite() && ! is_main_site() ) {
542 $multisite_arg = '/sites/' . get_current_blog_id() . '/';
543 }
544
545 $dir = $wp_filesystem->wp_content_dir() . 'uploads' . $multisite_arg . 'visualizer';
546 $file = $wp_filesystem->wp_content_dir() . 'uploads' . $multisite_arg . 'visualizer/customization.js';
547
548 if ( $wp_filesystem->is_readable( $file ) ) {
549 return $specific;
550 }
551
552 if ( $wp_filesystem->exists( $file ) && ! $wp_filesystem->is_readable( $file ) ) {
553 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Unable to read file %s', $file ), 'error', __FILE__, __LINE__ );
554 return $default;
555 }
556
557 if ( ! $wp_filesystem->exists( $dir ) ) {
558 // phpcs:ignore WordPress.CodeAnalysis.AssignmentInCondition.Found
559 if ( ( $done = $wp_filesystem->mkdir( $dir ) ) === false ) {
560 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Unable to create directory %s', $dir ), 'error', __FILE__, __LINE__ );
561 return $default;
562 }
563 }
564
565 // if file does not exist, copy.
566 if ( ! $wp_filesystem->exists( $file ) ) {
567 $src = str_replace( ABSPATH, $wp_filesystem->abspath(), VISUALIZER_ABSPATH . '/js/customization.js' );
568 // phpcs:ignore WordPress.CodeAnalysis.AssignmentInCondition.Found
569 if ( ( $done = $wp_filesystem->copy( $src, $file ) ) === false ) {
570 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Unable to copy file %s to %s', $src, $file ), 'error', __FILE__, __LINE__ );
571 return $default;
572 }
573 }
574
575 return $specific;
576 }
577
578 /**
579 * Load the class for the given chart's chart type so that its assets can be loaded.
580 */
581 protected function load_chart_type( $chart_id ) {
582 $name = $this->load_chart_class_name( $chart_id );
583 $class = null;
584 if ( class_exists( $name ) || true === apply_filters( 'visualizer_load_chart', false, $name ) ) {
585 if ( 'Visualizer_Render_Sidebar_Type_DataTable_DataTable' === $name ) {
586 $name = 'Visualizer_Render_Sidebar_Type_DataTable_Tabular';
587 }
588 $class = new $name;
589 }
590
591 if ( is_null( $class ) && Visualizer_Module::is_pro() ) {
592 // lets see if this type exists in pro. New Lite(3.1.0+) & old Pro(1.8.0-).
593 $type = get_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_TYPE, true );
594 $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 ) ) );
595 }
596
597 return is_null( $class ) ? null : $class->getLibrary();
598 }
599
600 /**
601 * Returns the class name for the given chart's chart type.
602 */
603 protected function load_chart_class_name( $chart_id ) {
604 $type = get_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_TYPE, true );
605 $lib = get_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_LIBRARY, true );
606
607 // backward compatibility.
608 if ( empty( $lib ) ) {
609 $lib = 'GoogleCharts';
610 if ( 'dataTable' === $type ) {
611 $lib = 'DataTable';
612 }
613 }
614 $name = 'Visualizer_Render_Sidebar_Type_' . $lib . '_' . ucwords( $type );
615 return $name;
616 }
617
618 /**
619 * Generates the inline CSS to apply to the chart and adds these classes to the settings.
620 *
621 * @access public
622 * @param int $id The id of the chart.
623 * @param array $settings The settings of the chart.
624 */
625 protected function get_inline_custom_css( $id, $settings ) {
626 $css = '';
627
628 $classes = array();
629 $css = '<style type="text/css" name="visualizer-custom-css" id="customcss-' . $id . '">';
630 if ( ! empty( $settings['customcss'] ) ) {
631 foreach ( $settings['customcss'] as $name => $element ) {
632 $attributes = array();
633 foreach ( $element as $property => $value ) {
634 $attributes[] = $this->handle_css_property( $property, $value );
635 }
636 $class_name = $id . $name;
637 $properties = implode( ' !important; ', array_filter( $attributes ) );
638 if ( ! empty( $properties ) ) {
639 $css .= '.' . $class_name . ' {' . $properties . ' !important;}';
640 $classes[ $name ] = $class_name;
641 }
642 }
643 $settings['cssClassNames'] = $classes;
644 }
645
646 $img_path = VISUALIZER_ABSURL . 'images';
647 $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;}";
648 $css .= '</style>';
649
650 $arguments = array( $css, $settings );
651 apply_filters_ref_array( 'visualizer_inline_css', array( &$arguments ) );
652
653 return $arguments;
654 }
655
656 /**
657 * Handles CSS properties that might need special syntax.
658 *
659 * @access private
660 * @param string $property The name of the css property.
661 * @param string $value The value of the css property.
662 */
663 private function handle_css_property( $property, $value ) {
664 if ( empty( $property ) || empty( $value ) ) {
665 return '';
666 }
667
668 switch ( $property ) {
669 case 'transform':
670 $value = 'rotate(' . $value . 'deg)';
671 break;
672 }
673 return $property . ': ' . $value;
674 }
675
676 /**
677 * Determines if charts have been created of the particular chart type.
678 */
679 protected static function hasChartType( $type ) {
680 $args = array(
681 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
682 'fields' => 'ids',
683 'post_status' => 'publish',
684 'meta_query' => array(
685 array(
686 'key' => Visualizer_Plugin::CF_CHART_TYPE,
687 'value' => $type,
688 ),
689 ),
690 );
691
692 $q = new WP_Query( $args );
693 return $q->found_posts > 0;
694 }
695
696 /**
697 * Determines how many charts have been created.
698 */
699 protected static function numberOfCharts() {
700 $args = array(
701 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
702 'fields' => 'ids',
703 'post_status' => 'publish',
704 'posts_per_page' => 300,
705 );
706
707 $q = new WP_Query( $args );
708 return $q->found_posts;
709 }
710
711 /**
712 * Checks if the PRO version is active.
713 *
714 * @since 3.3.0
715 */
716 public static function is_pro() {
717 // versions of pro before 1.9.0 will use the constant VISUALIZER_PRO
718 // versions of pro 1.9.0 onwards will use the filter
719 return apply_filters( 'visualizer_is_pro', VISUALIZER_PRO );
720 }
721
722 /**
723 * Checks if the PRO version is older than a particular version.
724 *
725 * @since 3.3.0
726 */
727 public static function is_pro_older_than( $version ) {
728 return version_compare( VISUALIZER_PRO_VERSION, $version, '<' );
729 }
730
731 /**
732 * Should we show some specific feature on the basis of the version?
733 *
734 * @since 3.4.0
735 */
736 public static function can_show_feature( $feature ) {
737 switch ( $feature ) {
738 case 'simple-editor':
739 // if user has pro but an older version, then don't load the simple editor functionality
740 // as the select box will not behave as expected because the pro editor's functionality will supercede.
741 return ! Visualizer_Module::is_pro() || ! Visualizer_Module::is_pro_older_than( '1.9.2' );
742 }
743 return false;
744 }
745
746 /**
747 * Gets the features for the provided license type.
748 */
749 public static final function get_features_for_license( $plan ) {
750 $is_new_personal = apply_filters( 'visualizer_is_new_personal', false );
751 switch ( $plan ) {
752 case 1:
753 $features = array( 'import-wp', 'import-wc-report', 'import-file', 'import-url' );
754 if ( ! $is_new_personal ) {
755 $features[] = 'db-query';
756 }
757 return $features;
758 case 2:
759 $features = array( 'schedule-chart', 'chart-permissions', 'import-chart', 'data-filter-configuration', 'frontend-actions' );
760 if ( $is_new_personal ) {
761 $features[] = 'db-query';
762 }
763 return $features;
764 }
765 }
766
767 /**
768 * Gets the chart content after common manipulations.
769 */
770 public static function get_chart_data( $chart, $type, $run_filter = true ) {
771 // change HTML entities
772 $post_content = html_entity_decode( htmlentities( $chart->post_content ) );
773 $post_content = preg_replace_callback(
774 '!s:(\d+):"(.*?)";!s',
775 function ( $matches ) {
776 if ( isset( $matches[2] ) ) {
777 return 's:' . strlen( $matches[2] ) . ':"' . $matches[2] . '";';
778 }
779 },
780 $post_content
781 );
782 $data = unserialize( $post_content );
783 $altered = array();
784 if ( ! empty( $data ) ) {
785 foreach ( $data as $index => $array ) {
786 if ( ! is_array( $index ) && is_array( $array ) ) {
787 foreach ( $array as &$datum ) {
788 if ( is_string( $datum ) ) {
789 $datum = stripslashes( $datum );
790 }
791 }
792 $altered[ $index ] = $array;
793 }
794 }
795 }
796 // if something goes wrong and the end result is empty, be safe and use the original data
797 if ( empty( $altered ) ) {
798 $altered = $data;
799 }
800 if ( $run_filter ) {
801 return apply_filters( Visualizer_Plugin::FILTER_GET_CHART_DATA, $altered, $chart->ID, $type );
802 }
803 return $altered;
804 }
805
806 /**
807 * Get chart image.
808 *
809 * @param object $chart Chart data.
810 * @return string
811 */
812 public function _getImage( $chart = null ) {
813 $image = '';
814 if ( $chart ) {
815 $chart_image = get_post_meta( $chart->ID, Visualizer_Plugin::CF_CHART_IMAGE, true );
816 if ( ! empty( $chart_image ) ) {
817 $image = wp_get_attachment_image( $chart_image, 'full' );
818 }
819 }
820 return array(
821 'csv' => $image,
822 );
823 }
824
825 /**
826 * Filter chart title if visualizer post type.
827 *
828 * @param object $query WP Query object.
829 * @return void
830 */
831 public function PreGetPosts( $query ) {
832 if ( ! $query->is_main_query() ) {
833 $post_type = $query->get( 'post_type' );
834 if ( 'visualizer' === $post_type ) {
835 $this->_addFilter( Visualizer_Plugin::FILTER_CHART_TITLE, 'filterChartTitle', 10, 2 );
836 }
837 }
838 }
839
840 /**
841 * Filter chart title.
842 *
843 * @access public
844 * @param string $post_title Post title.
845 * @param int $post_id Post ID.
846 * @return string
847 */
848 public function filterChartTitle( $post_title, $post_id ) {
849 $post_type = get_post_type( $post_id );
850 $post_title = trim( $post_title );
851 if ( 'visualizer' === $post_type && 'Visualization' === $post_title ) {
852 return sprintf( '%s #%d', $post_title, $post_id );
853 }
854 return $post_title;
855 }
856 }
857