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

449 lines 13.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
70 }
71
72 /**
73 * Registers an action hook.
74 *
75 * @since 1.0.0
76 * @uses add_action() To register action hook.
77 *
78 * @access protected
79 * @param string $tag The name of the action to which the $method is hooked.
80 * @param string $method The name of the method to be called.
81 * @param bool $methodClass The root of the method.
82 * @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.
83 * @param int $accepted_args optional. The number of arguments the function accept (default 1).
84 * @return Visualizer_Module
85 */
86 protected function _addAction( $tag, $method, $methodClass = null, $priority = 10, $accepted_args = 1 ) {
87 add_action( $tag, array( $methodClass ? $methodClass : $this, $method ), $priority, $accepted_args );
88 return $this;
89 }
90
91 /**
92 * Registers AJAX action hook.
93 *
94 * @since 1.0.0
95 *
96 * @access public
97 * @param string $tag The name of the AJAX action to which the $method is hooked.
98 * @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.
99 * @param bool $methodClass The root of the method.
100 * @param boolean $private Optional. Determines if we should register hook for logged in users.
101 * @param boolean $public Optional. Determines if we should register hook for not logged in users.
102 * @return Visualizer_Module
103 */
104 protected function _addAjaxAction( $tag, $method = '', $methodClass = null, $private = true, $public = false ) {
105 if ( $private ) {
106 $this->_addAction( 'wp_ajax_' . $tag, $method, $methodClass );
107 }
108
109 if ( $public ) {
110 $this->_addAction( 'wp_ajax_nopriv_' . $tag, $method, $methodClass );
111 }
112
113 return $this;
114 }
115
116 /**
117 * Registers a filter hook.
118 *
119 * @since 1.0.0
120 * @uses add_filter() To register filter hook.
121 *
122 * @access protected
123 * @param string $tag The name of the filter to hook the $method to.
124 * @param type $method The name of the method to be called when the filter is applied.
125 * @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.
126 * @param int $accepted_args optional. The number of arguments the function accept (default 1).
127 * @return Visualizer_Module
128 */
129 protected function _addFilter( $tag, $method, $priority = 10, $accepted_args = 1 ) {
130 add_filter( $tag, array( $this, $method ), $priority, $accepted_args );
131 return $this;
132 }
133
134 /**
135 * Registers a hook for shortcode tag.
136 *
137 * @since 1.0.0
138 * @uses add_shortcode() To register shortcode hook.
139 *
140 * @access protected
141 * @param string $tag Shortcode tag to be searched in post content.
142 * @param string $method Hook to run when shortcode is found.
143 * @return Visualizer_Module
144 */
145 protected function _addShortcode( $tag, $method ) {
146 add_shortcode( $tag, array( $this, $method ) );
147 return $this;
148 }
149
150 /**
151 * Extracts the data for a chart and prepares it for the given type.
152 *
153 * @access public
154 * @param int $chart_id The chart id.
155 * @param string $type The exported type.
156 */
157 public function _getDataAs( $chart_id, $type ) {
158 $final = null;
159 $success = false;
160 if ( $chart_id ) {
161 $chart = get_post( $chart_id );
162 $success = $chart && $chart->post_type == Visualizer_Plugin::CPT_VISUALIZER;
163 }
164 if ( $success ) {
165 $settings = get_post_meta( $chart_id, Visualizer_Plugin::CF_SETTINGS, true );
166 $rows = array();
167 $series = get_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, true );
168 $data = unserialize( $chart->post_content );
169 if ( ! empty( $series ) ) {
170 $row = array();
171 foreach ( $series as $array ) {
172 $row[] = $array['label'];
173 }
174 $rows[] = $row;
175 $row = array();
176 foreach ( $series as $array ) {
177 $row[] = $array['type'];
178 }
179 $rows[] = $row;
180 }
181 if ( ! empty( $data ) ) {
182 foreach ( $data as $array ) {
183 // ignore strings
184 if ( ! is_array( $array ) ) {
185 continue;
186 }
187 // if this is an array of arrays...
188 if ( is_array( $array[0] ) ) {
189 foreach ( $array as $arr ) {
190 $rows[] = $arr;
191 }
192 } else {
193 // just an array
194 $rows[] = $array;
195 }
196 }
197 }
198
199 $filename = isset( $settings['title'] ) && ! empty( $settings['title'] ) ? $settings['title'] : 'visualizer#' . $chart_id;
200
201 switch ( $type ) {
202 case 'csv':
203 $final = $this->_getCSV( $rows, $filename );
204 break;
205 case 'xls':
206 $final = $this->_getExcel( $rows, $filename );
207 break;
208 case 'print':
209 $final = $this->_getHTML( $rows );
210 break;
211 }
212 }
213 return $final;
214 }
215
216 /**
217 * Prepares a CSV.
218 *
219 * @access private
220 * @param array $rows The array of data.
221 * @param string $filename The name of the file to use.
222 */
223 private function _getCSV( $rows, $filename ) {
224 $filename .= '.csv';
225
226 $fp = tmpfile();
227 // support for MS Excel
228 fprintf( $fp, $bom = ( chr( 0xEF ) . chr( 0xBB ) . chr( 0xBF ) ) );
229 foreach ( $rows as $row ) {
230 fputcsv( $fp, $row );
231 }
232 rewind( $fp );
233 $csv = '';
234 while ( ( $array = fgetcsv( $fp ) ) !== false ) {
235 if ( strlen( $csv ) > 0 ) {
236 $csv .= PHP_EOL;
237 }
238 $csv .= implode( ',', $array );
239 }
240 fclose( $fp );
241
242 return array(
243 'csv' => $csv,
244 'name' => $filename,
245 );
246 }
247
248 /**
249 * Prepares an Excel file.
250 *
251 * @access private
252 * @param array $rows The array of data.
253 * @param string $filename The name of the file to use.
254 */
255 private function _getExcel( $rows, $filename ) {
256 // PHPExcel does not like sheet names longer than 31 characters.
257 $chart = substr( $filename, 0, 30 );
258 $filename .= '.xlsx';
259
260 $xlsData = '';
261 if ( class_exists( 'PHPExcel' ) ) {
262 $doc = new PHPExcel();
263 $doc->getActiveSheet()->fromArray( $rows, null, 'A1' );
264 $doc->getActiveSheet()->setTitle( sanitize_title( $chart ) );
265 $doc = apply_filters( 'visualizer_excel_doc', $doc );
266 $writer = PHPExcel_IOFactory::createWriter( $doc, 'Excel2007' );
267 ob_start();
268 $writer->save( 'php://output' );
269 $xlsData = ob_get_contents();
270 ob_end_clean();
271 } else {
272 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, 'Class PHPExcel does not exist!', 'error', __FILE__, __LINE__ );
273 error_log( 'Class PHPExcel does not exist!' );
274 }
275 return array(
276 'csv' => 'data:application/vnd.ms-excel;base64,' . base64_encode( $xlsData ),
277 'name' => $filename,
278 );
279 }
280
281 /**
282 * Prepares an HTML table.
283 *
284 * @access private
285 * @param array $rows The array of data.
286 */
287 private function _getHTML( $rows ) {
288 $css = '
289 table.visualizer-print {
290 border-collapse: collapse;
291 }
292 table.visualizer-print, table.visualizer-print th, table.visualizer-print td {
293 border: 1px solid #000;
294 }
295 ';
296 $html = '';
297 $html .= '
298 <html>
299 <head>
300 <style>
301 ' . apply_filters( 'visualizer_print_css', $css ) . '
302 </style>
303 </head>
304 <body>';
305
306 $table = '<table class="visualizer-print">';
307 $index = 0;
308 foreach ( $rows as $row ) {
309 $table .= '<tr>';
310 foreach ( $row as $col ) {
311 if ( $index === 0 ) {
312 $table .= '<th>' . $col . '</th>';
313 } else {
314 $table .= '<td>' . $col . '</td>';
315 }
316 }
317 $table .= '</tr>';
318 $index++;
319 }
320 $table .= '</table>';
321
322 $html .= apply_filters( 'visualizer_print_table', $table ) . '
323 </body>
324 </html>';
325 return array(
326 'csv' => $html,
327 );
328 }
329
330 /**
331 * Undo revisions for the chart, and if necessary, restore the earliest version.
332 *
333 * @return bool If any revisions were found.
334 */
335 public final function undoRevisions( $chart_id, $restore = false ) {
336 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'undoRevisions for %d with%s restore', $chart_id, ( $restore ? '' : 'out' ) ), 'debug', __FILE__, __LINE__ );
337
338 $revisions = wp_get_post_revisions( $chart_id, array( 'order' => 'ASC' ) );
339 if ( count( $revisions ) > 1 ) {
340 $revision_ids = array_keys( $revisions );
341
342 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'found %d revisions = %s', count( $revisions ), print_r( $revision_ids, true ) ), 'debug', __FILE__, __LINE__ );
343
344 // when we restore, a new revision is likely to be created. so, let's disable revisions for the time being.
345 add_filter( 'wp_revisions_to_keep', '__return_false' );
346
347 if ( $restore ) {
348 // restore to the oldest one i.e. the first one.
349 wp_restore_post_revision( array_shift( $revision_ids ) );
350 }
351
352 // delete all revisions.
353 foreach ( $revision_ids as $id ) {
354 wp_delete_post_revision( $id );
355 }
356
357 return true;
358 }
359 return false;
360 }
361
362 /**
363 * If existing revisions exist for the chart, restore the earliest version and then create a new revision to initiate editing.
364 */
365 public final function handleExistingRevisions( $chart_id, $chart ) {
366 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'handleExistingRevisions for %d', $chart_id ), 'debug', __FILE__, __LINE__ );
367
368 // undo revisions.
369 $revisions_found = $this->undoRevisions( $chart_id, true );
370
371 // create revision for the edit action.
372 wp_save_post_revision( $chart_id );
373
374 if ( $revisions_found ) {
375 // fetch chart data again in case it was updated by an earlier revision.
376 $chart = get_post( $chart_id );
377 }
378 return $chart;
379 }
380
381 /**
382 * Returns the language of the locale.
383 *
384 * @access protected
385 */
386 protected function get_language() {
387 $locale = get_locale();
388 if ( empty( $locale ) ) {
389 return '';
390 }
391 $array = explode( '_', $locale );
392 if ( count( $array ) < 2 ) {
393 return '';
394 }
395 return reset( $array );
396 }
397
398 /**
399 * Gets/creates the JS where user-specific customizations can be/have been added.
400 */
401 protected function get_user_customization_js() {
402 // use this as the JS file in case we are not able to create the file in uploads.
403 $default = VISUALIZER_ABSURL . 'js/customization.js';
404
405 $uploads = wp_get_upload_dir();
406 $specific = $uploads['baseurl'] . '/visualizer/customization.js';
407
408 // for testing on user sites (before we send them the correctly customized file).
409 if ( VISUALIZER_TEST_JS_CUSTOMIZATION ) {
410 return $default;
411 }
412
413 require_once( ABSPATH . 'wp-admin/includes/file.php' );
414 WP_Filesystem();
415 global $wp_filesystem;
416
417 $dir = $wp_filesystem->wp_content_dir() . 'uploads/visualizer';
418 $file = $wp_filesystem->wp_content_dir() . 'uploads/visualizer/customization.js';
419
420 if ( $wp_filesystem->is_readable( $file ) ) {
421 return $specific;
422 }
423
424 if ( $wp_filesystem->exists( $file ) && ! $wp_filesystem->is_readable( $file ) ) {
425 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Unable to read file %s', $file ), 'error', __FILE__, __LINE__ );
426 return $default;
427 }
428
429 if ( ! $wp_filesystem->exists( $dir ) ) {
430 if ( ( $done = $wp_filesystem->mkdir( $dir ) ) === false ) {
431 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Unable to create directory %s', $dir ), 'error', __FILE__, __LINE__ );
432 return $default;
433 }
434 }
435
436 // if file does not exist, copy.
437 if ( ! $wp_filesystem->exists( $file ) ) {
438 $src = str_replace( ABSPATH, $wp_filesystem->abspath(), VISUALIZER_ABSPATH . '/js/customization.js' );
439 if ( ( $done = $wp_filesystem->copy( $src, $file ) ) === false ) {
440 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Unable to copy file %s to %s', $src, $file ), 'error', __FILE__, __LINE__ );
441 return $default;
442 }
443 }
444
445 return $specific;
446 }
447
448 }
449