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

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