PluginProbe
TablePress – Tables in WordPress made easy / 3.4
TablePress – Tables in WordPress made easy v3.4
3.4 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 All 45 releases
← All changes | controllers/controller-admin.php +140 -69 2.4 → 3.4 View file →
@@ -7,8 +7,10 @@
7 7 * @author Tobias Bäthge
8 8 * @since 1.0.0
9 9 */
10 10
11 +declare(strict_types=1);
12 +
11 13 // Prohibit direct script loading.
12 14 defined( 'ABSPATH' ) || die( 'No direct script access allowed!' );
13 15
14 16 /**
@@ -27,9 +29,9 @@
27 29 *
28 30 * @since 1.0.0
29 31 * @var string[]
30 32 */
31 - protected $page_hooks = array();
33 + protected array $page_hooks = array();
32 34
33 35 /**
34 36 * Actions that have a view and admin menu or nav tab menu entry.
35 37 *
@@ -35,17 +37,16 @@
35 37 *
36 38 * @since 1.0.0
37 39 * @var array<string, array<string, bool|string>>
38 40 */
39 - protected $view_actions = array();
41 + protected array $view_actions = array();
40 42
41 43 /**
42 44 * Instance of the TablePress Admin View that is rendered.
43 45 *
44 46 * @since 1.0.0
45 - * @var TablePress_View
46 47 */
47 - protected $view;
48 + protected \TablePress_View $view;
48 49
49 50 /**
50 51 * Initialize the Admin Controller, determine location the admin menu, set up actions.
51 52 *
@@ -94,14 +95,18 @@
94 95 * @param string $entry_name The admin menu entry name. Default "TablePress".
95 96 */
96 97 $admin_menu_entry_name = apply_filters( 'tablepress_admin_menu_entry_name', 'TablePress' );
97 98
99 + if ( TablePress::$model_options->get( 'message_plugin_update' ) && strtotime( '2026-10-10' ) >= strtotime( 'today' ) ) {
100 + $admin_menu_entry_name .= ' <span class="dashicons dashicons-buddicons-community" aria-hidden="true" style="color:orange"></span>';
101 + }
102 +
98 103 $this->init_view_actions();
99 104 $min_access_cap = $this->view_actions['list']['required_cap'];
100 105
101 - if ( $this->is_top_level_page ) {
102 - $icon_url = 'dashicons-list-view';
103 - switch ( $this->parent_page ) {
106 + if ( TablePress::$controller->is_top_level_page ) {
107 + $icon_url = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCIgdmlld0JveD0iLTMyIC0zMiA2NCA2NCIgZmlsbD0iI2ZmZiI+PHBhdGggZD0iTTAtMjUuODU0aC0yNS44NTR2NTEuNzA4aDUxLjcwOFYwSDIxdjIxaC00MnYtNDJIMFoiLz48cGF0aCBkPSJNLTE4LTE4aDEwdjEwaC0xMHpNLTE4LTVoMTBWNWgtMTB6TS01LTVINVY1SC01ek0tMTggOGgxMHYxMGgtMTB6TS01IDhINXYxMEgtNXpNOCA4aDEwdjEwSDh6TTUtMzFoNi4xOHY2LjE4SDV6TTE5LTI1aDYuMTh2Ni4xOEgxOXpNMC0xNWgzLjgydjMuODJIMHpNMTAtMjBoMy44MnYzLjgySDEwek0yNS0xMmgzLjgydjMuODJIMjV6TTgtMTNoMTB2MTBIOHoiLz48L3N2Zz4=';
108 + switch ( TablePress::$controller->parent_page ) {
104 109 case 'top':
105 110 $position = 3; // Position of Dashboard + 1.
106 111 break;
107 112 case 'bottom':
@@ -115,9 +120,9 @@
115 120 // Prevent overwriting existing menu entries.
116 121 while ( isset( $GLOBALS['menu'][ $position ] ) ) {
117 122 ++$position;
118 123 }
119 - add_menu_page( 'TablePress', $admin_menu_entry_name, $min_access_cap, 'tablepress', $callback, $icon_url, $position ); // @phpstan-ignore-line
124 + add_menu_page( 'TablePress', $admin_menu_entry_name, $min_access_cap, 'tablepress', $callback, $icon_url, $position ); // @phpstan-ignore argument.type
120 125 foreach ( $this->view_actions as $action => $entry ) {
121 126 if ( ! $entry['show_entry'] ) {
122 127 continue;
123 128 }
@@ -124,17 +129,17 @@
124 129 $slug = 'tablepress';
125 130 if ( 'list' !== $action ) {
126 131 $slug .= '_' . $action;
127 132 }
128 - // @phpstan-ignore-next-line
129 - $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 );
133 + /* translators: %1$s: Page title, %2$s: Plugin name (TablePress) */
134 + $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 ); // @phpstan-ignore argument.type, argument.type
130 135 if ( false !== $page_hook ) {
131 136 $this->page_hooks[] = $page_hook;
132 137 }
133 138 }
134 139 } 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 );
140 + // @phpstan-ignore argument.type
141 + $page_hook = add_submenu_page( TablePress::$controller->parent_page, 'TablePress', $admin_menu_entry_name, $min_access_cap, 'tablepress', $callback );
137 142 if ( false !== $page_hook ) {
138 143 $this->page_hooks[] = $page_hook;
139 144 }
140 145 }
@@ -179,13 +184,8 @@
179 184 add_action( 'admin_bar_menu', array( $this, 'add_wp_admin_bar_new_content_menu_entry' ), 71 );
180 185 }
181 186
182 187 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 188 }
189 189
190 190 /**
191 191 * Loads additional JavaScript code for the TablePress table block (in the block editor context).
@@ -192,9 +192,8 @@
192 192 *
193 193 * @since 2.2.0
194 194 */
195 195 public function enqueue_block_editor_assets(): void {
196 - // Add table information for the block editor to the page.
197 196 $handle = generate_block_asset_handle( 'tablepress/table', 'editorScript' );
198 197 $data = $this->get_block_editor_data();
199 198 wp_add_inline_script( $handle, $data, 'before' );
200 199 }
@@ -206,9 +205,9 @@
206 205 */
207 206 public function enqueue_block_assets(): void {
208 207 // Load the TablePress default CSS and the user's "Custom CSS" in the block editor iframe.
209 208 if ( is_admin() ) {
210 - TablePress::$controller->enqueue_css();
209 + TablePress::$controller->maybe_enqueue_css();
211 210 }
212 211 }
213 212
214 213 /**
@@ -280,17 +279,17 @@
280 279 $url = TablePress::url( array( 'action' => 'list' ) );
281 280 }
282 281
283 282 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;
283 + // Ensure the global `tp` object exists.
284 + window.tp = window.tp || {};
285 + tp.url = '{$url}';
286 + tp.load_block_preview = {$load_block_preview};
287 + tp.table = {};
288 + tp.table.shortcode = '{$shortcode}';
289 + tp.table.template = JSON.parse( '{$template}' );
290 + tp.tables = JSON.parse( '{$tables}' );
291 + JS;
293 292 }
294 293
295 294 /**
296 295 * Register actions to add "Table" button to "HTML editor" and "Visual editor" toolbars.
@@ -302,15 +301,14 @@
302 301 return;
303 302 }
304 303
305 304 // Only load the toolbar integration if the Block Editor is not used.
306 - if ( TablePress::site_uses_block_editor() ) {
305 + if ( 'block' === TablePress::site_used_editor() ) {
307 306 return;
308 307 }
309 308
310 309 add_thickbox(); // The files are usually already loaded by media upload functions.
311 - $admin_page = TablePress::load_class( 'TablePress_Admin_Page', 'class-admin-page-helper.php', 'classes' );
312 - $admin_page->enqueue_script(
310 + TablePress::enqueue_script(
313 311 'quicktags-button',
314 312 array( 'quicktags', 'media-upload' ),
315 313 array(
316 314 'editor_button' => array(
@@ -318,9 +316,9 @@
318 316 'title' => __( 'Insert a TablePress table', 'tablepress' ),
319 317 'thickbox_title' => __( 'Insert a TablePress table', 'tablepress' ),
320 318 'thickbox_url' => TablePress::url( array( 'action' => 'editor_button_thickbox' ), true, 'admin-post.php' ),
321 319 ),
322 - )
320 + ),
323 321 );
324 322
325 323 // TinyMCE integration.
326 324 if ( user_can_richedit() ) {
@@ -374,9 +372,9 @@
374 372
375 373 $wp_admin_bar->add_menu( array(
376 374 'parent' => 'new-content',
377 375 'id' => 'new-tablepress-table',
378 - 'title' => __( 'TablePress Table', 'tablepress' ),
376 + 'title' => __( 'TablePress table', 'tablepress' ),
379 377 'href' => TablePress::url( array( 'action' => 'add' ) ),
380 378 ) );
381 379 }
382 380
@@ -388,8 +386,21 @@
388 386 public function plugins_page(): void {
389 387 // Add additional links on Plugins page.
390 388 add_filter( 'plugin_action_links_' . TABLEPRESS_BASENAME, array( $this, 'add_plugin_action_links' ) );
391 389 add_filter( 'plugin_row_meta', array( $this, 'add_plugin_row_meta' ), 10, 2 );
390 + $incompatible_superseded_extensions = array(
391 + 'tablepress-datatables-alphabetsearch/tablepress-datatables-alphabetsearch.php',
392 + 'tablepress-datatables-column-filter-widgets/tablepress-datatables-column-filter-widgets.php',
393 + 'tablepress-datatables-columnfilter/tablepress-datatables-columnfilter.php',
394 + 'tablepress-datatables-fixedcolumns/tablepress-datatables-fixedcolumns.php',
395 + 'tablepress-datatables-inverted-filter/tablepress-datatables-inverted-filter.php',
396 + 'tablepress-datatables-row-details/tablepress-datatables-row-details.php',
397 + 'tablepress-datatables-rowgroup/tablepress-datatables-rowgroup.php',
398 + 'tablepress-responsive-tables/tablepress-responsive-tables.php',
399 + );
400 + foreach ( $incompatible_superseded_extensions as $plugin_file ) {
401 + add_action( "after_plugin_row_{$plugin_file}", array( $this, 'add_superseded_extension_meta_row' ), 10, 3 );
402 + }
392 403 }
393 404
394 405 /**
395 406 * Add links to the TablePress entry in the "Plugin" column on the Plugins page.
@@ -427,8 +438,54 @@
427 438 return $links;
428 439 }
429 440
430 441 /**
442 + * Prints a superseded extension notice below certain TablePress Extension plugins' meta rows on the "Plugins" screen.
443 + *
444 + * @since 2.4.1
445 + *
446 + * @param string $plugin_file Path to the plugin file relative to the plugins directory.
447 + * @param array<int, string|string[]|bool> $plugin_data An array of plugin data.
448 + * @param string $status Status filter currently applied to the plugin list.
449 + */
450 + public function add_superseded_extension_meta_row( string $plugin_file, array $plugin_data, string $status ): void {
451 + if ( ! is_plugin_active( $plugin_file ) ) {
452 + return;
453 + }
454 + ?>
455 + <tr class="plugin-update-tr active">
456 + <td colspan="<?php echo esc_attr( $GLOBALS['wp_list_table']->get_column_count() ); ?>" class="plugin-update colspanchange">
457 + <div class="update-message notice inline notice-error notice-alt">
458 + <?php
459 + if ( tb_tp_fs()->is_free_plan() ) {
460 + echo '<p style="font-size:14px;">';
461 + _e( 'This TablePress Extension was retired.', 'tablepress' );
462 + echo ' ';
463 + _e( '<strong>The plugin does no longer work</strong> and will no longer receive updates or support!', 'tablepress' );
464 + echo '<br>';
465 + _e( 'Keeping it activated can lead to errors on your website!', 'tablepress' );
466 + 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>';
467 + echo '</p>';
468 + }
469 + ?>
470 + <style>
471 + /* Remove the separator line between the plugin's and the notice's table row. */
472 + .plugins .active[data-plugin="<?php echo $plugin_file; ?>"] th,
473 + .plugins .active[data-plugin="<?php echo $plugin_file; ?>"] td {
474 + box-shadow: none;
475 + }
476 + /* Hide the plugin update row for the Extension as those won't work anymore anyways. */
477 + .plugins .plugin-update-tr[data-plugin="<?php echo $plugin_file; ?>"] {
478 + display: none;
479 + }
480 + </style>
481 + </div>
482 + </td>
483 + </tr>
484 + <?php
485 + }
486 +
487 + /**
431 488 * Prepare the rendering of an admin screen, by determining the current action, loading necessary data and initializing the view.
432 489 *
433 490 * @since 1.0.0
434 491 */
@@ -434,9 +491,9 @@
434 491 */
435 492 public function load_admin_page(): void {
436 493 // Determine the action from either the GET parameter (for sub-menu entries, and the main admin menu entry).
437 494 $action = ( ! empty( $_GET['action'] ) ) ? $_GET['action'] : 'list'; // Default action is list.
438 - if ( $this->is_top_level_page ) {
495 + if ( TablePress::$controller->is_top_level_page ) {
439 496 // Or, for sub-menu entry of an admin menu "TablePress" entry, get it from the "page" GET parameter.
440 497 if ( 'tablepress' !== $_GET['page'] ) {
441 498 // Actions that are top-level entries, but don't have an action GET parameter (action is after last _ in string).
442 499 $action = substr( $_GET['page'], 11 ); // $_GET['page'] has the format 'tablepress_{$action}'
@@ -443,9 +500,9 @@
443 500 }
444 501 }
445 502
446 503 // 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.)
504 + 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 505 wp_die( __( 'Sorry, you are not allowed to access this page.', 'default' ), 403 );
449 506 }
450 507
451 508 // Don't load TablePress assets on the Freemius opt-in/activation screen.
@@ -465,24 +522,24 @@
465 522 }
466 523
467 524 // Pre-define some view data.
468 525 $data = array(
469 - 'view_actions' => $this->view_actions,
470 - 'message' => ( ! empty( $_GET['message'] ) ) ? $_GET['message'] : false,
471 - 'error_details' => ( ! empty( $_GET['error_details'] ) ) ? $_GET['error_details'] : '',
472 - 'site_uses_block_editor' => TablePress::site_uses_block_editor(),
526 + 'view_actions' => $this->view_actions,
527 + 'message' => ( ! empty( $_GET['message'] ) ) ? $_GET['message'] : false,
528 + 'error_details' => ( ! empty( $_GET['error_details'] ) ) ? rawurldecode( wp_unslash( $_GET['error_details'] ) ) : '',
529 + 'site_used_editor' => TablePress::site_used_editor(),
473 530 );
474 531
475 532 // Depending on the action, load more necessary data for the corresponding view.
476 533 switch ( $action ) {
477 534 case 'list':
478 - $data['table_id'] = ( ! empty( $_GET['table_id'] ) ) ? $_GET['table_id'] : false;
535 + $data['table_id'] = ( isset( $_GET['table_id'] ) ) ? preg_replace( '/[^a-zA-Z0-9_-]/', '', $_GET['table_id'] ) : false;
479 536 // Prime the post meta cache for cached loading of last_editor.
480 537 $data['table_ids'] = TablePress::$model_table->load_all( true );
481 538 $data['messages']['donation_nag'] = $this->maybe_show_donation_message();
482 539 $data['messages']['first_visit'] = ! $data['messages']['donation_nag'] && TablePress::$model_options->get( 'message_first_visit' );
483 540 $data['messages']['plugin_update'] = TablePress::$model_options->get( 'message_plugin_update' );
484 - $data['messages']['superseded_extensions'] = TablePress::$model_options->get( 'message_superseded_extensions' );
541 + $data['messages']['superseded_extensions'] = current_user_can( 'manage_options' ) && TablePress::$model_options->get( 'message_superseded_extensions' );
485 542 $data['table_count'] = count( $data['table_ids'] );
486 543 break;
487 544 case 'about':
488 545 $data['first_activation'] = TablePress::$model_options->get( 'first_activation' );
@@ -493,9 +550,9 @@
493 550 * (called here, as the credentials form posts to this handler again, due to how `request_filesystem_credentials()` works)
494 551 */
495 552 if ( isset( $_GET['item'] ) && 'save_custom_css' === $_GET['item'] ) {
496 553 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
554 + $action = 'options_custom_css'; // To load a different view.
498 555 // Try saving "Custom CSS" to a file, otherwise this gets the HTML for the credentials form.
499 556 $tablepress_css = TablePress::load_class( 'TablePress_CSS', 'class-css.php', 'classes' );
500 557 $result = $tablepress_css->save_custom_css_to_file_plugin_options( TablePress::$model_options->get( 'custom_css' ), TablePress::$model_options->get( 'custom_css_minified' ) );
501 558 if ( is_string( $result ) ) {
@@ -516,12 +573,12 @@
516 573 break;
517 574 }
518 575 $data['frontend_options']['use_custom_css'] = TablePress::$model_options->get( 'use_custom_css' );
519 576 $data['frontend_options']['custom_css'] = TablePress::$model_options->get( 'custom_css' );
520 - $data['user_options']['parent_page'] = $this->parent_page;
577 + $data['user_options']['parent_page'] = TablePress::$controller->parent_page;
521 578 break;
522 579 case 'edit':
523 - if ( empty( $_GET['table_id'] ) ) {
580 + if ( ! isset( $_GET['table_id'] ) || ! preg_match( '/^[a-zA-Z0-9_-]+$/', $_GET['table_id'] ) ) {
524 581 TablePress::redirect( array( 'action' => 'list', 'message' => 'error_no_table' ) );
525 582 }
526 583 // Load table, with table data, options, and visibility settings.
527 584 $data['table'] = TablePress::$model_table->load( $_GET['table_id'], true, true );
@@ -550,9 +607,9 @@
550 607
551 608 $data['tables'][ $table['id'] ] = $table['name'];
552 609 }
553 610 $data['tables_count'] = TablePress::$model_table->count_tables();
554 - $data['export_ids'] = ( ! empty( $_GET['table_id'] ) ) ? explode( ',', $_GET['table_id'] ) : array();
611 + $data['export_ids'] = ( isset( $_GET['table_id'] ) && preg_match( '/^[,a-zA-Z0-9_-]+$/', $_GET['table_id'] ) ) ? explode( ',', $_GET['table_id'] ) : array();
555 612 $exporter = TablePress::load_class( 'TablePress_Export', 'class-export.php', 'classes' );
556 613 $data['zip_support_available'] = $exporter->zip_support_available;
557 614 $data['export_formats'] = $exporter->export_formats;
558 615 $data['csv_delimiters'] = $exporter->csv_delimiters;
@@ -580,13 +637,13 @@
580 637 $data['table_ids'] = $table_ids; // Backward compatibility for the retired "Table Auto Update" Extension, which still relies on this variable name.
581 638 $data['tables_count'] = TablePress::$model_table->count_tables();
582 639 $importer = TablePress::load_class( 'TablePress_Import', 'class-import.php', 'classes' );
583 640 $data['import_type'] = ( ! empty( $_GET['import_type'] ) ) ? $_GET['import_type'] : 'add';
584 - $data['import_existing_table'] = ( ! empty( $_GET['import_existing_table'] ) ) ? $_GET['import_existing_table'] : '';
641 + $data['import_existing_table'] = $_GET['import_existing_table'] ?? '';
585 642 $data['import_source'] = ( ! empty( $_GET['import_source'] ) ) ? $_GET['import_source'] : 'file-upload';
586 - $data['import_url'] = ( ! empty( $_GET['import_url'] ) ) ? wp_unslash( $_GET['import_url'] ) : 'https://';
587 - $data['import_server'] = ( ! empty( $_GET['import_server'] ) ) ? wp_unslash( $_GET['import_server'] ) : ABSPATH;
588 - $data['import_form-field'] = ( ! empty( $_GET['import_form-field'] ) ) ? wp_unslash( $_GET['import_form-field'] ) : '';
643 + $data['import_url'] = ( ! empty( $_GET['import_url'] ) ) ? rawurldecode( wp_unslash( $_GET['import_url'] ) ) : 'https://';
644 + $data['import_server'] = ( ! empty( $_GET['import_server'] ) ) ? rawurldecode( wp_unslash( $_GET['import_server'] ) ) : ABSPATH;
645 + $data['import_form-field'] = ( ! empty( $_GET['import_form-field'] ) ) ? rawurldecode( wp_unslash( $_GET['import_form-field'] ) ) : '';
589 646 $data['legacy_import'] = ( ! empty( $_GET['legacy_import'] ) ) ? $_GET['legacy_import'] : 'false';
590 647 break;
591 648 }
592 649
@@ -810,10 +867,10 @@
810 867
811 868 $add_table = wp_unslash( $_POST['table'] );
812 869
813 870 // Perform confidence checks of posted data.
814 - $name = ( isset( $add_table['name'] ) ) ? $add_table['name'] : '';
815 - $description = ( isset( $add_table['description'] ) ) ? $add_table['description'] : '';
871 + $name = $add_table['name'] ?? '';
872 + $description = $add_table['description'] ?? '';
816 873 if ( ! isset( $add_table['rows'], $add_table['columns'] ) ) {
817 874 TablePress::redirect( array( 'action' => 'add', 'message' => 'error_add', 'error_details' => 'The HTTP POST data does not contain the table size.' ) );
818 875 }
819 876
@@ -873,10 +930,10 @@
873 930 if ( ! empty( $posted_options['admin_menu_parent_page'] ) && '-' !== $posted_options['admin_menu_parent_page'] ) {
874 931 $new_options['admin_menu_parent_page'] = $posted_options['admin_menu_parent_page'];
875 932 // Re-init parent information, as `TablePress::redirect()` URL might be wrong otherwise.
876 933 /** 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 );
934 + TablePress::$controller->parent_page = apply_filters( 'tablepress_admin_menu_parent_page', $posted_options['admin_menu_parent_page'] );
935 + TablePress::$controller->is_top_level_page = in_array( TablePress::$controller->parent_page, array( 'top', 'middle', 'bottom' ), true );
879 936 }
880 937
881 938 // Custom CSS can only be saved if the user is allowed to do so.
882 939 $update_custom_css_files = false;
@@ -887,13 +944,20 @@
887 944 if ( isset( $posted_options['custom_css'] ) ) {
888 945 $new_options['custom_css'] = $posted_options['custom_css'];
889 946
890 947 $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 948
949 + if ( '' !== $new_options['custom_css'] ) {
950 + // Update "Custom CSS" to use DataTables 2 variants instead of old DataTables 1.x CSS classes.
951 + $new_options['custom_css'] = TablePress::convert_datatables_api_data( $new_options['custom_css'] );
952 + // Sanitize and tidy up Custom CSS.
953 + $new_options['custom_css'] = $tablepress_css->sanitize_css( $new_options['custom_css'] );
954 + // Minify Custom CSS.
955 + $new_options['custom_css_minified'] = $tablepress_css->minify_css( $new_options['custom_css'] );
956 + } else {
957 + $new_options['custom_css_minified'] = '';
958 + }
959 +
896 960 // Maybe update CSS files as well.
897 961 $custom_css_file_contents = $tablepress_css->load_custom_css_from_file( 'normal' );
898 962 if ( false === $custom_css_file_contents ) {
899 963 $custom_css_file_contents = '';
@@ -947,10 +1011,9 @@
947 1011
948 1012 if ( empty( $export['format'] ) || ! isset( $exporter->export_formats[ $export['format'] ] ) ) {
949 1013 TablePress::redirect( array( 'action' => 'export', 'message' => 'error_export', 'error_details' => 'The export format is invalid.' ) );
950 1014 }
951 - if ( empty( $export['csv_delimiter'] ) ) {
952 - // Set a value, so that the variable exists.
1015 + if ( ! isset( $export['csv_delimiter'] ) ) {
953 1016 $export['csv_delimiter'] = '';
954 1017 }
955 1018 if ( 'csv' === $export['format'] && ! isset( $exporter->csv_delimiters[ $export['csv_delimiter'] ] ) ) {
956 1019 TablePress::redirect( array( 'action' => 'export', 'message' => 'error_export', 'error_details' => 'The CSV delimiter is invalid.' ) );
@@ -994,9 +1057,13 @@
994 1057 */
995 1058 $download_filename = apply_filters( 'tablepress_export_filename', $download_filename, $table['id'], $table['name'], $export['format'], $export_to_zip );
996 1059 $download_filename = sanitize_file_name( $download_filename );
997 1060 // Export the table.
998 - $export_data = $exporter->export_table( $table, $export['format'], $export['csv_delimiter'] );
1061 + $options = array();
1062 + if ( 'csv' === $export['format'] ) {
1063 + $options['csv_delimiter'] = $export['csv_delimiter'];
1064 + }
1065 + $export_data = $exporter->export_table( $table, $export['format'], $options );
999 1066 /**
1000 1067 * Filters the exported table data.
1001 1068 *
1002 1069 * @since 1.6.0
@@ -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
@@ -1040,9 +1107,13 @@
1040 1107 // Don't export if the table is corrupted.
1041 1108 if ( isset( $table['is_corrupted'] ) && $table['is_corrupted'] ) {
1042 1109 continue;
1043 1110 }
1044 - $export_data = $exporter->export_table( $table, $export['format'], $export['csv_delimiter'] );
1111 + $options = array();
1112 + if ( 'csv' === $export['format'] ) {
1113 + $options['csv_delimiter'] = $export['csv_delimiter'];
1114 + }
1115 + $export_data = $exporter->export_table( $table, $export['format'], $options );
1045 1116 /** This filter is documented in controllers/controller-admin.php */
1046 1117 $export_data = apply_filters( 'tablepress_export_data', $export_data, $table, $export['format'], $export['csv_delimiter'] );
1047 1118 $export_filename = sprintf( '%1$s-%2$s-%3$s.%4$s', $table['id'], $table['name'], wp_date( 'Y-m-d' ), $export['format'] );
1048 1119 /** This filter is documented in controllers/controller-admin.php */
@@ -1052,9 +1123,9 @@
1052 1123 }
1053 1124
1054 1125 // If something went wrong, or no files were added to the ZIP file, bail out.
1055 1126 // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
1056 - if ( ZIPARCHIVE::ER_OK !== $zip_file->status || 0 === $zip_file->numFiles ) {
1127 + if ( ZipArchive::ER_OK !== $zip_file->status || 0 === $zip_file->numFiles ) {
1057 1128 $zip_file->close();
1058 1129 @unlink( $full_filename ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
1059 1130 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 1131 }
@@ -1152,9 +1223,9 @@
1152 1223 'import_source' => $import_config['source'],
1153 1224 'legacy_import' => $import_config['legacy_import'],
1154 1225 );
1155 1226 if ( in_array( $import_config['source'], array( 'url', 'server' ), true ) ) {
1156 - $redirect_parameters[ "import_{$import_config['source']}" ] = $import_config[ $import_config['source'] ];
1227 + $redirect_parameters[ "import_{$import_config['source']}" ] = rawurlencode( $import_config[ $import_config['source'] ] );
1157 1228 }
1158 1229 if ( is_wp_error( $import ) ) {
1159 1230 $redirect_parameters['error_details'] = TablePress::get_wp_error_string( $import );
1160 1231 } elseif ( 0 < count( $import['errors'] ) ) {
@@ -1186,9 +1257,9 @@
1186 1257 *
1187 1258 * @since 1.0.0
1188 1259 */
1189 1260 public function handle_get_action_hide_message(): void {
1190 - $message_item = ! empty( $_GET['item'] ) ? $_GET['item'] : '';
1261 + $message_item = $_GET['item'] ?? '';
1191 1262 TablePress::check_nonce( 'hide_message', $message_item );
1192 1263
1193 1264 if ( ! current_user_can( 'tablepress_list_tables' ) ) {
1194 1265 wp_die( __( 'Sorry, you are not allowed to access this page.', 'default' ), 403 );
@@ -1324,12 +1395,12 @@
1324 1395 $render_options['html_id'] = "tablepress-{$table['id']}";
1325 1396 $render_options['block_preview'] = true;
1326 1397 $_render->set_input( $table, $render_options );
1327 1398 $view_data = array(
1328 - 'table_id' => $table_id,
1329 - 'head_html' => $_render->get_preview_css(),
1330 - 'body_html' => $_render->get_output( 'html' ),
1331 - 'site_uses_block_editor' => TablePress::site_uses_block_editor(),
1399 + 'table_id' => $table_id,
1400 + 'head_html' => $_render->get_preview_css(),
1401 + 'body_html' => $_render->get_output( 'html' ),
1402 + 'site_used_editor' => TablePress::site_used_editor(),
1332 1403 );
1333 1404
1334 1405 $custom_css = TablePress::$model_options->get( 'custom_css' );
1335 1406 $use_custom_css = ( TablePress::$model_options->get( 'use_custom_css' ) && '' !== $custom_css );