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 +274 -132 2.1.83.0 View file →
@@ -25,27 +25,26 @@
25 25 * Page hooks (i.e. names) WordPress uses for the TablePress admin screens,
26 26 * populated in add_admin_menu_entry().
27 27 *
28 28 * @since 1.0.0
29 - * @var array
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 *
36 36 * @since 1.0.0
37 - * @var array
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 *
@@ -59,9 +58,10 @@
59 58
60 59 add_action( 'admin_menu', array( $this, 'add_admin_menu_entry' ) );
61 60 add_action( 'admin_init', array( $this, 'add_admin_actions' ) );
62 61
63 - add_action( 'enqueue_block_editor_assets', array( $this, 'add_block_editor_js' ) );
62 + add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_block_editor_assets' ) );
63 + add_action( 'enqueue_block_assets', array( $this, 'enqueue_block_assets' ) );
64 64 }
65 65
66 66 /**
67 67 * Handler for changing the number of shown tables in the list of tables (via WP List Table class).
@@ -70,11 +70,11 @@
70 70 *
71 71 * @param mixed $screen_option Current value of the filter (probably bool false).
72 72 * @param string $option Option in which the setting is stored.
73 73 * @param int $value Current value of the setting.
74 - * @return bool|int False to not save the changed setting, or the int value to be saved.
74 + * @return int Changed value of the setting
75 75 */
76 - public function save_list_tables_screen_option( $screen_option, $option, $value ) {
76 + public function save_list_tables_screen_option( /* mixed */ $screen_option, string $option, int $value ): int {
77 77 return $value;
78 78 }
79 79
80 80 /**
@@ -81,9 +81,9 @@
81 81 * Add admin screens to the correct place in the admin menu.
82 82 *
83 83 * @since 1.0.0
84 84 */
85 - public function add_admin_menu_entry() {
85 + public function add_admin_menu_entry(): void {
86 86 // Callback for all menu entries.
87 87 $callback = array( $this, 'show_admin_page' );
88 88 /**
89 89 * Filters the TablePress admin menu entry name.
@@ -96,23 +96,27 @@
96 96
97 97 $this->init_view_actions();
98 98 $min_access_cap = $this->view_actions['list']['required_cap'];
99 99
100 - if ( $this->is_top_level_page ) {
101 - $icon_url = 'dashicons-list-view';
102 - 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 ) {
103 103 case 'top':
104 104 $position = 3; // Position of Dashboard + 1.
105 105 break;
106 106 case 'bottom':
107 - $position = ( ++$GLOBALS['_wp_last_utility_menu'] );
107 + $position = isset( $GLOBALS['_wp_last_utility_menu'] ) ? ++$GLOBALS['_wp_last_utility_menu'] : 80;
108 108 break;
109 109 case 'middle':
110 110 default:
111 - $position = ( ++$GLOBALS['_wp_last_object_menu'] );
111 + $position = isset( $GLOBALS['_wp_last_object_menu'] ) ? ++$GLOBALS['_wp_last_object_menu'] : 25;
112 112 break;
113 113 }
114 - add_menu_page( 'TablePress', $admin_menu_entry_name, $min_access_cap, 'tablepress', $callback, $icon_url, $position );
114 + // Prevent overwriting existing menu entries.
115 + while ( isset( $GLOBALS['menu'][ $position ] ) ) {
116 + ++$position;
117 + }
118 + add_menu_page( 'TablePress', $admin_menu_entry_name, $min_access_cap, 'tablepress', $callback, $icon_url, $position ); // @phpstan-ignore argument.type
115 119 foreach ( $this->view_actions as $action => $entry ) {
116 120 if ( ! $entry['show_entry'] ) {
117 121 continue;
118 122 }
@@ -119,12 +123,20 @@
119 123 $slug = 'tablepress';
120 124 if ( 'list' !== $action ) {
121 125 $slug .= '_' . $action;
122 126 }
123 - $this->page_hooks[] = add_submenu_page( 'tablepress', sprintf( __( '%1$s &lsaquo; %2$s', 'tablepress' ), $entry['page_title'], 'TablePress' ), $entry['admin_menu_title'], $entry['required_cap'], $slug, $callback );
127 + // @phpstan-ignore argument.type, argument.type
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 );
129 + if ( false !== $page_hook ) {
130 + $this->page_hooks[] = $page_hook;
131 + }
124 132 }
125 133 } else {
126 - $this->page_hooks[] = 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 );
136 + if ( false !== $page_hook ) {
137 + $this->page_hooks[] = $page_hook;
138 + }
127 139 }
128 140 }
129 141
130 142 /**
@@ -131,9 +143,9 @@
131 143 * Set up handlers for user actions in the backend that exceed plain viewing.
132 144 *
133 145 * @since 1.0.0
134 146 */
135 - public function add_admin_actions() {
147 + public function add_admin_actions(): void {
136 148 // Register the callbacks for processing action requests.
137 149 $post_actions = array( 'list', 'add', 'options', 'export', 'import' );
138 150 $get_actions = array( 'hide_message', 'delete_table', 'copy_table', 'preview_table', 'editor_button_thickbox', 'uninstall_tablepress' );
139 151 foreach ( $post_actions as $action ) {
@@ -166,22 +178,25 @@
166 178 add_action( 'admin_bar_menu', array( $this, 'add_wp_admin_bar_new_content_menu_entry' ), 71 );
167 179 }
168 180
169 181 add_action( 'load-plugins.php', array( $this, 'plugins_page' ) );
170 -
171 - // Add filters and actions for the integration into the WP WXR exporter and importer.
172 - add_action( 'wp_import_insert_post', array( TablePress::$model_table, 'add_table_id_on_wp_import' ), 10, 4 );
173 - add_filter( 'wp_import_post_meta', array( TablePress::$model_table, 'prevent_table_id_post_meta_import_on_wp_import' ), 10, 3 );
174 - add_filter( 'wxr_export_skip_postmeta', array( TablePress::$model_table, 'add_table_id_to_wp_export' ), 10, 3 );
175 182 }
176 183
177 184 /**
178 - * Loads additional JavaScript code for the TablePress table block.
185 + * Loads additional JavaScript code for the TablePress table block (in the block editor context).
179 186 *
180 - * @since 2.0.0
187 + * @since 2.2.0
181 188 */
182 - public function add_block_editor_js() {
183 - // Add table information for the Block Editor to the page.
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 +
198 + // Add table information for the block editor to the page.
184 199 $handle = generate_block_asset_handle( 'tablepress/table', 'editorScript' );
185 200 $data = $this->get_block_editor_data();
186 201 wp_add_inline_script( $handle, $data, 'before' );
187 202 }
@@ -186,8 +201,20 @@
186 201 wp_add_inline_script( $handle, $data, 'before' );
187 202 }
188 203
189 204 /**
205 + * Loads additional CSS code for the TablePress table block (inside the block editor iframe).
206 + *
207 + * @since 2.2.0
208 + */
209 + public function enqueue_block_assets(): void {
210 + // Load the TablePress default CSS and the user's "Custom CSS" in the block editor iframe.
211 + if ( is_admin() ) {
212 + TablePress::$controller->enqueue_css();
213 + }
214 + }
215 +
216 + /**
190 217 * Gets the inline data that is referenced by the Block Editor JavaScript code for the TablePress blocks.
191 218 *
192 219 * @since 2.0.0
193 220 *
@@ -192,9 +219,9 @@
192 219 * @since 2.0.0
193 220 *
194 221 * @return string JavaScript code for the Block Editor.
195 222 */
196 - protected function get_block_editor_data() {
223 + protected function get_block_editor_data(): string {
197 224 $tables = array();
198 225 // Load all table IDs without priming the post meta cache, as table options/visibility are not needed.
199 226 $table_ids = TablePress::$model_table->load_all( false );
200 227 foreach ( $table_ids as $table_id ) {
@@ -199,8 +226,14 @@
199 226 $table_ids = TablePress::$model_table->load_all( false );
200 227 foreach ( $table_ids as $table_id ) {
201 228 // Load table, without table data, options, and visibility settings.
202 229 $table = TablePress::$model_table->load( $table_id, false, false );
230 +
231 + // Skip tables that could not be loaded.
232 + if ( is_wp_error( $table ) ) {
233 + continue;
234 + }
235 +
203 236 if ( '' === trim( $table['name'] ) ) {
204 237 $table['name'] = __( '(no name)', 'tablepress' );
205 238 }
206 239 $tables[ $table_id ] = esc_html( $table['name'] );
@@ -210,22 +243,30 @@
210 243 * Filters the list of table IDs and names that is passed to the block editor, and is then used in the dropdown of the TablePress table block.
211 244 *
212 245 * @since 2.0.0
213 246 *
214 - * @param array $tables List of table names, the table ID is the array key.
247 + * @param array<string, string> $tables List of table names, the table ID is the array key.
215 248 */
216 249 $tables = apply_filters( 'tablepress_block_editor_tables_list', $tables );
217 250
218 - $tables = wp_json_encode( $tables, TABLEPRESS_JSON_OPTIONS );
219 - // Print them inside a `JSON.parse()` call in JS for speed gains, with necessary escaping of `</script>`, `'`, and `\`.
220 - $tables = str_replace( array( '</script>', '\\', "'" ), array( '<\/script>', '\\\\', "\'" ), $tables );
251 + $tables = wp_json_encode( $tables, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES );
252 + if ( false === $tables ) {
253 + // JSON encoding failed, return an error object. Use a prefixed "_error" key to avoid conflicts with intentionally added "error" keys.
254 + $tables = '{ "_error": "The data could not be encoded to JSON!" }';
255 + }
256 + // Print the JSON data inside a `JSON.parse()` call in JS for speed gains, with necessary escaping of `\` and `'`.
257 + $tables = str_replace( array( '\\', "'" ), array( '\\\\', "\'" ), $tables );
221 258
222 259 $shortcode = esc_js( TablePress::$shortcode );
223 260
224 261 $template = TablePress::$model_table->get_table_template();
225 - $template = wp_json_encode( $template['options'], TABLEPRESS_JSON_OPTIONS );
226 - // Print them inside a `JSON.parse()` call in JS for speed gains, with necessary escaping of `</script>`, `'`, and `\`.
227 - $template = str_replace( array( '</script>', '\\', "'" ), array( '<\/script>', '\\\\', "\'" ), $template );
262 + $template = wp_json_encode( $template['options'], JSON_HEX_TAG | JSON_UNESCAPED_SLASHES );
263 + if ( false === $template ) {
264 + // JSON encoding failed, return an error object. Use a prefixed "_error" key to avoid conflicts with intentionally added "error" keys.
265 + $template = '{ "_error": "The data could not be encoded to JSON!" }';
266 + }
267 + // Print the JSON data inside a `JSON.parse()` call in JS for speed gains, with necessary escaping of `\` and `'`.
268 + $template = str_replace( array( '\\', "'" ), array( '\\\\', "\'" ), $template );
228 269
229 270 /**
230 271 * Filters whether the table block preview should be loaded via a <ServerSideRender> in the block editor.
231 272 *
@@ -241,17 +282,17 @@
241 282 $url = TablePress::url( array( 'action' => 'list' ) );
242 283 }
243 284
244 285 return <<<JS
245 -// Ensure the global `tp` object exists.
246 -window.tp = window.tp || {};
247 -tp.url = '{$url}';
248 -tp.load_block_preview = {$load_block_preview};
249 -tp.table = {};
250 -tp.table.shortcode = '{$shortcode}';
251 -tp.table.template = JSON.parse( '{$template}' );
252 -tp.tables = JSON.parse( '{$tables}' );
253 -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;
254 295 }
255 296
256 297 /**
257 298 * Register actions to add "Table" button to "HTML editor" and "Visual editor" toolbars.
@@ -257,9 +298,9 @@
257 298 * Register actions to add "Table" button to "HTML editor" and "Visual editor" toolbars.
258 299 *
259 300 * @since 1.0.0
260 301 */
261 - public function add_editor_buttons() {
302 + public function add_editor_buttons(): void {
262 303 if ( ! current_user_can( 'tablepress_list_tables' ) ) {
263 304 return;
264 305 }
265 306
@@ -279,9 +320,9 @@
279 320 'title' => __( 'Insert a TablePress table', 'tablepress' ),
280 321 'thickbox_title' => __( 'Insert a TablePress table', 'tablepress' ),
281 322 'thickbox_url' => TablePress::url( array( 'action' => 'editor_button_thickbox' ), true, 'admin-post.php' ),
282 323 ),
283 - )
324 + ),
284 325 );
285 326
286 327 // TinyMCE integration.
287 328 if ( user_can_richedit() ) {
@@ -294,12 +335,12 @@
294 335 * Adds the "Table" button to the TinyMCE toolbar.
295 336 *
296 337 * @since 1.0.0
297 338 *
298 - * @param array $buttons Current set of buttons in the TinyMCE toolbar.
299 - * @return array Extended set of buttons in the TinyMCE toolbar, including the "Table" button.
339 + * @param string[] $buttons Current set of buttons in the TinyMCE toolbar.
340 + * @return string[] Extended set of buttons in the TinyMCE toolbar, including the "Table" button.
300 341 */
301 - public function add_tinymce_button( array $buttons ) {
342 + public function add_tinymce_button( array $buttons ): array {
302 343 $buttons[] = 'tablepress_insert_table';
303 344 return $buttons;
304 345 }
305 346
@@ -307,12 +348,12 @@
307 348 * Registers the "Table" button plugin for the TinyMCE editor.
308 349 *
309 350 * @since 1.0.0
310 351 *
311 - * @param array $plugins Current set of registered TinyMCE plugins.
312 - * @return array Extended set of registered TinyMCE plugins, including the "Table" button plugin.
352 + * @param array<string, string> $plugins Current set of registered TinyMCE plugins.
353 + * @return array<string, string> Extended set of registered TinyMCE plugins, including the "Table" button plugin.
313 354 */
314 - public function add_tinymce_plugin( array $plugins ) {
355 + public function add_tinymce_plugin( array $plugins ): array {
315 356 $plugins['tablepress_tinymce'] = plugins_url( 'admin/js/build/tinymce-button.js', TABLEPRESS__FILE__ );
316 357 return $plugins;
317 358 }
318 359
@@ -322,9 +363,9 @@
322 363 * @since 1.0.0
323 364 *
324 365 * @param WP_Admin_Bar $wp_admin_bar The current WP Admin Bar object.
325 366 */
326 - public function add_wp_admin_bar_new_content_menu_entry( $wp_admin_bar ) {
367 + public function add_wp_admin_bar_new_content_menu_entry( WP_Admin_Bar $wp_admin_bar ): void {
327 368 if ( ! current_user_can( 'tablepress_add_tables' ) ) {
328 369 return;
329 370 }
330 371
@@ -345,12 +386,24 @@
345 386 * Handle actions for loading of Plugins page.
346 387 *
347 388 * @since 1.0.0
348 389 */
349 - public function plugins_page() {
390 + public function plugins_page(): void {
350 391 // Add additional links on Plugins page.
351 392 add_filter( 'plugin_action_links_' . TABLEPRESS_BASENAME, array( $this, 'add_plugin_action_links' ) );
352 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 + }
353 406 }
354 407
355 408 /**
356 409 * Add links to the TablePress entry in the "Plugin" column on the Plugins page.
@@ -356,14 +409,14 @@
356 409 * Add links to the TablePress entry in the "Plugin" column on the Plugins page.
357 410 *
358 411 * @since 1.0.0
359 412 *
360 - * @param array $links List of links to print in the "Plugin" column on the Plugins page.
361 - * @return array Extended list of links to print in the "Plugin" column on the Plugins page.
413 + * @param string[] $links List of links to print in the "Plugin" column on the Plugins page.
414 + * @return string[] Extended list of links to print in the "Plugin" column on the Plugins page.
362 415 */
363 - public function add_plugin_action_links( array $links ) {
416 + public function add_plugin_action_links( array $links ): array {
364 417 if ( current_user_can( 'tablepress_list_tables' ) ) {
365 - $links[] = '<a href="' . TablePress::url() . '">' . __( 'Plugin page', 'tablepress' ) . '</a>';
418 + $links[] = '<a href="' . esc_url( TablePress::url() ) . '">' . __( 'Plugin page', 'tablepress' ) . '</a>';
366 419 }
367 420 return $links;
368 421 }
369 422
@@ -371,19 +424,19 @@
371 424 * Add links to the TablePress entry in the "Description" column on the Plugins page.
372 425 *
373 426 * @since 1.0.0
374 427 *
375 - * @param array $links List of links to print in the "Description" column on the Plugins page.
376 - * @param string $file Name of the plugin.
377 - * @return array Extended list of links to print in the "Description" column on the Plugins page.
428 + * @param string[] $links List of links to print in the "Description" column on the Plugins page.
429 + * @param string $file Name of the plugin.
430 + * @return string[] Extended list of links to print in the "Description" column on the Plugins page.
378 431 */
379 - public function add_plugin_row_meta( array $links, $file ) {
432 + public function add_plugin_row_meta( array $links, string $file ): array {
380 433 if ( TABLEPRESS_BASENAME === $file ) {
381 434 $links[] = '<a href="https://tablepress.org/faq/" title="' . esc_attr__( 'Frequently Asked Questions', 'tablepress' ) . '">' . __( 'FAQ', 'tablepress' ) . '</a>';
382 435 $links[] = '<a href="https://tablepress.org/documentation/">' . __( 'Documentation', 'tablepress' ) . '</a>';
383 436 $links[] = '<a href="https://tablepress.org/support/">' . __( 'Support', 'tablepress' ) . '</a>';
384 - if ( tb_tp_fs()->is_free_plan() ) {
385 - $links[] = '<a href="' . 'https://tablepress.org/premium/' . '" title="' . esc_attr__( 'Check out the Premium version of TablePress!', 'tablepress' ) . '"><strong>' . __( 'Go Premium', 'tablepress' ) . '</strong></a>';
437 + if ( ! TABLEPRESS_IS_PLAYGROUND_PREVIEW && tb_tp_fs()->is_free_plan() ) {
438 + $links[] = '<a href="https://tablepress.org/premium/?utm_source=plugin&utm_medium=textlink&utm_content=plugins-screen" title="' . esc_attr__( 'Check out the Premium version of TablePress!', 'tablepress' ) . '"><strong>' . __( 'Go Premium', 'tablepress' ) . '</strong></a>';
386 439 }
387 440 }
388 441 return $links;
389 442 }
@@ -388,16 +441,62 @@
388 441 return $links;
389 442 }
390 443
391 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 + /**
392 491 * Prepare the rendering of an admin screen, by determining the current action, loading necessary data and initializing the view.
393 492 *
394 493 * @since 1.0.0
395 494 */
396 - public function load_admin_page() {
495 + public function load_admin_page(): void {
397 496 // Determine the action from either the GET parameter (for sub-menu entries, and the main admin menu entry).
398 497 $action = ( ! empty( $_GET['action'] ) ) ? $_GET['action'] : 'list'; // Default action is list.
399 - if ( $this->is_top_level_page ) {
498 + if ( TablePress::$controller->is_top_level_page ) {
400 499 // Or, for sub-menu entry of an admin menu "TablePress" entry, get it from the "page" GET parameter.
401 500 if ( 'tablepress' !== $_GET['page'] ) {
402 501 // Actions that are top-level entries, but don't have an action GET parameter (action is after last _ in string).
403 502 $action = substr( $_GET['page'], 11 ); // $_GET['page'] has the format 'tablepress_{$action}'
@@ -404,9 +503,9 @@
404 503 }
405 504 }
406 505
407 506 // Check if action is a supported action, and whether the user is allowed to access this screen.
408 - if ( ! isset( $this->view_actions[ $action ] ) || ! current_user_can( $this->view_actions[ $action ]['required_cap'] ) ) {
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.)
409 508 wp_die( __( 'Sorry, you are not allowed to access this page.', 'default' ), 403 );
410 509 }
411 510
412 511 // Don't load TablePress assets on the Freemius opt-in/activation screen.
@@ -421,9 +520,9 @@
421 520 * Set the `$typenow` global to the current CPT ourselves, as `WP_Screen::get()` does not determine the CPT correctly.
422 521 * This is necessary as the WP Admin Menu can otherwise highlight wrong entries, see https://github.com/TablePress/TablePress/issues/24.
423 522 */
424 523 if ( isset( $_GET['post_type'] ) && post_type_exists( $_GET['post_type'] ) ) {
425 - $GLOBALS['typenow'] = $_GET['post_type'];
524 + $GLOBALS['typenow'] = $_GET['post_type']; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
426 525 }
427 526
428 527 // Pre-define some view data.
429 528 $data = array(
@@ -438,17 +537,16 @@
438 537 case 'list':
439 538 $data['table_id'] = ( ! empty( $_GET['table_id'] ) ) ? $_GET['table_id'] : false;
440 539 // Prime the post meta cache for cached loading of last_editor.
441 540 $data['table_ids'] = TablePress::$model_table->load_all( true );
442 - $data['messages']['first_visit'] = TablePress::$model_options->get( 'message_first_visit' );
443 - $data['messages']['plugin_update_message'] = TablePress::$model_options->get( 'message_plugin_update' );
444 - $data['messages']['donation_message'] = $this->maybe_show_donation_message();
541 + $data['messages']['donation_nag'] = $this->maybe_show_donation_message();
542 + $data['messages']['first_visit'] = ! $data['messages']['donation_nag'] && TablePress::$model_options->get( 'message_first_visit' );
543 + $data['messages']['plugin_update'] = TablePress::$model_options->get( 'message_plugin_update' );
544 + $data['messages']['superseded_extensions'] = current_user_can( 'manage_options' ) && TablePress::$model_options->get( 'message_superseded_extensions' );
445 545 $data['table_count'] = count( $data['table_ids'] );
446 546 break;
447 547 case 'about':
448 548 $data['first_activation'] = TablePress::$model_options->get( 'first_activation' );
449 - $exporter = TablePress::load_class( 'TablePress_Export', 'class-export.php', 'classes' );
450 - $data['zip_support_available'] = $exporter->zip_support_available;
451 549 break;
452 550 case 'options':
453 551 /*
454 552 * Maybe try saving "Custom CSS" to a file:
@@ -455,9 +553,9 @@
455 553 * (called here, as the credentials form posts to this handler again, due to how `request_filesystem_credentials()` works)
456 554 */
457 555 if ( isset( $_GET['item'] ) && 'save_custom_css' === $_GET['item'] ) {
458 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.
459 - $action = 'options_custom_css'; // to load a different view
557 + $action = 'options_custom_css'; // To load a different view.
460 558 // Try saving "Custom CSS" to a file, otherwise this gets the HTML for the credentials form.
461 559 $tablepress_css = TablePress::load_class( 'TablePress_CSS', 'class-css.php', 'classes' );
462 560 $result = $tablepress_css->save_custom_css_to_file_plugin_options( TablePress::$model_options->get( 'custom_css' ), TablePress::$model_options->get( 'custom_css_minified' ) );
463 561 if ( is_string( $result ) ) {
@@ -478,9 +576,9 @@
478 576 break;
479 577 }
480 578 $data['frontend_options']['use_custom_css'] = TablePress::$model_options->get( 'use_custom_css' );
481 579 $data['frontend_options']['custom_css'] = TablePress::$model_options->get( 'custom_css' );
482 - $data['user_options']['parent_page'] = $this->parent_page;
580 + $data['user_options']['parent_page'] = TablePress::$controller->parent_page;
483 581 break;
484 582 case 'edit':
485 583 if ( empty( $_GET['table_id'] ) ) {
486 584 TablePress::redirect( array( 'action' => 'list', 'message' => 'error_no_table' ) );
@@ -495,29 +593,54 @@
495 593 }
496 594 break;
497 595 case 'export':
498 596 // Load all table IDs without priming the post meta cache, as table options/visibility are not needed.
499 - $data['table_ids'] = TablePress::$model_table->load_all( false );
597 + $table_ids = TablePress::$model_table->load_all( false );
598 + $data['tables'] = array();
599 + foreach ( $table_ids as $table_id ) {
600 + if ( ! current_user_can( 'tablepress_export_table', $table_id ) ) {
601 + continue;
602 + }
603 + // Load table, without table data, options, and visibility settings.
604 + $table = TablePress::$model_table->load( $table_id, false, false );
605 +
606 + // Skip tables that could not be loaded.
607 + if ( is_wp_error( $table ) ) {
608 + continue;
609 + }
610 +
611 + $data['tables'][ $table['id'] ] = $table['name'];
612 + }
500 613 $data['tables_count'] = TablePress::$model_table->count_tables();
501 - if ( ! empty( $_GET['table_id'] ) ) {
502 - $data['export_ids'] = explode( ',', $_GET['table_id'] );
503 - } else {
504 - // Just show empty export form.
505 - $data['export_ids'] = array();
506 - }
614 + $data['export_ids'] = ( ! empty( $_GET['table_id'] ) ) ? explode( ',', $_GET['table_id'] ) : array();
507 615 $exporter = TablePress::load_class( 'TablePress_Export', 'class-export.php', 'classes' );
508 616 $data['zip_support_available'] = $exporter->zip_support_available;
509 617 $data['export_formats'] = $exporter->export_formats;
510 618 $data['csv_delimiters'] = $exporter->csv_delimiters;
511 - $data['export_format'] = ( ! empty( $_GET['export_format'] ) ) ? $_GET['export_format'] : false;
619 + $data['export_format'] = ( ! empty( $_GET['export_format'] ) ) ? $_GET['export_format'] : 'csv';
512 620 $data['csv_delimiter'] = ( ! empty( $_GET['csv_delimiter'] ) ) ? $_GET['csv_delimiter'] : _x( ',', 'Default CSV delimiter in the translated language (";", ",", or "tab")', 'tablepress' );
513 621 break;
514 622 case 'import':
515 623 // Load all table IDs without priming the post meta cache, as table options/visibility are not needed.
516 - $data['table_ids'] = TablePress::$model_table->load_all( false );
624 + $table_ids = TablePress::$model_table->load_all( false );
625 + $data['tables'] = array();
626 + foreach ( $table_ids as $table_id ) {
627 + if ( ! current_user_can( 'tablepress_edit_table', $table_id ) ) {
628 + continue;
629 + }
630 + // Load table, without table data, options, and visibility settings.
631 + $table = TablePress::$model_table->load( $table_id, false, false );
632 +
633 + // Skip tables that could not be loaded.
634 + if ( is_wp_error( $table ) ) {
635 + continue;
636 + }
637 +
638 + $data['tables'][ $table['id'] ] = $table['name'];
639 + }
640 + $data['table_ids'] = $table_ids; // Backward compatibility for the retired "Table Auto Update" Extension, which still relies on this variable name.
517 641 $data['tables_count'] = TablePress::$model_table->count_tables();
518 642 $importer = TablePress::load_class( 'TablePress_Import', 'class-import.php', 'classes' );
519 - $data['zip_support_available'] = $importer->zip_support_available;
520 643 $data['import_type'] = ( ! empty( $_GET['import_type'] ) ) ? $_GET['import_type'] : 'add';
521 644 $data['import_existing_table'] = ( ! empty( $_GET['import_existing_table'] ) ) ? $_GET['import_existing_table'] : '';
522 645 $data['import_source'] = ( ! empty( $_GET['import_source'] ) ) ? $_GET['import_source'] : 'file-upload';
523 646 $data['import_url'] = ( ! empty( $_GET['import_url'] ) ) ? wp_unslash( $_GET['import_url'] ) : 'https://';
@@ -531,10 +654,10 @@
531 654 * Filters the data that is passed to the current TablePress View.
532 655 *
533 656 * @since 1.0.0
534 657 *
535 - * @param array $data Data for the view.
536 - * @param string $action The current action for the view.
658 + * @param array<string, mixed> $data Data for the view.
659 + * @param string $action The current action for the view.
537 660 */
538 661 $data = apply_filters( 'tablepress_view_data', $data, $action );
539 662
540 663 // Prepare and initialize the view.
@@ -545,9 +668,9 @@
545 668 * Render the view that has been initialized in load_admin_page() (called by WordPress when the actual page content is needed).
546 669 *
547 670 * @since 1.0.0
548 671 */
549 - public function show_admin_page() {
672 + public function show_admin_page(): void {
550 673 $this->view->render();
551 674 }
552 675
553 676 /**
@@ -556,9 +679,9 @@
556 679 * @since 1.0.0
557 680 *
558 681 * @return bool Whether the message shall be shown on the "All Tables" screen.
559 682 */
560 - protected function maybe_show_donation_message() {
683 + protected function maybe_show_donation_message(): bool {
561 684 // Only show the message to plugin admins.
562 685 if ( ! current_user_can( 'tablepress_edit_options' ) ) {
563 686 return false;
564 687 }
@@ -576,9 +699,9 @@
576 699 * Init list of actions that have a view with their titles/names/caps.
577 700 *
578 701 * @since 1.0.0
579 702 */
580 - protected function init_view_actions() {
703 + protected function init_view_actions(): void {
581 704 $this->view_actions = array(
582 705 'list' => array(
583 706 'show_entry' => true,
584 707 'page_title' => __( 'All Tables', 'tablepress' ),
@@ -634,9 +757,9 @@
634 757 * Filters the available TablePres Views/Actions and their parameters.
635 758 *
636 759 * @since 1.0.0
637 760 *
638 - * @param array $view_actions The available Views/Actions and their parameters.
761 + * @param array<string, array<string, bool|string>> $view_actions The available Views/Actions and their parameters.
639 762 */
640 763 $this->view_actions = apply_filters( 'tablepress_admin_view_actions', $this->view_actions );
641 764 }
642 765
@@ -648,9 +771,9 @@
648 771 * Handle Bulk Actions (Copy, Export, Delete) on "All Tables" list screen.
649 772 *
650 773 * @since 1.0.0
651 774 */
652 - public function handle_post_action_list() {
775 + public function handle_post_action_list(): void {
653 776 TablePress::check_nonce( 'list' );
654 777
655 778 if ( isset( $_POST['bulk-action-selector-top'] ) && '-1' !== $_POST['bulk-action-selector-top'] ) {
656 779 $bulk_action = $_POST['bulk-action-selector-top'];
@@ -691,9 +814,9 @@
691 814 * To export, redirect to "Export" screen, with selected table IDs.
692 815 */
693 816 $table_ids = implode( ',', $tables );
694 817 TablePress::redirect( array( 'action' => 'export', 'table_id' => $table_ids ) );
695 - break;
818 + // break; // unreachable.
696 819 case 'delete':
697 820 foreach ( $tables as $table_id ) {
698 821 if ( current_user_can( 'tablepress_delete_table', $table_id ) ) {
699 822 $deleted = TablePress::$model_table->delete( $table_id );
@@ -706,9 +829,9 @@
706 829 }
707 830 break;
708 831 }
709 832
710 - if ( 0 !== count( $no_success ) ) { // @TODO: maybe pass this information to the view?
833 + if ( 0 !== count( $no_success ) ) { // @todo maybe pass this information to the view?
711 834 $message = "error_{$bulk_action}_not_all_tables";
712 835 } else {
713 836 $plural = ( count( $tables ) > 1 ) ? '_plural' : '';
714 837 $message = "success_{$bulk_action}{$plural}";
@@ -733,9 +856,9 @@
733 856 * Add a table, according to the parameters on the "Add new Table" screen.
734 857 *
735 858 * @since 1.0.0
736 859 */
737 - public function handle_post_action_add() {
860 + public function handle_post_action_add(): void {
738 861 TablePress::check_nonce( 'add' );
739 862
740 863 if ( ! current_user_can( 'tablepress_add_tables' ) ) {
741 864 wp_die( __( 'Sorry, you are not allowed to access this page.', 'default' ), 403 );
@@ -746,11 +869,11 @@
746 869 }
747 870
748 871 $add_table = wp_unslash( $_POST['table'] );
749 872
750 - // Perform sanity checks of posted data.
751 - $name = ( isset( $add_table['name'] ) ) ? $add_table['name'] : '';
752 - $description = ( isset( $add_table['description'] ) ) ? $add_table['description'] : '';
873 + // Perform confidence checks of posted data.
874 + $name = $add_table['name'] ?? '';
875 + $description = $add_table['description'] ?? '';
753 876 if ( ! isset( $add_table['rows'], $add_table['columns'] ) ) {
754 877 TablePress::redirect( array( 'action' => 'add', 'message' => 'error_add', 'error_details' => 'The HTTP POST data does not contain the table size.' ) );
755 878 }
756 879
@@ -789,9 +912,9 @@
789 912 * Save changed "Plugin Options".
790 913 *
791 914 * @since 1.0.0
792 915 */
793 - public function handle_post_action_options() {
916 + public function handle_post_action_options(): void {
794 917 TablePress::check_nonce( 'options' );
795 918
796 919 if ( ! current_user_can( 'tablepress_access_options_screen' ) ) {
797 920 wp_die( __( 'Sorry, you are not allowed to access this page.', 'default' ), 403 );
@@ -810,10 +933,10 @@
810 933 if ( ! empty( $posted_options['admin_menu_parent_page'] ) && '-' !== $posted_options['admin_menu_parent_page'] ) {
811 934 $new_options['admin_menu_parent_page'] = $posted_options['admin_menu_parent_page'];
812 935 // Re-init parent information, as `TablePress::redirect()` URL might be wrong otherwise.
813 936 /** This filter is documented in classes/class-controller.php */
814 - $this->parent_page = apply_filters( 'tablepress_admin_menu_parent_page', $posted_options['admin_menu_parent_page'] );
815 - $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 );
816 939 }
817 940
818 941 // Custom CSS can only be saved if the user is allowed to do so.
819 942 $update_custom_css_files = false;
@@ -824,13 +947,20 @@
824 947 if ( isset( $posted_options['custom_css'] ) ) {
825 948 $new_options['custom_css'] = $posted_options['custom_css'];
826 949
827 950 $tablepress_css = TablePress::load_class( 'TablePress_CSS', 'class-css.php', 'classes' );
828 - // Sanitize and tidy up Custom CSS.
829 - $new_options['custom_css'] = $tablepress_css->sanitize_css( $new_options['custom_css'] );
830 - // Minify Custom CSS.
831 - $new_options['custom_css_minified'] = $tablepress_css->minify_css( $new_options['custom_css'] );
832 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 +
833 963 // Maybe update CSS files as well.
834 964 $custom_css_file_contents = $tablepress_css->load_custom_css_from_file( 'normal' );
835 965 if ( false === $custom_css_file_contents ) {
836 966 $custom_css_file_contents = '';
@@ -861,9 +991,9 @@
861 991 * Export selected tables.
862 992 *
863 993 * @since 1.0.0
864 994 */
865 - public function handle_post_action_export() {
995 + public function handle_post_action_export(): void {
866 996 TablePress::check_nonce( 'export' );
867 997
868 998 if ( ! current_user_can( 'tablepress_export_tables' ) ) {
869 999 wp_die( __( 'Sorry, you are not allowed to access this page.', 'default' ), 403 );
@@ -874,12 +1004,13 @@
874 1004 }
875 1005
876 1006 $export = wp_unslash( $_POST['export'] );
877 1007
878 - if ( empty( $export['tables'] ) ) {
1008 + if ( empty( $export['tables_list'] ) ) {
879 1009 TablePress::redirect( array( 'action' => 'export', 'message' => 'error_export', 'error_details' => 'The HTTP POST data does not contain tables.' ) );
880 1010 }
881 1011
1012 + /** @var TablePress_Export $exporter */ // phpcs:ignore Generic.Commenting.DocComment.MissingShort
882 1013 $exporter = TablePress::load_class( 'TablePress_Export', 'class-export.php', 'classes' );
883 1014
884 1015 if ( empty( $export['format'] ) || ! isset( $exporter->export_formats[ $export['format'] ] ) ) {
885 1016 TablePress::redirect( array( 'action' => 'export', 'message' => 'error_export', 'error_details' => 'The export format is invalid.' ) );
@@ -891,10 +1022,9 @@
891 1022 if ( 'csv' === $export['format'] && ! isset( $exporter->csv_delimiters[ $export['csv_delimiter'] ] ) ) {
892 1023 TablePress::redirect( array( 'action' => 'export', 'message' => 'error_export', 'error_details' => 'The CSV delimiter is invalid.' ) );
893 1024 }
894 1025
895 - // Use list of tables from concatenated field if available (as that's hopefully not truncated by Suhosin, which is possible for $export['tables']).
896 - $tables = ( ! empty( $export['tables_list'] ) ) ? explode( ',', $export['tables_list'] ) : $export['tables'];
1026 + $tables = explode( ',', $export['tables_list'] );
897 1027
898 1028 // Determine if ZIP file support is available.
899 1029 if ( $exporter->zip_support_available
900 1030 && ( ( isset( $export['zip_file'] ) && 'true' === $export['zip_file'] ) || count( $tables ) > 1 ) ) {
@@ -904,9 +1034,9 @@
904 1034 $export_to_zip = false;
905 1035 }
906 1036
907 1037 if ( ! $export_to_zip ) {
908 - // This is only possible for one table, so take the first one.
1038 + // Exporting without a ZIP file is only possible for one table, so take the first one.
909 1039 if ( ! current_user_can( 'tablepress_export_table', $tables[0] ) ) {
910 1040 wp_die( __( 'Sorry, you are not allowed to access this page.', 'default' ), 403 );
911 1041 }
912 1042 // Load table, with table data, options, and visibility settings.
@@ -937,12 +1067,12 @@
937 1067 * Filters the exported table data.
938 1068 *
939 1069 * @since 1.6.0
940 1070 *
941 - * @param string $export_data The exported table data.
942 - * @param array $table Table to be exported.
943 - * @param string $export_format Format for the export ('csv', 'html', 'json').
944 - * @param string $csv_delimiter Delimiter for CSV export.
1071 + * @param string $export_data The exported table data.
1072 + * @param array<string, mixed> $table Table to be exported.
1073 + * @param string $export_format Format for the export ('csv', 'html', 'json').
1074 + * @param string $csv_delimiter Delimiter for CSV export.
945 1075 */
946 1076 $export_data = apply_filters( 'tablepress_export_data', $export_data, $table, $export['format'], $export['csv_delimiter'] );
947 1077 $download_data = $export_data;
948 1078 } else {
@@ -957,10 +1087,10 @@
957 1087 /** This filter is documented in controllers/controller-admin.php */
958 1088 $download_filename = apply_filters( 'tablepress_export_filename', $download_filename, '', '', $export['format'], $export_to_zip );
959 1089 $download_filename = sanitize_file_name( $download_filename );
960 1090 $full_filename = wp_tempnam( $download_filename );
961 - if ( true !== $zip_file->open( $full_filename, ZIPARCHIVE::OVERWRITE ) ) {
962 - @unlink( $full_filename );
1091 + if ( true !== $zip_file->open( $full_filename, ZipArchive::OVERWRITE ) ) {
1092 + @unlink( $full_filename ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
963 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.' ) );
964 1094 }
965 1095
966 1096 foreach ( $tables as $table_id ) {
@@ -989,11 +1119,11 @@
989 1119 }
990 1120
991 1121 // If something went wrong, or no files were added to the ZIP file, bail out.
992 1122 // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
993 - if ( ! ZIPARCHIVE::ER_OK === $zip_file->status || 0 === $zip_file->numFiles ) {
1123 + if ( ZipArchive::ER_OK !== $zip_file->status || 0 === $zip_file->numFiles ) {
994 1124 $zip_file->close();
995 - @unlink( $full_filename );
1125 + @unlink( $full_filename ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
996 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.' ) );
997 1127 }
998 1128 $zip_file->close();
999 1129
@@ -998,9 +1128,13 @@
998 1128 $zip_file->close();
999 1129
1000 1130 // Load contents of the ZIP file, to send it as a download.
1001 1131 $download_data = file_get_contents( $full_filename );
1002 - @unlink( $full_filename );
1132 + if ( false === $download_data ) {
1133 + @unlink( $full_filename ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
1134 + TablePress::redirect( array( 'action' => 'export', 'message' => 'error_create_zip_file', 'export_format' => $export['format'], 'csv_delimiter' => $export['csv_delimiter'], 'error_details' => 'The ZIP file content could not be read.' ) );
1135 + }
1136 + @unlink( $full_filename ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
1003 1137 }
1004 1138
1005 1139 // Send download headers for export file.
1006 1140 header( 'Content-Description: File Transfer' );
@@ -1023,9 +1157,9 @@
1023 1157 * Import data from existing source (Upload, URL, Server, Direct input).
1024 1158 *
1025 1159 * @since 1.0.0
1026 1160 */
1027 - public function handle_post_action_import() {
1161 + public function handle_post_action_import(): void {
1028 1162 TablePress::check_nonce( 'import' );
1029 1163
1030 1164 if ( ! current_user_can( 'tablepress_import_tables' ) ) {
1031 1165 wp_die( __( 'Sorry, you are not allowed to access this page.', 'default' ), 403 );
@@ -1047,10 +1181,17 @@
1047 1181 TablePress::redirect( array( 'action' => 'import', 'message' => 'error_import', 'error_details' => 'You do not have the required access rights.' ) );
1048 1182 }
1049 1183 }
1050 1184
1185 + // For security reasons, the "url" source is only available admins and editors via a custom capability.
1186 + if ( 'url' === $import_config['source'] ) {
1187 + if ( ! current_user_can( 'tablepress_import_tables_url' ) ) {
1188 + TablePress::redirect( array( 'action' => 'import', 'message' => 'error_import', 'error_details' => 'You do not have the required access rights.' ) );
1189 + }
1190 + }
1191 +
1051 1192 // Move file upload data to the main import configuration.
1052 - $import_config['file-upload'] = isset( $_FILES['import_file_upload'] ) ? $_FILES['import_file_upload'] : null;
1193 + $import_config['file-upload'] = $_FILES['import_file_upload'] ?? null;
1053 1194
1054 1195 // Check if the source data for the chosen import source is defined.
1055 1196 if ( empty( $import_config[ $import_config['source'] ] ) ) {
1056 1197 TablePress::redirect( array( 'action' => 'import', 'message' => 'error_import', 'error_details' => 'The HTTP POST data does not contain an import source.' ) );
@@ -1085,9 +1226,9 @@
1085 1226 $redirect_parameters['error_details'] = TablePress::get_wp_error_string( $import );
1086 1227 } elseif ( 0 < count( $import['errors'] ) ) {
1087 1228 $wp_error_strings = array();
1088 1229 foreach ( $import['errors'] as $file ) {
1089 - $wp_error_strings[] = TablePress::get_wp_error_string( $file['error'] );
1230 + $wp_error_strings[] = TablePress::get_wp_error_string( $file->error );
1090 1231 }
1091 1232 $redirect_parameters['error_details'] = implode( ', ', $wp_error_strings );
1092 1233 }
1093 1234 TablePress::redirect( $redirect_parameters );
@@ -1111,9 +1252,9 @@
1111 1252 * Hide a header message on an admin screen.
1112 1253 *
1113 1254 * @since 1.0.0
1114 1255 */
1115 - public function handle_get_action_hide_message() {
1256 + public function handle_get_action_hide_message(): void {
1116 1257 $message_item = ! empty( $_GET['item'] ) ? $_GET['item'] : '';
1117 1258 TablePress::check_nonce( 'hide_message', $message_item );
1118 1259
1119 1260 if ( ! current_user_can( 'tablepress_list_tables' ) ) {
@@ -1130,9 +1271,9 @@
1130 1271 * Delete a table.
1131 1272 *
1132 1273 * @since 1.0.0
1133 1274 */
1134 - public function handle_get_action_delete_table() {
1275 + public function handle_get_action_delete_table(): void {
1135 1276 $table_id = ( ! empty( $_GET['item'] ) ) ? $_GET['item'] : false;
1136 1277 TablePress::check_nonce( 'delete_table', $table_id );
1137 1278
1138 1279 $return = ! empty( $_GET['return'] ) ? $_GET['return'] : 'list';
@@ -1171,9 +1312,9 @@
1171 1312 * Copy a table.
1172 1313 *
1173 1314 * @since 1.0.0
1174 1315 */
1175 - public function handle_get_action_copy_table() {
1316 + public function handle_get_action_copy_table(): void {
1176 1317 $table_id = ( ! empty( $_GET['item'] ) ) ? $_GET['item'] : false;
1177 1318 TablePress::check_nonce( 'copy_table', $table_id );
1178 1319
1179 1320 $return = ! empty( $_GET['return'] ) ? $_GET['return'] : 'list';
@@ -1213,9 +1354,9 @@
1213 1354 * Preview a table.
1214 1355 *
1215 1356 * @since 1.0.0
1216 1357 */
1217 - public function handle_get_action_preview_table() {
1358 + public function handle_get_action_preview_table(): void {
1218 1359 $table_id = ( ! empty( $_GET['item'] ) ) ? $_GET['item'] : false;
1219 1360 TablePress::check_nonce( 'preview_table', $table_id );
1220 1361
1221 1362 // Nonce check should actually catch this already.
@@ -1247,13 +1388,14 @@
1247 1388 $render_options = shortcode_atts( $default_render_options, $table['options'] );
1248 1389 /** This filter is documented in controllers/controller-frontend.php */
1249 1390 $render_options = apply_filters( 'tablepress_shortcode_table_shortcode_atts', $render_options );
1250 1391 $render_options['html_id'] = "tablepress-{$table['id']}";
1392 + $render_options['block_preview'] = true;
1251 1393 $_render->set_input( $table, $render_options );
1252 1394 $view_data = array(
1253 1395 'table_id' => $table_id,
1254 1396 'head_html' => $_render->get_preview_css(),
1255 - 'body_html' => $_render->get_output(),
1397 + 'body_html' => $_render->get_output( 'html' ),
1256 1398 'site_uses_block_editor' => TablePress::site_uses_block_editor(),
1257 1399 );
1258 1400
1259 1401 $custom_css = TablePress::$model_options->get( 'custom_css' );
@@ -1271,9 +1413,9 @@
1271 1413 * Shows a list of tables in the Editor toolbar Thickbox (opened by TinyMCE or Quicktags button).
1272 1414 *
1273 1415 * @since 1.0.0
1274 1416 */
1275 - public function handle_get_action_editor_button_thickbox() {
1417 + public function handle_get_action_editor_button_thickbox(): void {
1276 1418 TablePress::check_nonce( 'editor_button_thickbox' );
1277 1419
1278 1420 if ( ! current_user_can( 'tablepress_list_tables' ) ) {
1279 1421 wp_die( __( 'Sorry, you are not allowed to access this page.', 'default' ), 403 );
@@ -1295,9 +1437,9 @@
1295 1437 * Uninstall TablePress, and delete all tables and options.
1296 1438 *
1297 1439 * @since 1.0.0
1298 1440 */
1299 - public function handle_get_action_uninstall_tablepress() {
1441 + public function handle_get_action_uninstall_tablepress(): void {
1300 1442 TablePress::check_nonce( 'uninstall_tablepress' );
1301 1443
1302 1444 $plugin = TABLEPRESS_BASENAME;
1303 1445
@@ -1317,9 +1459,9 @@
1317 1459
1318 1460 TablePress::$model_table->destroy();
1319 1461 TablePress::$model_options->destroy();
1320 1462
1321 - $output = '<strong>' . __( 'TablePress was uninstalled successfully.', 'tablepress' ) . '</strong><br /><br />';
1463 + $output = '<strong>' . __( 'TablePress was uninstalled successfully.', 'tablepress' ) . '</strong><br><br>';
1322 1464 $output .= __( 'All tables, data, and options were deleted.', 'tablepress' );
1323 1465 if ( is_multisite() ) {
1324 1466 $output .= ' ' . __( 'You may now ask the network admin to delete the plugin&#8217;s folder <code>tablepress</code> from the server, if no other site in the network uses it.', 'tablepress' );
1325 1467 } else {
@@ -1326,9 +1468,9 @@
1326 1468 $output .= ' ' . __( 'You may now manually delete the plugin&#8217;s folder <code>tablepress</code> from the <code>plugins</code> directory on your server or use the &#8220;Delete&#8221; link for TablePress on the WordPress &#8220;Plugins&#8221; page.', 'tablepress' );
1327 1469 }
1328 1470 if ( $css_files_deleted ) {
1329 1471 $output .= ' ' . __( 'Your TablePress &#8220;Custom CSS&#8221; files have been deleted automatically.', 'tablepress' );
1330 - } else {
1472 + } else { // phpcs:ignore Universal.ControlStructures.DisallowLonelyIf.Found
1331 1473 if ( is_multisite() ) {
1332 1474 $output .= ' ' . __( 'Please also ask him to delete your TablePress &#8220;Custom CSS&#8221; files from the server.', 'tablepress' );
1333 1475 } else {
1334 1476 $output .= ' ' . __( 'You may now also delete your TablePress &#8220;Custom CSS&#8221; files in the <code>wp-content</code> folder.', 'tablepress' );