PluginProbe
TablePress – Tables in WordPress made easy / 3.0
TablePress – Tables in WordPress made easy v3.0
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
← All changes | classes/class-tablepress.php +366 -85 2.1.73.0 View file →
@@ -26,9 +26,9 @@
26 26 *
27 27 * @since 1.0.0
28 28 * @const string
29 29 */
30 - const version = '2.1.7'; // phpcs:ignore Generic.NamingConventions.UpperCaseConstantName.ClassConstantNotUpperCase
30 + public const version = '3.0'; // phpcs:ignore Generic.NamingConventions.UpperCaseConstantName.ClassConstantNotUpperCase
31 31
32 32 /**
33 33 * TablePress internal plugin version ("options scheme" version).
34 34 *
@@ -36,9 +36,9 @@
36 36 *
37 37 * @since 1.0.0
38 38 * @const int
39 39 */
40 - const db_version = 63; // phpcs:ignore Generic.NamingConventions.UpperCaseConstantName.ClassConstantNotUpperCase
40 + public const db_version = 96; // phpcs:ignore Generic.NamingConventions.UpperCaseConstantName.ClassConstantNotUpperCase
41 41
42 42 /**
43 43 * TablePress "table scheme" (data format structure) version.
44 44 *
@@ -47,33 +47,30 @@
47 47 *
48 48 * @since 1.0.0
49 49 * @const int
50 50 */
51 - const table_scheme_version = 3; // phpcs:ignore Generic.NamingConventions.UpperCaseConstantName.ClassConstantNotUpperCase
51 + public const table_scheme_version = 3; // phpcs:ignore Generic.NamingConventions.UpperCaseConstantName.ClassConstantNotUpperCase
52 52
53 53 /**
54 54 * Instance of the Options Model.
55 55 *
56 56 * @since 1.3.0
57 - * @var TablePress_Options_Model
58 57 */
59 - public static $model_options;
58 + public static \TablePress_Options_Model $model_options;
60 59
61 60 /**
62 61 * Instance of the Table Model.
63 62 *
64 63 * @since 1.3.0
65 - * @var TablePress_Table_Model
66 64 */
67 - public static $model_table;
65 + public static \TablePress_Table_Model $model_table;
68 66
69 67 /**
70 68 * Instance of the controller.
71 69 *
72 70 * @since 1.0.0
73 - * @var TablePress_*_Controller
74 71 */
75 - public static $controller;
72 + public static \TablePress_Frontend_Controller $controller;
76 73
77 74 /**
78 75 * Name of the Shortcode to show a TablePress table.
79 76 *
@@ -79,11 +76,10 @@
79 76 *
80 77 * Should only be modified through the filter hook 'tablepress_table_shortcode'.
81 78 *
82 79 * @since 1.0.0
83 - * @var string
84 80 */
85 - public static $shortcode = 'table';
81 + public static string $shortcode = 'table';
86 82
87 83 /**
88 84 * Name of the Shortcode to show extra information of a TablePress table.
89 85 *
@@ -89,19 +85,18 @@
89 85 *
90 86 * Should only be modified through the filter hook 'tablepress_table_info_shortcode'.
91 87 *
92 88 * @since 1.0.0
93 - * @var string
94 89 */
95 - public static $shortcode_info = 'table-info';
90 + public static string $shortcode_info = 'table-info';
96 91
97 92 /**
98 93 * List of TablePress premium modules.
99 94 *
100 95 * @since 2.1.0
101 - * @var array
96 + * @var array<string, array{name: string, description: string, category: string, class: string, incompatible_classes: string[], minimum_plan: string, default_active: bool}>
102 97 */
103 - public static $modules = array();
98 + public static array $modules = array();
104 99
105 100 /**
106 101 * Start-up TablePress (run on WordPress "init") and load the controller for the current state.
107 102 *
@@ -106,9 +101,9 @@
106 101 * Start-up TablePress (run on WordPress "init") and load the controller for the current state.
107 102 *
108 103 * @since 1.0.0
109 104 */
110 - public static function run() {
105 + public static function run(): void {
111 106 /**
112 107 * Fires before TablePress is loaded.
113 108 *
114 109 * The `tablepress_loaded` action hook might be a better choice in most situations, as TablePress options will then be available.
@@ -116,19 +111,8 @@
116 111 * @since 1.0.0
117 112 */
118 113 do_action( 'tablepress_run' );
119 114
120 - // Check if minimum requirements are fulfilled, currently WordPress 5.8.
121 - include ABSPATH . WPINC . '/version.php'; // Include an unmodified $wp_version.
122 - if ( version_compare( str_replace( '-src', '', $wp_version ), '5.8', '<' ) ) {
123 - // Show error notice to admins, if WP is not installed in the minimum required version, in which case TablePress will not work.
124 - if ( current_user_can( 'update_plugins' ) ) {
125 - add_action( 'admin_notices', array( 'TablePress', 'show_minimum_requirements_error_notice' ) );
126 - }
127 - // And exit TablePress.
128 - return;
129 - }
130 -
131 115 /**
132 116 * Filters the string that is used as the [table] Shortcode.
133 117 *
134 118 * @since 1.0.0
@@ -169,9 +153,9 @@
169 153
170 154 if ( is_admin() ) {
171 155 $controller = 'admin';
172 156 if ( wp_doing_ajax() ) {
173 - $controller .= '_ajax';
157 + $controller = 'admin_ajax';
174 158 }
175 159 self::load_controller( $controller );
176 160 }
177 161 // Load the frontend controller in all scenarios, so that Shortcode render functions are always available.
@@ -176,8 +160,13 @@
176 160 }
177 161 // Load the frontend controller in all scenarios, so that Shortcode render functions are always available.
178 162 self::$controller = self::load_controller( 'frontend' );
179 163
164 + // Add filters and actions for the integration into the WP WXR exporter and importer.
165 + add_action( 'wp_import_insert_post', array( TablePress::$model_table, 'add_table_id_on_wp_import' ), 10, 4 ); // phpcs:ignore Squiz.Classes.SelfMemberReference.NotUsed
166 + add_filter( 'wp_import_post_meta', array( TablePress::$model_table, 'prevent_table_id_post_meta_import_on_wp_import' ), 10, 3 ); // phpcs:ignore Squiz.Classes.SelfMemberReference.NotUsed
167 + add_filter( 'wxr_export_skip_postmeta', array( TablePress::$model_table, 'add_table_id_to_wp_export' ), 10, 3 ); // phpcs:ignore Squiz.Classes.SelfMemberReference.NotUsed
168 +
180 169 /**
181 170 * Fires after TablePress is loaded.
182 171 *
183 172 * The `tablepress_run` action hook can be used if code has to run before TablePress is loaded.
@@ -194,9 +183,9 @@
194 183 *
195 184 * @param string $file Name of the PHP file.
196 185 * @param string $folder Name of the folder with the file.
197 186 */
198 - public static function load_file( $file, $folder ) {
187 + public static function load_file( string $file, string $folder ): void {
199 188 $full_path = TABLEPRESS_ABSPATH . $folder . '/' . $file;
200 189 /**
201 190 * Filters the full path of a file that shall be loaded.
202 191 *
@@ -217,15 +206,15 @@
217 206 * of the plugin's directory.
218 207 *
219 208 * @since 1.0.0
220 209 *
221 - * @param string $class_name Name of the class.
222 - * @param string $file Name of the PHP file with the class.
223 - * @param string $folder Name of the folder with $class_name's $file.
224 - * @param mixed $params Optional. Parameters that are passed to the constructor of $class_name.
210 + * @param string $class_name Name of the class.
211 + * @param string $file Name of the PHP file with the class.
212 + * @param string $folder Name of the folder with $class_name's $file.
213 + * @param mixed[]|string|null $params Optional. Parameters that are passed to the constructor of $class_name.
225 214 * @return object Initialized instance of the class.
226 215 */
227 - public static function load_class( $class_name, $file, $folder, $params = null ) {
216 + public static function load_class( string $class_name, string $file, string $folder, /* ?array|string */ $params = null ): object {
228 217 /**
229 218 * Filters name of the class that shall be loaded.
230 219 *
231 220 * @since 1.0.0
@@ -247,9 +236,9 @@
247 236 *
248 237 * @param string $model Name of the model.
249 238 * @return object Instance of the initialized model.
250 239 */
251 - public static function load_model( $model ) {
240 + public static function load_model( string $model ): object {
252 241 // Model Base Class.
253 242 self::load_file( 'class-model.php', 'classes' );
254 243 // Make first letter uppercase for a better looking naming pattern.
255 244 $ucmodel = ucfirst( $model );
@@ -261,13 +250,13 @@
261 250 * Create a new instance of the $view, which is stored in the "views" subfolder, and set it up with $data.
262 251 *
263 252 * @since 1.0.0
264 253 *
265 - * @param string $view Name of the view to load.
266 - * @param array $data Optional. Parameters/PHP variables that shall be available to the view.
254 + * @param string $view Name of the view to load.
255 + * @param array<string, mixed> $data Optional. Parameters/PHP variables that shall be available to the view.
267 256 * @return object Instance of the initialized view, already set up, just needs to be rendered.
268 257 */
269 - public static function load_view( $view, array $data = array() ) {
258 + public static function load_view( string $view, array $data = array() ): object {
270 259 // View Base Class.
271 260 self::load_file( 'class-view.php', 'classes' );
272 261 // Make first letter uppercase for a better looking naming pattern.
273 262 $ucview = ucfirst( $view );
@@ -283,9 +272,9 @@
283 272 *
284 273 * @param string $controller Name of the controller.
285 274 * @return object Instance of the initialized controller.
286 275 */
287 - public static function load_controller( $controller ) {
276 + public static function load_controller( string $controller ): object {
288 277 // Controller Base Class.
289 278 self::load_file( 'class-controller.php', 'classes' );
290 279 // Make first letter uppercase for a better looking naming pattern.
291 280 $uccontroller = ucfirst( $controller );
@@ -297,13 +286,13 @@
297 286 * Generate the complete nonce string, from the nonce base, the action and an item, e.g. tablepress_delete_table_3.
298 287 *
299 288 * @since 1.0.0
300 289 *
301 - * @param string $action Action for which the nonce is needed.
302 - * @param string|bool $item Optional. Item for which the action will be performed, like "table".
290 + * @param string $action Action for which the nonce is needed.
291 + * @param string|false $item Optional. Item for which the action will be performed, like "table". false if no item should be used in the nonce.
303 292 * @return string The resulting nonce string.
304 293 */
305 - public static function nonce( $action, $item = false ) {
294 + public static function nonce( string $action, /* string|false */ $item = false ): string {
306 295 $nonce = "tablepress_{$action}";
307 296 if ( $item ) {
308 297 $nonce .= "_{$item}";
309 298 }
@@ -314,14 +303,14 @@
314 303 * Check whether a nonce string is valid.
315 304 *
316 305 * @since 1.0.0
317 306 *
318 - * @param string $action Action for which the nonce should be checked.
319 - * @param string|bool $item Optional. Item for which the action should be performed, like "table".
320 - * @param string $query_arg Optional. Name of the nonce query string argument in $_POST.
321 - * @param bool $ajax Whether the nonce comes from an AJAX request.
307 + * @param string $action Action for which the nonce should be checked.
308 + * @param string|false $item Optional. Item for which the action should be performed, like "table". false if no item should be used in the nonce.
309 + * @param string $query_arg Optional. Name of the nonce query string argument in $_POST.
310 + * @param bool $ajax Whether the nonce comes from an AJAX request.
322 311 */
323 - public static function check_nonce( $action, $item = false, $query_arg = '_wpnonce', $ajax = false ) {
312 + public static function check_nonce( string $action, /* string|false */ $item = false, string $query_arg = '_wpnonce', bool $ajax = false ): void {
324 313 $nonce_action = self::nonce( $action, $item );
325 314 if ( $ajax ) {
326 315 check_ajax_referer( $nonce_action, $query_arg );
327 316 } else {
@@ -338,14 +327,15 @@
338 327 *
339 328 * @param string $column Column string.
340 329 * @return int Column number, 1-based.
341 330 */
342 - public static function letter_to_number( $column ) {
331 + public static function letter_to_number( string $column ): int {
332 + $column = (string) preg_replace( '/[^A-Za-z]/', '', $column );
343 333 $column = strtoupper( $column );
344 334 $count = strlen( $column );
345 335 $number = 0;
346 336 for ( $i = 0; $i < $count; $i++ ) {
347 - $number += ( ord( $column[ $count - 1 - $i ] ) - 64 ) * pow( 26, $i );
337 + $number += ( ord( $column[ $count - 1 - $i ] ) - 64 ) * 26 ** $i;
348 338 }
349 339 return $number;
350 340 }
351 341
@@ -358,13 +348,13 @@
358 348 *
359 349 * @param int $number Column number, 1-based.
360 350 * @return string Column string.
361 351 */
362 - public static function number_to_letter( $number ) {
352 + public static function number_to_letter( int $number ): string {
363 353 $column = '';
364 354 while ( $number > 0 ) {
365 355 $column = chr( 65 + ( ( $number - 1 ) % 26 ) ) . $column;
366 - $number = floor( ( $number - 1 ) / 26 );
356 + $number = intdiv( $number - 1, 26 );
367 357 }
368 358 return $column;
369 359 }
370 360
@@ -376,22 +366,27 @@
376 366 * @param string $datetime_string DateTime string, often in mySQL format..
377 367 * @param string $separator_or_format Optional. Separator between date and time, or format string.
378 368 * @return string Nice looking string with the date and time.
379 369 */
380 - public static function format_datetime( $datetime_string, $separator_or_format = ' ' ) {
370 + public static function format_datetime( string $datetime_string, string $separator_or_format = ' ' ): string {
381 371 $timezone = wp_timezone();
382 372 $datetime = date_create( $datetime_string, $timezone );
373 + if ( false === $datetime ) {
374 + return $datetime_string;
375 + }
383 376 $timestamp = $datetime->getTimestamp();
384 377
385 378 switch ( $separator_or_format ) {
386 379 case ' ':
387 380 case '<br />':
381 + case '<br/>':
382 + case '<br>':
388 383 $date = wp_date( get_option( 'date_format' ), $timestamp, $timezone );
389 384 $time = wp_date( get_option( 'time_format' ), $timestamp, $timezone );
390 385 $output = "{$date}{$separator_or_format}{$time}";
391 386 break;
392 387 default:
393 - $output = wp_date( $separator_or_format, $timestamp, $timezone );
388 + $output = (string) wp_date( $separator_or_format, $timestamp, $timezone );
394 389 break;
395 390 }
396 391
397 392 return $output;
@@ -404,11 +399,11 @@
404 399 *
405 400 * @param int $user_id WP user ID.
406 401 * @return string Nickname of the WP user with the $user_id.
407 402 */
408 - public static function get_user_display_name( $user_id ) {
403 + public static function get_user_display_name( int $user_id ): string {
409 404 $user = get_userdata( $user_id );
410 - return ( isset( $user->display_name ) ) ? $user->display_name : sprintf( '<em>%s</em>', __( 'unknown', 'tablepress' ) );
405 + return $user->display_name ?? sprintf( '<em>%s</em>', __( 'unknown', 'tablepress' ) );
411 406 }
412 407
413 408 /**
414 409 * Sanitizes a CSS class to ensure it only contains valid characters.
@@ -420,17 +415,265 @@
420 415 *
421 416 * @param string $css_class The CSS class name to be sanitized.
422 417 * @return string The sanitized CSS class.
423 418 */
424 - public static function sanitize_css_class( $css_class ) {
419 + public static function sanitize_css_class( string $css_class ): string {
425 420 // Strip out any %-encoded octets.
426 - $sanitized_css_class = preg_replace( '|%[a-fA-F0-9][a-fA-F0-9]|', '', $css_class );
421 + $sanitized_css_class = (string) preg_replace( '|%[a-fA-F0-9][a-fA-F0-9]|', '', $css_class );
427 422 // Limit to A-Z, a-z, 0-9, ':', '_', and '-'.
428 - $sanitized_css_class = preg_replace( '/[^A-Za-z0-9:_-]/', '', $sanitized_css_class );
423 + $sanitized_css_class = (string) preg_replace( '/[^A-Za-z0-9:_-]/', '', $sanitized_css_class );
429 424 return $sanitized_css_class;
430 425 }
431 426
432 427 /**
428 + * Extracts the top-level keys from a JavaScript object string.
429 + *
430 + * This function is used to extract the keys of the "Custom Commands" JavaScript object string, to check for overrides.
431 + * It covers most cases, like normal object properties with and without quotes, shorthand properties, and shorthand methods,
432 + * and also ignores single-line and multi-line comments.
433 + * It does not cover all possible JavaScript syntax (like template literals, special characters, ...),
434 + * but should be sufficient for the use case.
435 + *
436 + * @since 3.0.0
437 + *
438 + * @param string $js_object_string A JavaScript object as a string.
439 + * @return string[] Array of top-level keys of the object.
440 + */
441 + public static function extract_keys_from_js_object_string( string $js_object_string ): array {
442 + $object_keys = array();
443 + $length = strlen( $js_object_string );
444 + $depth = 0;
445 + $key_expected = true;
446 + $in_quotes = false;
447 + $quote_char = '';
448 + $in_function_declaration = false;
449 + $in_single_line_comment = false;
450 + $in_multi_line_comment = false;
451 + $object_key = '';
452 +
453 + for ( $i = 0; $i < $length; $i++ ) {
454 + $char = $js_object_string[ $i ];
455 +
456 + // Skip parsing single-line comments.
457 + if ( $in_single_line_comment ) {
458 + if ( "\n" === $char ) {
459 + $in_single_line_comment = false;
460 + }
461 + continue;
462 + } else { // phpcs:ignore Universal.ControlStructures.DisallowLonelyIf.Found
463 + if ( '/' === $char && $i + 1 < $length && '/' === $js_object_string[ $i + 1 ] ) {
464 + $in_single_line_comment = true;
465 + ++$i; // Skip the second '/'.
466 + continue;
467 + }
468 + }
469 +
470 + // Skip parsing multi-line comments.
471 + if ( $in_multi_line_comment ) {
472 + if ( '*' === $char && $i + 1 < $length && '/' === $js_object_string[ $i + 1 ] ) {
473 + $in_multi_line_comment = false;
474 + ++$i; // Skip the '/' that ends the multi-line comment.
475 + }
476 + continue;
477 + } else { // phpcs:ignore Universal.ControlStructures.DisallowLonelyIf.Found
478 + if ( '/' === $char && $i + 1 < $length && '*' === $js_object_string[ $i + 1 ] ) {
479 + $in_multi_line_comment = true;
480 + ++$i; // Skip the '*'.
481 + continue;
482 + }
483 + }
484 +
485 + // Skip parsing while inside a quoted string.
486 + if ( $in_quotes ) {
487 + if ( $quote_char === $char ) {
488 + $in_quotes = false;
489 + }
490 + continue;
491 + } else { // phpcs:ignore Universal.ControlStructures.DisallowLonelyIf.Found
492 + if ( '"' === $char || "'" === $char ) {
493 + $in_quotes = true;
494 + $quote_char = $char;
495 + continue;
496 + }
497 + }
498 +
499 + /*
500 + * Skip parsing while inside a `function abc( ... )` declaration string.
501 + * The `$key_expected` check limits search the "function" string to object values.
502 + * The check for the plain `f` reduces expensive `substr()` calls.
503 + */
504 + if ( ! $key_expected ) {
505 + if ( $in_function_declaration ) {
506 + if ( ')' === $char ) {
507 + $in_function_declaration = false;
508 + }
509 + continue;
510 + } else { // phpcs:ignore Universal.ControlStructures.DisallowLonelyIf.Found
511 + if ( 'f' === $char && 'function' === substr( $js_object_string, $i, 8 ) ) {
512 + $in_function_declaration = true;
513 + $i += 7; // Skip the rest of the "function" string.
514 + continue;
515 + }
516 + }
517 + }
518 +
519 + // Handle object depth, so that most parsing can be limited to the top level.
520 + if ( '{' === $char || '[' === $char ) {
521 + ++$depth;
522 + }
523 +
524 + // Extract only keys at the top level.
525 + if ( 1 === $depth ) {
526 + if ( $key_expected ) {
527 + if ( ':' === $char ) {
528 + // Check for normal keys, with value after :.
529 +
530 + // Go backwards to find the start of the key.
531 + $j = $i - 1;
532 + while ( $j >= 0 && preg_match( '/\s/', $js_object_string[ $j ] ) ) {
533 + --$j;
534 + }
535 + $key_end = $j; // Position of the last character of the key (potentially with quote).
536 + if ( '"' === $js_object_string[ $j ] || "'" === $js_object_string[ $j ] ) {
537 + // Quoted key.
538 + $quote_char = $js_object_string[ $j ];
539 + --$j;
540 + while ( $j >= 0 && $quote_char !== $js_object_string[ $j ] ) {
541 + --$j;
542 + }
543 + $key_start = $j + 1;
544 + } else {
545 + // Unquoted key.
546 + while ( $j >= 0 && preg_match( '/[\w]/', $js_object_string[ $j ] ) ) {
547 + --$j;
548 + }
549 + $key_start = $j + 1;
550 + }
551 + $object_key = substr( $js_object_string, $key_start, $key_end - $key_start + 1 );
552 + $object_key = trim( $object_key, "\"'" );
553 + if ( '' !== $object_key && ! in_array( $object_key, $object_keys, true ) ) {
554 + $object_keys[] = $object_key;
555 + }
556 + $key_expected = false;
557 + } elseif ( ( ',' === $char || '}' === $char ) ) { // The `}` case is for the last key.
558 + // Check for shorthand properties (which must be unquoted).
559 +
560 + // Go backwards to find the start of the shorthand key.
561 + $j = $i - 1;
562 + while ( $j >= 0 && preg_match( '/\s/', $js_object_string[ $j ] ) ) {
563 + --$j;
564 + }
565 + $key_end = $j; // Position of the last character of the key (without a quote).
566 + while ( $j >= 0 && preg_match( '/[\w]/', $js_object_string[ $j ] ) ) {
567 + --$j;
568 + }
569 + $key_start = $j + 1;
570 + $object_key = substr( $js_object_string, $key_start, $key_end - $key_start + 1 );
571 + if ( '' !== $object_key && ! in_array( $object_key, $object_keys, true ) ) {
572 + $object_keys[] = $object_key;
573 + }
574 + } elseif ( '(' === $char ) {
575 + // Detect shorthand method definitions.
576 +
577 + // Go back to find the start of the method name.
578 + $j = $i - 1;
579 + while ( $j >= 0 && preg_match( '/\s/', $js_object_string[ $j ] ) ) {
580 + --$j;
581 + }
582 + $key_end = $j;
583 + while ( $j >= 0 && preg_match( '/[\w]/', $js_object_string[ $j ] ) ) {
584 + --$j;
585 + }
586 + $key_start = $j + 1;
587 + $object_key = substr( $js_object_string, $key_start, $key_end - $key_start + 1 );
588 + if ( '' !== $object_key && ! in_array( $object_key, $object_keys, true ) ) {
589 + $object_keys[] = $object_key;
590 + }
591 + }
592 + }
593 +
594 + // Reset the "key expected" flag after a comma or closing brace.
595 + if ( ',' === $char || '}' === $char ) {
596 + $key_expected = true;
597 + }
598 + }
599 +
600 + // Handle object depth.
601 + if ( '}' === $char || ']' === $char ) {
602 + --$depth;
603 + }
604 + }
605 +
606 + return $object_keys;
607 + }
608 +
609 + /**
610 + * Converts old DataTables 1.x CSS classes and parameters to the DataTables 2 variants.
611 + *
612 + * This function is used to modernize "Custom CSS" and "Custom Commands" for compatibility with DataTables 2.x.
613 + * It probably does not catch all possible cases.
614 + *
615 + * @since 3.0.0
616 + *
617 + * @param string $code Code that contains DataTables 1.x CSS classes and parameters.
618 + * @return string Updated code with DataTables 2.x CSS classes and parameters.
619 + */
620 + public static function convert_datatables_api_data( string $code ): string {
621 + /**
622 + * Mappings for DataTables 1.x CSS class or parameter to DataTables 2 variants.
623 + * As this array is used in `strtr()`, it's pre-sorted for descending string length of the array keys.
624 + */
625 + static $datatables_api_data_mappings = array(
626 + // CSS classes.
627 + '.tablepress thead .sorting:hover' => '.tablepress thead .dt-orderable-asc:hover,.tablepress thead .dt-orderable-desc:hover',
628 + '.tablepress thead .sorting_desc' => '.tablepress thead .dt-ordering-desc',
629 + '.dataTables_filter label input' => '.dt-container .dt-search input',
630 + '.tablepress thead .sorting_asc' => '.tablepress thead .dt-ordering-asc',
631 + '.dataTables_scrollFootInner' => '.dt-scroll-footInner',
632 + '.dataTables_scrollHeadInner' => '.dt-scroll-headInner',
633 + '.tablepress thead .sorting' => '.tablepress thead .dt-orderable-asc,.tablepress thead .dt-orderable-desc',
634 + '.dataTables_processing' => '.dt-processing',
635 + '.dataTables_scrollBody' => '.dt-scroll-body',
636 + '.dataTables_scrollFoot' => '.dt-scroll-foot',
637 + '.dataTables_scrollHead' => '.dt-scroll-head',
638 + '.dataTables_paginate' => '.dt-paging',
639 + '.tablepress .even td' => '.tablepress>:where(tbody.row-striping)>:nth-child(odd)>*',
640 + '.dataTables_wrapper' => '.dt-container',
641 + '.tablepress .odd td' => '.tablepress>:where(tbody.row-striping)>:nth-child(even)>*',
642 + '.dataTables_filter' => '.dt-search',
643 + '.dataTables_length' => '.dt-length',
644 + '.dataTables_scroll' => '.dt-scroll',
645 + '.dataTables_empty' => '.dt-empty',
646 + '.dataTables_info' => '.dt-info',
647 + '.paginate_button' => '.dt-paging-button',
648 + // DataTables API functions.
649 + '$.fn.dataTable.' => 'DataTable.',
650 + );
651 + $code = strtr( $code, $datatables_api_data_mappings );
652 +
653 + // HTML ID mappings, which were removed.
654 + if ( str_contains( $code, '#tablepress-' ) ) {
655 + $code = (string) preg_replace(
656 + array(
657 + '/#tablepress-([A-Za-z1-9_-]|[A-Za-z0-9_-]{2,})_paginate/',
658 + '/#tablepress-([A-Za-z1-9_-]|[A-Za-z0-9_-]{2,})_filter/',
659 + '/#tablepress-([A-Za-z1-9_-]|[A-Za-z0-9_-]{2,})_length/',
660 + '/#tablepress-([A-Za-z1-9_-]|[A-Za-z0-9_-]{2,})_info/',
661 + ),
662 + array(
663 + '#tablepress-$1_wrapper .dt-paging',
664 + '#tablepress-$1_wrapper .dt-search',
665 + '#tablepress-$1_wrapper .dt-length',
666 + '#tablepress-$1_wrapper .dt-info',
667 + ),
668 + $code,
669 + );
670 + }
671 +
672 + return $code;
673 + }
674 +
675 + /**
433 676 * Retrieves all information of a WP_Error object as a string.
434 677 *
435 678 * @since 1.4.0
436 679 *
@@ -436,9 +679,9 @@
436 679 *
437 680 * @param WP_Error $wp_error A WP_Error object.
438 681 * @return string All error codes, messages, and data of the WP_Error.
439 682 */
440 - public static function get_wp_error_string( $wp_error ) {
683 + public static function get_wp_error_string( WP_Error $wp_error ): string {
441 684 $error_strings = array();
442 685 $error_codes = $wp_error->get_error_codes();
443 686 // Reverse order to get latest errors first.
444 687 $error_codes = array_reverse( $error_codes );
@@ -449,10 +692,16 @@
449 692 if ( ! empty( $error_messages ) ) {
450 693 $error_strings[ $error_code ] .= " ({$error_messages})";
451 694 }
452 695 $error_data = $wp_error->get_error_data( $error_code );
453 - if ( ! is_null( $error_data ) ) {
696 + if ( is_string( $error_data ) ) {
454 697 $error_strings[ $error_code ] .= " [{$error_data}]";
698 + } elseif ( is_array( $error_data ) ) {
699 + foreach ( $error_data as $key => $value ) {
700 + $error_data[ $key ] = "{$key}: {$value}";
701 + }
702 + $error_data = implode( ', ', $error_data );
703 + $error_strings[ $error_code ] .= " [{$error_data}]";
455 704 }
456 705 }
457 706 return implode( ";\n", $error_strings );
458 707 }
@@ -461,14 +710,14 @@
461 710 * Generate the action URL, to be used as a link within the plugin (e.g. in the submenu navigation or List of Tables).
462 711 *
463 712 * @since 1.0.0
464 713 *
465 - * @param array $params Optional. Parameters to form the query string of the URL.
466 - * @param bool $add_nonce Optional. Whether the URL shall be nonced by WordPress.
467 - * @param string $target Optional. Target File, e.g. "admin-post.php" for POST requests.
714 + * @param array<string, mixed> $params Optional. Parameters to form the query string of the URL.
715 + * @param bool $add_nonce Optional. Whether the URL shall be nonced by WordPress.
716 + * @param string $target Optional. Target File, e.g. "admin-post.php" for POST requests.
468 717 * @return string The URL for the given parameters (already run through esc_url() with $add_nonce === true!).
469 718 */
470 - public static function url( array $params = array(), $add_nonce = false, $target = '' ) {
719 + public static function url( array $params = array(), bool $add_nonce = false, string $target = '' ): string {
471 720 // Default action is "list", if no action given.
472 721 if ( ! isset( $params['action'] ) ) {
473 722 $params['action'] = 'list';
474 723 }
@@ -511,12 +760,12 @@
511 760 * Create a redirect URL from the $target_parameters and redirect the user.
512 761 *
513 762 * @since 1.0.0
514 763 *
515 - * @param array $params Optional. Parameters from which the target URL is constructed.
516 - * @param bool $add_nonce Optional. Whether the URL shall be nonced by WordPress.
764 + * @param array<string, mixed> $params Optional. Parameters from which the target URL is constructed.
765 + * @param bool $add_nonce Optional. Whether the URL shall be nonced by WordPress.
517 766 */
518 - public static function redirect( array $params = array(), $add_nonce = false ) {
767 + public static function redirect( array $params = array(), bool $add_nonce = false ): void {
519 768 $redirect = self::url( $params );
520 769 if ( $add_nonce ) {
521 770 if ( ! isset( $params['item'] ) ) {
522 771 $params['item'] = false;
@@ -528,21 +777,8 @@
528 777 exit;
529 778 }
530 779
531 780 /**
532 - * Show an error notice to admins, if TablePress's minimum requirements are not reached.
533 - *
534 - * @since 1.0.0
535 - */
536 - public static function show_minimum_requirements_error_notice() {
537 - // Message is not translated as it is shown on every admin screen, for which we don't want to load translations.
538 - echo '<div class="notice notice-error form-invalid"><p>' .
539 - '<strong>Attention:</strong> ' .
540 - '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>!' .
541 - "</p></div>\n";
542 - }
543 -
544 - /**
545 781 * Determines whether the site uses the block editor, so that certain text and input fields referring to Shortcodes can be displayed or not.
546 782 *
547 783 * @since 2.0.1
548 784 *
@@ -547,9 +783,9 @@
547 783 * @since 2.0.1
548 784 *
549 785 * @return bool True if the site uses the block editor, false otherwise.
550 786 */
551 - public static function site_uses_block_editor() {
787 + public static function site_uses_block_editor(): bool {
552 788 $site_uses_block_editor = use_block_editor_for_post_type( 'post' )
553 789 && ! is_plugin_active( 'beaver-builder-lite-version/fl-builder.php' )
554 790 && ! is_plugin_active( 'classic-editor/classic-editor.php' )
555 791 && ! is_plugin_active( 'classic-editor-addon/classic-editor-addon.php' )
@@ -564,9 +800,9 @@
564 800 * @since 2.0.1
565 801 *
566 802 * @param bool $site_uses_block_editor True if the site uses the block editor, false otherwise.
567 803 */
568 - $site_uses_block_editor = apply_filters( 'tablepress_site_uses_block_editor', $site_uses_block_editor );
804 + $site_uses_block_editor = (bool) apply_filters( 'tablepress_site_uses_block_editor', $site_uses_block_editor );
569 805
570 806 return $site_uses_block_editor;
571 807 }
572 808
@@ -574,9 +810,9 @@
574 810 * Initializes the list of TablePress premium modules.
575 811 *
576 812 * @since 2.1.0
577 813 */
578 - public static function init_modules() {
814 + public static function init_modules(): void {
579 815 self::$modules = array(
580 816 'advanced-access-rights' => array(
581 817 'name' => __( 'Advanced Access Rights', 'tablepress' ),
582 818 'description' => __( 'Restrict access to individual tables for individual users.', 'tablepress' ),
@@ -649,9 +885,9 @@
649 885 'minimum_plan' => 'pro',
650 886 'default_active' => false,
651 887 ),
652 888 'datatables-buttons' => array(
653 - 'name' => __( 'Buttons', 'tablepress' ),
889 + 'name' => __( 'User Action Buttons', 'tablepress' ),
654 890 'description' => __( 'Add buttons for downloading, copying, printing, and changing column visibility of tables.', 'tablepress' ),
655 891 'category' => 'frontend',
656 892 'class' => 'TablePress_Module_DataTables_Buttons',
657 893 'incompatible_classes' => array( 'TablePress_DataTables_Buttons' ),
@@ -676,9 +912,9 @@
676 912 'minimum_plan' => 'pro',
677 913 'default_active' => false,
678 914 ),
679 915 'datatables-counter-column' => array(
680 - 'name' => __( 'Counter Column', 'tablepress' ),
916 + 'name' => __( 'Index Column', 'tablepress' ),
681 917 'description' => __( 'Make the first column an index or counter column with the row position.', 'tablepress' ),
682 918 'category' => 'frontend',
683 919 'class' => 'TablePress_Module_DataTables_Counter_Column',
684 920 'incompatible_classes' => array(),
@@ -696,8 +932,44 @@
696 932 ),
697 933 'minimum_plan' => 'pro',
698 934 'default_active' => true,
699 935 ),
936 + 'datatables-layout' => array(
937 + 'name' => __( 'Table Layout', 'tablepress' ),
938 + 'description' => __( 'Customize the layout and position of features around a table.', 'tablepress' ),
939 + 'category' => 'frontend',
940 + 'class' => 'TablePress_Module_DataTables_Layout',
941 + 'incompatible_classes' => array(),
942 + 'minimum_plan' => 'pro',
943 + 'default_active' => true,
944 + ),
945 + 'datatables-fuzzysearch' => array(
946 + 'name' => __( 'Fuzzy Search', 'tablepress' ),
947 + 'description' => __( 'Let the search account for spelling mistakes and typos and find similar matches.', 'tablepress' ),
948 + 'category' => 'search-filter',
949 + 'class' => 'TablePress_Module_DataTables_FuzzySearch',
950 + 'incompatible_classes' => array(),
951 + 'minimum_plan' => 'max',
952 + 'default_active' => false,
953 + ),
954 + 'datatables-inverted-filter' => array(
955 + 'name' => __( 'Inverted Filtering', 'tablepress' ),
956 + 'description' => __( 'Turn the filtering into a search and hide the table if no search term is entered.', 'tablepress' ),
957 + 'category' => 'search-filter',
958 + 'class' => 'TablePress_Module_DataTables_Inverted_Filter',
959 + 'incompatible_classes' => array(),
960 + 'minimum_plan' => 'max',
961 + 'default_active' => false,
962 + ),
963 + 'datatables-pagination' => array(
964 + 'name' => __( 'Advanced Pagination Settings', 'tablepress' ),
965 + 'description' => __( 'Customize the pagination settings of the table.', 'tablepress' ),
966 + 'category' => 'frontend',
967 + 'class' => 'TablePress_Module_DataTables_Pagination',
968 + 'incompatible_classes' => array(),
969 + 'minimum_plan' => 'pro',
970 + 'default_active' => false,
971 + ),
700 972 'datatables-rowgroup' => array(
701 973 'name' => __( 'Row Grouping', 'tablepress' ),
702 974 'description' => __( 'Group table rows by a common keyword, category, or title.', 'tablepress' ),
703 975 'category' => 'frontend',
@@ -739,8 +1011,17 @@
739 1011 'category' => 'backend',
740 1012 'class' => 'TablePress_Module_DataTables_ServerSide_Processing',
741 1013 'incompatible_classes' => array(),
742 1014 'minimum_plan' => 'max',
1015 + 'default_active' => true,
1016 + ),
1017 + 'default-style-customizer' => array(
1018 + 'name' => __( 'Default Style Customizer', 'tablepress' ),
1019 + 'description' => __( 'Change the default styling of your tables in the visual style customizer.', 'tablepress' ),
1020 + 'category' => 'frontend',
1021 + 'class' => 'TablePress_Module_Default_Style_Customizer',
1022 + 'incompatible_classes' => array(),
1023 + 'minimum_plan' => 'pro',
743 1024 'default_active' => true,
744 1025 ),
745 1026 'responsive-tables' => array(
746 1027 'name' => __( 'Responsive Tables', 'tablepress' ),