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

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