PluginProbe
TablePress – Tables in WordPress made easy / 1.12
TablePress – Tables in WordPress made easy v1.12
3.3.4 3.3.3 3.3.2 3.3.1 trunk 1.12 1.14 1.9.2 2.0.4 2.1.7 2.1.8 2.2 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.3 2.3.1 2.3.2 2.4 2.4.1 2.4.2 2.4.3 2.4.4 All 44 releases
tablepress / classes / class-tablepress.php

class-tablepress.php in TablePress – Tables in WordPress made easy 1.12, at classes/class-tablepress.php

477 lines 15.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * TablePress Class
4 *
5 * @package TablePress
6 * @author Tobias Bäthge
7 * @since 1.0.0
8 */
9
10 // Prohibit direct script loading.
11 defined( 'ABSPATH' ) || die( 'No direct script access allowed!' );
12
13 /**
14 * TablePress class
15 * @package TablePress
16 * @author Tobias Bäthge
17 * @since 1.0.0
18 */
19 abstract class TablePress {
20
21 /**
22 * TablePress version.
23 *
24 * Increases whenever a new plugin version is released.
25 *
26 * @since 1.0.0
27 * @const string
28 */
29 const version = '1.12';
30
31 /**
32 * TablePress internal plugin version ("options scheme" version).
33 *
34 * Increases whenever the scheme for the plugin options changes, or on a plugin update.
35 *
36 * @since 1.0.0
37 * @const int
38 */
39 const db_version = 41;
40
41 /**
42 * TablePress "table scheme" (data format structure) version.
43 *
44 * Increases whenever the scheme for a $table changes,
45 * used to be able to update plugin options and table scheme independently.
46 *
47 * @since 1.0.0
48 * @const int
49 */
50 const table_scheme_version = 3;
51
52 /**
53 * Instance of the Options Model.
54 *
55 * @since 1.3.0
56 * @var TablePress_Options_Model
57 */
58 public static $model_options;
59
60 /**
61 * Instance of the Table Model.
62 *
63 * @since 1.3.0
64 * @var TablePress_Table_Model
65 */
66 public static $model_table;
67
68 /**
69 * Instance of the controller.
70 *
71 * @since 1.0.0
72 * @var TablePress_*_Controller
73 */
74 public static $controller;
75
76 /**
77 * Name of the Shortcode to show a TablePress table.
78 *
79 * Should only be modified through the filter hook 'tablepress_table_shortcode'.
80 *
81 * @since 1.0.0
82 * @var string
83 */
84 public static $shortcode = 'table';
85
86 /**
87 * Name of the Shortcode to show extra information of a TablePress table.
88 *
89 * Should only be modified through the filter hook 'tablepress_table_info_shortcode'.
90 *
91 * @since 1.0.0
92 * @var string
93 */
94 public static $shortcode_info = 'table-info';
95
96 /**
97 * Start-up TablePress (run on WordPress "init") and load the controller for the current state.
98 *
99 * @since 1.0.0
100 */
101 public static function run() {
102 /**
103 * Fires when TablePress is loaded.
104 *
105 * @since 1.0.0
106 */
107 do_action( 'tablepress_run' );
108
109 // Exit early if TablePress doesn't have to be loaded.
110 if ( ( 'wp-login.php' === basename( $_SERVER['SCRIPT_FILENAME'] ) ) // Login screen
111 || ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST )
112 || ( defined( 'DOING_CRON' ) && DOING_CRON ) ) {
113 return;
114 }
115
116 // Check if minimum requirements are fulfilled, currently WordPress 5.3.
117 include( ABSPATH . WPINC . '/version.php' ); // Include an unmodified $wp_version.
118 if ( version_compare( str_replace( '-src', '', $wp_version ), '5.3', '<' ) ) {
119 // Show error notice to admins, if WP is not installed in the minimum required version, in which case TablePress will not work.
120 if ( current_user_can( 'update_plugins' ) ) {
121 add_action( 'admin_notices', array( 'TablePress', 'show_minimum_requirements_error_notice' ) );
122 }
123 // And exit TablePress.
124 return;
125 }
126
127 /**
128 * Filter the string that is used as the [table] Shortcode.
129 *
130 * @since 1.0.0
131 *
132 * @param string $shortcode The [table] Shortcode string.
133 */
134 self::$shortcode = apply_filters( 'tablepress_table_shortcode', self::$shortcode );
135 /**
136 * Filter the string that is used as the [table-info] Shortcode.
137 *
138 * @since 1.0.0
139 *
140 * @param string $shortcode_info The [table-info] Shortcode string.
141 */
142 self::$shortcode_info = apply_filters( 'tablepress_table_info_shortcode', self::$shortcode_info );
143
144 // Load modals for table and options, to be accessible from everywhere via `TablePress::$model_options` and `TablePress::$model_table`.
145 self::$model_options = self::load_model( 'options' );
146 self::$model_table = self::load_model( 'table' );
147
148 if ( is_admin() ) {
149 $controller = 'admin';
150 if ( wp_doing_ajax() ) {
151 $controller .= '_ajax';
152 }
153 } else {
154 $controller = 'frontend';
155 }
156 self::$controller = self::load_controller( $controller );
157 }
158
159 /**
160 * Load a file with require_once(), after running it through a filter.
161 *
162 * @since 1.0.0
163 *
164 * @param string $file Name of the PHP file with the class.
165 * @param string $folder Name of the folder with $class's $file.
166 */
167 public static function load_file( $file, $folder ) {
168 $full_path = TABLEPRESS_ABSPATH . $folder . '/' . $file;
169 /**
170 * Filter the full path of a file that shall be loaded.
171 *
172 * @since 1.0.0
173 *
174 * @param string $full_path Full path of the file that shall be loaded.
175 * @param string $file File name of the file that shall be loaded.
176 * @param string $folder Folder name of the file that shall be loaded.
177 */
178 $full_path = apply_filters( 'tablepress_load_file_full_path', $full_path, $file, $folder );
179 if ( $full_path ) {
180 require_once $full_path;
181 }
182 }
183
184 /**
185 * Create a new instance of the $class, which is stored in $file in the $folder subfolder
186 * of the plugin's directory.
187 *
188 * @since 1.0.0
189 *
190 * @param string $class Name of the class.
191 * @param string $file Name of the PHP file with the class.
192 * @param string $folder Name of the folder with $class's $file.
193 * @param mixed $params Optional. Parameters that are passed to the constructor of $class.
194 * @return object Initialized instance of the class.
195 */
196 public static function load_class( $class, $file, $folder, $params = null ) {
197 /**
198 * Filter name of the class that shall be loaded.
199 *
200 * @since 1.0.0
201 *
202 * @param string $class Name of the class that shall be loaded.
203 */
204 $class = apply_filters( 'tablepress_load_class_name', $class );
205 if ( ! class_exists( $class, false ) ) {
206 self::load_file( $file, $folder );
207 }
208 $the_class = new $class( $params );
209 return $the_class;
210 }
211
212 /**
213 * Create a new instance of the $model, which is stored in the "models" subfolder.
214 *
215 * @since 1.0.0
216 *
217 * @param string $model Name of the model.
218 * @return object Instance of the initialized model.
219 */
220 public static function load_model( $model ) {
221 // Model Base Class.
222 self::load_file( 'class-model.php', 'classes' );
223 // Make first letter uppercase for a better looking naming pattern.
224 $ucmodel = ucfirst( $model );
225 $the_model = self::load_class( "TablePress_{$ucmodel}_Model", "model-{$model}.php", 'models' );
226 return $the_model;
227 }
228
229 /**
230 * Create a new instance of the $view, which is stored in the "views" subfolder, and set it up with $data.
231 *
232 * @since 1.0.0
233 *
234 * @param string $view Name of the view to load.
235 * @param array $data Optional. Parameters/PHP variables that shall be available to the view.
236 * @return object Instance of the initialized view, already set up, just needs to be rendered.
237 */
238 public static function load_view( $view, array $data = array() ) {
239 // View Base Class.
240 self::load_file( 'class-view.php', 'classes' );
241 // Make first letter uppercase for a better looking naming pattern.
242 $ucview = ucfirst( $view );
243 $the_view = self::load_class( "TablePress_{$ucview}_View", "view-{$view}.php", 'views' );
244 $the_view->setup( $view, $data );
245 return $the_view;
246 }
247
248 /**
249 * Create a new instance of the $controller, which is stored in the "controllers" subfolder.
250 *
251 * @since 1.0.0
252 *
253 * @param string $controller Name of the controller.
254 * @return object Instance of the initialized controller.
255 */
256 public static function load_controller( $controller ) {
257 // Controller Base Class.
258 self::load_file( 'class-controller.php', 'classes' );
259 // Make first letter uppercase for a better looking naming pattern.
260 $uccontroller = ucfirst( $controller );
261 $the_controller = self::load_class( "TablePress_{$uccontroller}_Controller", "controller-{$controller}.php", 'controllers' );
262 return $the_controller;
263 }
264
265 /**
266 * Generate the complete nonce string, from the nonce base, the action and an item, e.g. tablepress_delete_table_3.
267 *
268 * @since 1.0.0
269 *
270 * @param string $action Action for which the nonce is needed.
271 * @param string|bool $item Optional. Item for which the action will be performed, like "table".
272 * @return string The resulting nonce string.
273 */
274 public static function nonce( $action, $item = false ) {
275 $nonce = "tablepress_{$action}";
276 if ( $item ) {
277 $nonce .= "_{$item}";
278 }
279 return $nonce;
280 }
281
282 /**
283 * Check whether a nonce string is valid.
284 *
285 * @since 1.0.0
286 *
287 * @param string $action Action for which the nonce should be checked.
288 * @param string|bool $item Optional. Item for which the action should be performed, like "table".
289 * @param string $query_arg Optional. Name of the nonce query string argument in $_POST.
290 * @param bool $ajax Whether the nonce comes from an AJAX request.
291 */
292 public static function check_nonce( $action, $item = false, $query_arg = '_wpnonce', $ajax = false ) {
293 $nonce_action = self::nonce( $action, $item );
294 if ( $ajax ) {
295 check_ajax_referer( $nonce_action, $query_arg );
296 } else {
297 check_admin_referer( $nonce_action, $query_arg );
298 }
299 }
300
301 /**
302 * Calculate the column index (number) of a column header string (example: A is 1, AA is 27, ...).
303 *
304 * For the opposite, @see number_to_letter().
305 *
306 * @since 1.0.0
307 *
308 * @param string $column Column string.
309 * @return int $number Column number, 1-based.
310 */
311 public static function letter_to_number( $column ) {
312 $column = strtoupper( $column );
313 $count = strlen( $column );
314 $number = 0;
315 for ( $i = 0; $i < $count; $i++ ) {
316 $number += ( ord( $column[ $count - 1 - $i ] ) - 64 ) * pow( 26, $i );
317 }
318 return $number;
319 }
320
321 /**
322 * "Calculate" the column header string of a column index (example: 2 is B, AB is 28, ...).
323 *
324 * For the opposite, @see letter_to_number().
325 *
326 * @since 1.0.0
327 *
328 * @param int $number Column number, 1-based.
329 * @return string $column Column string.
330 */
331 public static function number_to_letter( $number ) {
332 $column = '';
333 while ( $number > 0 ) {
334 $column = chr( 65 + ( ( $number - 1 ) % 26 ) ) . $column;
335 $number = floor( ( $number - 1 ) / 26 );
336 }
337 return $column;
338 }
339
340 /**
341 * Get a nice looking date and time string from the mySQL format of datetime strings for output.
342 *
343 * @since 1.0.0
344 *
345 * @param string $datetime DateTime string in mySQL format or a Unix timestamp.
346 * @param string $type Optional. Type of $datetime, 'mysql' or 'timestamp'.
347 * @param string $separator Optional. Separator between date and time.
348 * @return string Nice looking string with the date and time.
349 */
350 public static function format_datetime( $datetime, $type = 'mysql', $separator = ' ' ) {
351 // @TODO: Maybe change from using the stored WP Options to translated date/time schemes, like in https://core.trac.wordpress.org/changeset/35811.
352 if ( 'mysql' === $type ) {
353 return mysql2date( get_option( 'date_format' ), $datetime ) . $separator . mysql2date( get_option( 'time_format' ), $datetime );
354 } else {
355 return date_i18n( get_option( 'date_format' ), $datetime ) . $separator . date_i18n( get_option( 'time_format' ), $datetime );
356 }
357 }
358
359 /**
360 * Get the name from a WP user ID (used to store information on last editor of a table).
361 *
362 * @since 1.0.0
363 *
364 * @param int $user_id WP user ID.
365 * @return string Nickname of the WP user with the $user_id.
366 */
367 public static function get_user_display_name( $user_id ) {
368 $user = get_userdata( $user_id );
369 return ( $user && isset( $user->display_name ) ) ? $user->display_name : sprintf( '<em>%s</em>', __( 'unknown', 'tablepress' ) );
370 }
371
372 /**
373 * Sanitizes a CSS class to ensure it only contains valid characters.
374 *
375 * Strips the string down to A-Z, a-z, 0-9, :, _, -.
376 * This is an extension to WP's `sanitize_html_class()`, to also allow `:` which are used in some CSS frameworks.
377 *
378 * @since 1.11.0
379 *
380 * @param string $class The CSS class name to be sanitized.
381 * @return string The sanitized CSS class.
382 */
383 public static function sanitize_css_class( $class ) {
384 // Strip out any %-encoded octets.
385 $sanitized_class = preg_replace( '|%[a-fA-F0-9][a-fA-F0-9]|', '', $class );
386 // Limit to A-Z, a-z, 0-9, ':', '_', and '-'.
387 $sanitized_class = preg_replace( '/[^A-Za-z0-9:_-]/', '', $sanitized_class );
388 return $sanitized_class;
389 }
390
391 /**
392 * Generate the action URL, to be used as a link within the plugin (e.g. in the submenu navigation or List of Tables).
393 *
394 * @since 1.0.0
395 *
396 * @param array $params Optional. Parameters to form the query string of the URL.
397 * @param bool $add_nonce Optional. Whether the URL shall be nonced by WordPress.
398 * @param string $target Optional. Target File, e.g. "admin-post.php" for POST requests.
399 * @return string The URL for the given parameters (already run through esc_url() with $add_nonce === true!).
400 */
401 public static function url( array $params = array(), $add_nonce = false, $target = '' ) {
402
403 // Default action is "list", if no action given.
404 if ( ! isset( $params['action'] ) ) {
405 $params['action'] = 'list';
406 }
407 $nonce_action = $params['action'];
408
409 if ( $target ) {
410 $params['action'] = "tablepress_{$params['action']}";
411 } else {
412 $params['page'] = 'tablepress';
413 // Top-level parent page needs special treatment for better action strings.
414 if ( self::$controller->is_top_level_page ) {
415 $target = 'admin.php';
416 if ( ! in_array( $params['action'], array( 'list', 'edit' ), true ) ) {
417 $params['page'] = "tablepress_{$params['action']}";
418 }
419 if ( ! in_array( $params['action'], array( 'edit' ), true ) ) {
420 $params['action'] = false;
421 }
422 } else {
423 $target = self::$controller->parent_page;
424 }
425 }
426
427 // $default_params also determines the order of the values in the query string.
428 $default_params = array(
429 'page' => false,
430 'action' => false,
431 'item' => false,
432 );
433 $params = array_merge( $default_params, $params );
434
435 $url = add_query_arg( $params, admin_url( $target ) );
436 if ( $add_nonce ) {
437 $url = wp_nonce_url( $url, self::nonce( $nonce_action, $params['item'] ) ); // wp_nonce_url() does esc_html()
438 }
439 return $url;
440 }
441
442 /**
443 * Create a redirect URL from the $target_parameters and redirect the user.
444 *
445 * @since 1.0.0
446 *
447 * @param array $params Optional. Parameters from which the target URL is constructed.
448 * @param bool $add_nonce Optional. Whether the URL shall be nonced by WordPress.
449 */
450 public static function redirect( array $params = array(), $add_nonce = false ) {
451 $redirect = self::url( $params );
452 if ( $add_nonce ) {
453 if ( ! isset( $params['item'] ) ) {
454 $params['item'] = false;
455 }
456 // Don't use wp_nonce_url(), as that uses esc_html().
457 $redirect = add_query_arg( '_wpnonce', wp_create_nonce( self::nonce( $params['action'], $params['item'] ) ), $redirect );
458 }
459 wp_redirect( $redirect );
460 exit;
461 }
462
463 /**
464 * Show an error notice to admins, if TablePress's minimum requirements are not reached.
465 *
466 * @since 1.0.0
467 */
468 public static function show_minimum_requirements_error_notice() {
469 // Message is not translated as it is shown on every admin screen, for which we don't want to load translations.
470 echo '<div class="notice notice-error form-invalid"><p>' .
471 '<strong>Attention:</strong> ' .
472 'The installed version of WordPress is too old for the TablePress plugin! TablePress requires an up-to-date version! <strong>Please <a href="' . esc_url( admin_url( 'update-core.php' ) ) . '">update your WordPress installation</a></strong>!' .
473 "</p></div>\n";
474 }
475
476 } // class TablePress
477