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 | controllers/controller-admin.php +108 -41 2.43.0 View file →
@@ -27,9 +27,9 @@
27 27 *
28 28 * @since 1.0.0
29 29 * @var string[]
30 30 */
31 - protected $page_hooks = array();
31 + protected array $page_hooks = array();
32 32
33 33 /**
34 34 * Actions that have a view and admin menu or nav tab menu entry.
35 35 *
@@ -35,17 +35,16 @@
35 35 *
36 36 * @since 1.0.0
37 37 * @var array<string, array<string, bool|string>>
38 38 */
39 - protected $view_actions = array();
39 + protected array $view_actions = array();
40 40
41 41 /**
42 42 * Instance of the TablePress Admin View that is rendered.
43 43 *
44 44 * @since 1.0.0
45 - * @var TablePress_View
46 45 */
47 - protected $view;
46 + protected \TablePress_View $view;
48 47
49 48 /**
50 49 * Initialize the Admin Controller, determine location the admin menu, set up actions.
51 50 *
@@ -97,11 +96,11 @@
97 96
98 97 $this->init_view_actions();
99 98 $min_access_cap = $this->view_actions['list']['required_cap'];
100 99
101 - if ( $this->is_top_level_page ) {
102 - $icon_url = 'dashicons-list-view';
103 - switch ( $this->parent_page ) {
100 + if ( TablePress::$controller->is_top_level_page ) {
101 + $icon_url = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCIgdmlld0JveD0iLTMyIC0zMiA2NCA2NCIgZmlsbD0iI2ZmZiI+PHBhdGggZD0iTTAtMjUuODU0aC0yNS44NTR2NTEuNzA4aDUxLjcwOFYwSDIxdjIxaC00MnYtNDJIMFoiLz48cGF0aCBkPSJNLTE4LTE4aDEwdjEwaC0xMHpNLTE4LTVoMTBWNWgtMTB6TS01LTVINVY1SC01ek0tMTggOGgxMHYxMGgtMTB6TS01IDhINXYxMEgtNXpNOCA4aDEwdjEwSDh6TTUtMzFoNi4xOHY2LjE4SDV6TTE5LTI1aDYuMTh2Ni4xOEgxOXpNMC0xNWgzLjgydjMuODJIMHpNMTAtMjBoMy44MnYzLjgySDEwek0yNS0xMmgzLjgydjMuODJIMjV6TTgtMTNoMTB2MTBIOHoiLz48L3N2Zz4=';
102 + switch ( TablePress::$controller->parent_page ) {
104 103 case 'top':
105 104 $position = 3; // Position of Dashboard + 1.
106 105 break;
107 106 case 'bottom':
@@ -115,9 +114,9 @@
115 114 // Prevent overwriting existing menu entries.
116 115 while ( isset( $GLOBALS['menu'][ $position ] ) ) {
117 116 ++$position;
118 117 }
119 - add_menu_page( 'TablePress', $admin_menu_entry_name, $min_access_cap, 'tablepress', $callback, $icon_url, $position ); // @phpstan-ignore-line
118 + add_menu_page( 'TablePress', $admin_menu_entry_name, $min_access_cap, 'tablepress', $callback, $icon_url, $position ); // @phpstan-ignore argument.type
120 119 foreach ( $this->view_actions as $action => $entry ) {
121 120 if ( ! $entry['show_entry'] ) {
122 121 continue;
123 122 }
@@ -124,9 +123,9 @@
124 123 $slug = 'tablepress';
125 124 if ( 'list' !== $action ) {
126 125 $slug .= '_' . $action;
127 126 }
128 - // @phpstan-ignore-next-line
127 + // @phpstan-ignore argument.type, argument.type
129 128 $page_hook = add_submenu_page( 'tablepress', sprintf( __( '%1$s &lsaquo; %2$s', 'tablepress' ), $entry['page_title'], 'TablePress' ), $entry['admin_menu_title'], $entry['required_cap'], $slug, $callback );
130 129 if ( false !== $page_hook ) {
131 130 $this->page_hooks[] = $page_hook;
132 131 }
@@ -131,10 +130,10 @@
131 130 $this->page_hooks[] = $page_hook;
132 131 }
133 132 }
134 133 } else {
135 - // @phpstan-ignore-next-line
136 - $page_hook = add_submenu_page( $this->parent_page, 'TablePress', $admin_menu_entry_name, $min_access_cap, 'tablepress', $callback );
134 + // @phpstan-ignore argument.type
135 + $page_hook = add_submenu_page( TablePress::$controller->parent_page, 'TablePress', $admin_menu_entry_name, $min_access_cap, 'tablepress', $callback );
137 136 if ( false !== $page_hook ) {
138 137 $this->page_hooks[] = $page_hook;
139 138 }
140 139 }
@@ -179,13 +178,8 @@
179 178 add_action( 'admin_bar_menu', array( $this, 'add_wp_admin_bar_new_content_menu_entry' ), 71 );
180 179 }
181 180
182 181 add_action( 'load-plugins.php', array( $this, 'plugins_page' ) );
183 -
184 - // Add filters and actions for the integration into the WP WXR exporter and importer.
185 - add_action( 'wp_import_insert_post', array( TablePress::$model_table, 'add_table_id_on_wp_import' ), 10, 4 );
186 - add_filter( 'wp_import_post_meta', array( TablePress::$model_table, 'prevent_table_id_post_meta_import_on_wp_import' ), 10, 3 );
187 - add_filter( 'wxr_export_skip_postmeta', array( TablePress::$model_table, 'add_table_id_to_wp_export' ), 10, 3 );
188 182 }
189 183
190 184 /**
191 185 * Loads additional JavaScript code for the TablePress table block (in the block editor context).
@@ -192,8 +186,16 @@
192 186 *
193 187 * @since 2.2.0
194 188 */
195 189 public function enqueue_block_editor_assets(): void {
190 + /*
191 + * Register the `react-jsx-runtime` polyfill, if it is not already registered.
192 + * This is needed as a polyfill for WP < 6.6, and can be removed once WP 6.6 is the minimum requirement for TablePress.
193 + */
194 + if ( ! wp_script_is( 'react-jsx-runtime', 'registered' ) ) {
195 + wp_register_script( 'react-jsx-runtime', plugins_url( 'admin/js/react-jsx-runtime.min.js', TABLEPRESS__FILE__ ), array( 'react' ), TablePress::version, true );
196 + }
197 +
196 198 // Add table information for the block editor to the page.
197 199 $handle = generate_block_asset_handle( 'tablepress/table', 'editorScript' );
198 200 $data = $this->get_block_editor_data();
199 201 wp_add_inline_script( $handle, $data, 'before' );
@@ -280,17 +282,17 @@
280 282 $url = TablePress::url( array( 'action' => 'list' ) );
281 283 }
282 284
283 285 return <<<JS
284 -// Ensure the global `tp` object exists.
285 -window.tp = window.tp || {};
286 -tp.url = '{$url}';
287 -tp.load_block_preview = {$load_block_preview};
288 -tp.table = {};
289 -tp.table.shortcode = '{$shortcode}';
290 -tp.table.template = JSON.parse( '{$template}' );
291 -tp.tables = JSON.parse( '{$tables}' );
292 -JS;
286 + // Ensure the global `tp` object exists.
287 + window.tp = window.tp || {};
288 + tp.url = '{$url}';
289 + tp.load_block_preview = {$load_block_preview};
290 + tp.table = {};
291 + tp.table.shortcode = '{$shortcode}';
292 + tp.table.template = JSON.parse( '{$template}' );
293 + tp.tables = JSON.parse( '{$tables}' );
294 + JS;
293 295 }
294 296
295 297 /**
296 298 * Register actions to add "Table" button to "HTML editor" and "Visual editor" toolbars.
@@ -318,9 +320,9 @@
318 320 'title' => __( 'Insert a TablePress table', 'tablepress' ),
319 321 'thickbox_title' => __( 'Insert a TablePress table', 'tablepress' ),
320 322 'thickbox_url' => TablePress::url( array( 'action' => 'editor_button_thickbox' ), true, 'admin-post.php' ),
321 323 ),
322 - )
324 + ),
323 325 );
324 326
325 327 // TinyMCE integration.
326 328 if ( user_can_richedit() ) {
@@ -388,8 +390,20 @@
388 390 public function plugins_page(): void {
389 391 // Add additional links on Plugins page.
390 392 add_filter( 'plugin_action_links_' . TABLEPRESS_BASENAME, array( $this, 'add_plugin_action_links' ) );
391 393 add_filter( 'plugin_row_meta', array( $this, 'add_plugin_row_meta' ), 10, 2 );
394 + $incompatible_superseded_extensions = array(
395 + 'tablepress-datatables-alphabetsearch/tablepress-datatables-alphabetsearch.php',
396 + 'tablepress-datatables-column-filter-widgets/tablepress-datatables-column-filter-widgets.php',
397 + 'tablepress-datatables-columnfilter/tablepress-datatables-columnfilter.php',
398 + 'tablepress-datatables-fixedcolumns/tablepress-datatables-fixedcolumns.php',
399 + 'tablepress-datatables-row-details/tablepress-datatables-row-details.php',
400 + 'tablepress-datatables-rowgroup/tablepress-datatables-rowgroup.php',
401 + 'tablepress-responsive-tables/tablepress-responsive-tables.php',
402 + );
403 + foreach ( $incompatible_superseded_extensions as $plugin_file ) {
404 + add_action( "after_plugin_row_{$plugin_file}", array( $this, 'add_superseded_extension_meta_row' ), 10, 3 );
405 + }
392 406 }
393 407
394 408 /**
395 409 * Add links to the TablePress entry in the "Plugin" column on the Plugins page.
@@ -427,8 +441,54 @@
427 441 return $links;
428 442 }
429 443
430 444 /**
445 + * Prints a superseded extension notice below certain TablePress Extension plugins' meta rows on the "Plugins" screen.
446 + *
447 + * @since 2.4.1
448 + *
449 + * @param string $plugin_file Path to the plugin file relative to the plugins directory.
450 + * @param array<int, string|string[]|bool> $plugin_data An array of plugin data.
451 + * @param string $status Status filter currently applied to the plugin list.
452 + */
453 + public function add_superseded_extension_meta_row( string $plugin_file, array $plugin_data, string $status ): void {
454 + if ( ! is_plugin_active( $plugin_file ) ) {
455 + return;
456 + }
457 + ?>
458 + <tr class="plugin-update-tr active">
459 + <td colspan="<?php echo esc_attr( $GLOBALS['wp_list_table']->get_column_count() ); ?>" class="plugin-update colspanchange">
460 + <div class="update-message notice inline notice-error notice-alt">
461 + <?php
462 + if ( tb_tp_fs()->is_free_plan() ) {
463 + echo '<p style="font-size:14px;">';
464 + _e( 'This TablePress Extension was retired.', 'tablepress' );
465 + echo ' ';
466 + _e( '<strong>The plugin does no longer work with TablePress 3</strong> and will no longer receive updates or support!', 'tablepress' );
467 + echo '<br>';
468 + _e( 'Keeping it activated can lead to errors on your website!', 'tablepress' );
469 + echo ' <strong>' . sprintf( __( '<a href="%s">Find out what you can do to continue using its features!</a>', 'tablepress' ), 'https://tablepress.org/upgrade-extensions/?utm_source=plugin&utm_medium=textlink&utm_content=plugins-list-table' ) . '</strong>';
470 + echo '</p>';
471 + }
472 + ?>
473 + <style>
474 + /* Remove the separator line between the plugin's and the notice's table row. */
475 + .plugins .active[data-plugin="<?php echo $plugin_file; ?>"] th,
476 + .plugins .active[data-plugin="<?php echo $plugin_file; ?>"] td {
477 + box-shadow: none;
478 + }
479 + /* Hide the plugin update row for the Extension as those won't work anymore anyways. */
480 + .plugins .plugin-update-tr[data-plugin="<?php echo $plugin_file; ?>"] {
481 + display: none;
482 + }
483 + </style>
484 + </div>
485 + </td>
486 + </tr>
487 + <?php
488 + }
489 +
490 + /**
431 491 * Prepare the rendering of an admin screen, by determining the current action, loading necessary data and initializing the view.
432 492 *
433 493 * @since 1.0.0
434 494 */
@@ -434,9 +494,9 @@
434 494 */
435 495 public function load_admin_page(): void {
436 496 // Determine the action from either the GET parameter (for sub-menu entries, and the main admin menu entry).
437 497 $action = ( ! empty( $_GET['action'] ) ) ? $_GET['action'] : 'list'; // Default action is list.
438 - if ( $this->is_top_level_page ) {
498 + if ( TablePress::$controller->is_top_level_page ) {
439 499 // Or, for sub-menu entry of an admin menu "TablePress" entry, get it from the "page" GET parameter.
440 500 if ( 'tablepress' !== $_GET['page'] ) {
441 501 // Actions that are top-level entries, but don't have an action GET parameter (action is after last _ in string).
442 502 $action = substr( $_GET['page'], 11 ); // $_GET['page'] has the format 'tablepress_{$action}'
@@ -443,9 +503,9 @@
443 503 }
444 504 }
445 505
446 506 // Check if action is a supported action, and whether the user is allowed to access this screen.
447 - if ( ! isset( $this->view_actions[ $action ] ) || ! current_user_can( $this->view_actions[ $action ]['required_cap'] ) ) { // @phpstan-ignore-line (The array value for the capability is always a string.)
507 + if ( ! isset( $this->view_actions[ $action ] ) || ! current_user_can( $this->view_actions[ $action ]['required_cap'] ) ) { // @phpstan-ignore argument.type (The array value for the capability is always a string.)
448 508 wp_die( __( 'Sorry, you are not allowed to access this page.', 'default' ), 403 );
449 509 }
450 510
451 511 // Don't load TablePress assets on the Freemius opt-in/activation screen.
@@ -480,9 +540,9 @@
480 540 $data['table_ids'] = TablePress::$model_table->load_all( true );
481 541 $data['messages']['donation_nag'] = $this->maybe_show_donation_message();
482 542 $data['messages']['first_visit'] = ! $data['messages']['donation_nag'] && TablePress::$model_options->get( 'message_first_visit' );
483 543 $data['messages']['plugin_update'] = TablePress::$model_options->get( 'message_plugin_update' );
484 - $data['messages']['superseded_extensions'] = TablePress::$model_options->get( 'message_superseded_extensions' );
544 + $data['messages']['superseded_extensions'] = current_user_can( 'manage_options' ) && TablePress::$model_options->get( 'message_superseded_extensions' );
485 545 $data['table_count'] = count( $data['table_ids'] );
486 546 break;
487 547 case 'about':
488 548 $data['first_activation'] = TablePress::$model_options->get( 'first_activation' );
@@ -493,9 +553,9 @@
493 553 * (called here, as the credentials form posts to this handler again, due to how `request_filesystem_credentials()` works)
494 554 */
495 555 if ( isset( $_GET['item'] ) && 'save_custom_css' === $_GET['item'] ) {
496 556 TablePress::check_nonce( 'options', $_GET['item'] ); // Nonce check here, as we don't have an explicit handler, and even viewing the screen needs to be checked.
497 - $action = 'options_custom_css'; // to load a different view
557 + $action = 'options_custom_css'; // To load a different view.
498 558 // Try saving "Custom CSS" to a file, otherwise this gets the HTML for the credentials form.
499 559 $tablepress_css = TablePress::load_class( 'TablePress_CSS', 'class-css.php', 'classes' );
500 560 $result = $tablepress_css->save_custom_css_to_file_plugin_options( TablePress::$model_options->get( 'custom_css' ), TablePress::$model_options->get( 'custom_css_minified' ) );
501 561 if ( is_string( $result ) ) {
@@ -516,9 +576,9 @@
516 576 break;
517 577 }
518 578 $data['frontend_options']['use_custom_css'] = TablePress::$model_options->get( 'use_custom_css' );
519 579 $data['frontend_options']['custom_css'] = TablePress::$model_options->get( 'custom_css' );
520 - $data['user_options']['parent_page'] = $this->parent_page;
580 + $data['user_options']['parent_page'] = TablePress::$controller->parent_page;
521 581 break;
522 582 case 'edit':
523 583 if ( empty( $_GET['table_id'] ) ) {
524 584 TablePress::redirect( array( 'action' => 'list', 'message' => 'error_no_table' ) );
@@ -810,10 +870,10 @@
810 870
811 871 $add_table = wp_unslash( $_POST['table'] );
812 872
813 873 // Perform confidence checks of posted data.
814 - $name = ( isset( $add_table['name'] ) ) ? $add_table['name'] : '';
815 - $description = ( isset( $add_table['description'] ) ) ? $add_table['description'] : '';
874 + $name = $add_table['name'] ?? '';
875 + $description = $add_table['description'] ?? '';
816 876 if ( ! isset( $add_table['rows'], $add_table['columns'] ) ) {
817 877 TablePress::redirect( array( 'action' => 'add', 'message' => 'error_add', 'error_details' => 'The HTTP POST data does not contain the table size.' ) );
818 878 }
819 879
@@ -873,10 +933,10 @@
873 933 if ( ! empty( $posted_options['admin_menu_parent_page'] ) && '-' !== $posted_options['admin_menu_parent_page'] ) {
874 934 $new_options['admin_menu_parent_page'] = $posted_options['admin_menu_parent_page'];
875 935 // Re-init parent information, as `TablePress::redirect()` URL might be wrong otherwise.
876 936 /** This filter is documented in classes/class-controller.php */
877 - $this->parent_page = apply_filters( 'tablepress_admin_menu_parent_page', $posted_options['admin_menu_parent_page'] );
878 - $this->is_top_level_page = in_array( $this->parent_page, array( 'top', 'middle', 'bottom' ), true );
937 + TablePress::$controller->parent_page = apply_filters( 'tablepress_admin_menu_parent_page', $posted_options['admin_menu_parent_page'] );
938 + TablePress::$controller->is_top_level_page = in_array( TablePress::$controller->parent_page, array( 'top', 'middle', 'bottom' ), true );
879 939 }
880 940
881 941 // Custom CSS can only be saved if the user is allowed to do so.
882 942 $update_custom_css_files = false;
@@ -887,13 +947,20 @@
887 947 if ( isset( $posted_options['custom_css'] ) ) {
888 948 $new_options['custom_css'] = $posted_options['custom_css'];
889 949
890 950 $tablepress_css = TablePress::load_class( 'TablePress_CSS', 'class-css.php', 'classes' );
891 - // Sanitize and tidy up Custom CSS.
892 - $new_options['custom_css'] = $tablepress_css->sanitize_css( $new_options['custom_css'] );
893 - // Minify Custom CSS.
894 - $new_options['custom_css_minified'] = $tablepress_css->minify_css( $new_options['custom_css'] );
895 951
952 + if ( '' !== $new_options['custom_css'] ) {
953 + // Update "Custom CSS" to use DataTables 2 variants instead of old DataTables 1.x CSS classes.
954 + $new_options['custom_css'] = TablePress::convert_datatables_api_data( $new_options['custom_css'] );
955 + // Sanitize and tidy up Custom CSS.
956 + $new_options['custom_css'] = $tablepress_css->sanitize_css( $new_options['custom_css'] );
957 + // Minify Custom CSS.
958 + $new_options['custom_css_minified'] = $tablepress_css->minify_css( $new_options['custom_css'] );
959 + } else {
960 + $new_options['custom_css_minified'] = '';
961 + }
962 +
896 963 // Maybe update CSS files as well.
897 964 $custom_css_file_contents = $tablepress_css->load_custom_css_from_file( 'normal' );
898 965 if ( false === $custom_css_file_contents ) {
899 966 $custom_css_file_contents = '';
@@ -1020,9 +1087,9 @@
1020 1087 /** This filter is documented in controllers/controller-admin.php */
1021 1088 $download_filename = apply_filters( 'tablepress_export_filename', $download_filename, '', '', $export['format'], $export_to_zip );
1022 1089 $download_filename = sanitize_file_name( $download_filename );
1023 1090 $full_filename = wp_tempnam( $download_filename );
1024 - if ( true !== $zip_file->open( $full_filename, ZIPARCHIVE::OVERWRITE ) ) {
1091 + if ( true !== $zip_file->open( $full_filename, ZipArchive::OVERWRITE ) ) {
1025 1092 @unlink( $full_filename ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
1026 1093 TablePress::redirect( array( 'action' => 'export', 'message' => 'error_create_zip_file', 'export_format' => $export['format'], 'csv_delimiter' => $export['csv_delimiter'], 'error_details' => 'The ZIP file could not be opened for writing.' ) );
1027 1094 }
1028 1095
@@ -1052,9 +1119,9 @@
1052 1119 }
1053 1120
1054 1121 // If something went wrong, or no files were added to the ZIP file, bail out.
1055 1122 // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
1056 - if ( ZIPARCHIVE::ER_OK !== $zip_file->status || 0 === $zip_file->numFiles ) {
1123 + if ( ZipArchive::ER_OK !== $zip_file->status || 0 === $zip_file->numFiles ) {
1057 1124 $zip_file->close();
1058 1125 @unlink( $full_filename ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
1059 1126 TablePress::redirect( array( 'action' => 'export', 'message' => 'error_create_zip_file', 'export_format' => $export['format'], 'csv_delimiter' => $export['csv_delimiter'], 'error_details' => 'The ZIP file could not be written or is empty.' ) );
1060 1127 }