PluginProbe
TablePress – Tables in WordPress made easy / 3.0.4
TablePress – Tables in WordPress made easy v3.0.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 2.4.4 All 44 releases
← All changes | controllers/controller-admin.php +276 -133 2.1.73.0.4 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->maybe_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
@@ -335,9 +376,9 @@
335 376
336 377 $wp_admin_bar->add_menu( array(
337 378 'parent' => 'new-content',
338 379 'id' => 'new-tablepress-table',
339 - 'title' => __( 'TablePress Table', 'tablepress' ),
380 + 'title' => __( 'TablePress table', 'tablepress' ),
340 381 'href' => TablePress::url( array( 'action' => 'add' ) ),
341 382 ) );
342 383 }
343 384
@@ -345,12 +386,25 @@
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-inverted-filter/tablepress-datatables-inverted-filter.php',
400 + 'tablepress-datatables-row-details/tablepress-datatables-row-details.php',
401 + 'tablepress-datatables-rowgroup/tablepress-datatables-rowgroup.php',
402 + 'tablepress-responsive-tables/tablepress-responsive-tables.php',
403 + );
404 + foreach ( $incompatible_superseded_extensions as $plugin_file ) {
405 + add_action( "after_plugin_row_{$plugin_file}", array( $this, 'add_superseded_extension_meta_row' ), 10, 3 );
406 + }
353 407 }
354 408
355 409 /**
356 410 * Add links to the TablePress entry in the "Plugin" column on the Plugins page.
@@ -356,14 +410,14 @@
356 410 * Add links to the TablePress entry in the "Plugin" column on the Plugins page.
357 411 *
358 412 * @since 1.0.0
359 413 *
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.
414 + * @param string[] $links List of links to print in the "Plugin" column on the Plugins page.
415 + * @return string[] Extended list of links to print in the "Plugin" column on the Plugins page.
362 416 */
363 - public function add_plugin_action_links( array $links ) {
417 + public function add_plugin_action_links( array $links ): array {
364 418 if ( current_user_can( 'tablepress_list_tables' ) ) {
365 - $links[] = '<a href="' . TablePress::url() . '">' . __( 'Plugin page', 'tablepress' ) . '</a>';
419 + $links[] = '<a href="' . esc_url( TablePress::url() ) . '">' . __( 'Plugin page', 'tablepress' ) . '</a>';
366 420 }
367 421 return $links;
368 422 }
369 423
@@ -371,19 +425,19 @@
371 425 * Add links to the TablePress entry in the "Description" column on the Plugins page.
372 426 *
373 427 * @since 1.0.0
374 428 *
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.
429 + * @param string[] $links List of links to print in the "Description" column on the Plugins page.
430 + * @param string $file Name of the plugin.
431 + * @return string[] Extended list of links to print in the "Description" column on the Plugins page.
378 432 */
379 - public function add_plugin_row_meta( array $links, $file ) {
433 + public function add_plugin_row_meta( array $links, string $file ): array {
380 434 if ( TABLEPRESS_BASENAME === $file ) {
381 435 $links[] = '<a href="https://tablepress.org/faq/" title="' . esc_attr__( 'Frequently Asked Questions', 'tablepress' ) . '">' . __( 'FAQ', 'tablepress' ) . '</a>';
382 436 $links[] = '<a href="https://tablepress.org/documentation/">' . __( 'Documentation', 'tablepress' ) . '</a>';
383 437 $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>';
438 + if ( ! TABLEPRESS_IS_PLAYGROUND_PREVIEW && tb_tp_fs()->is_free_plan() ) {
439 + $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 440 }
387 441 }
388 442 return $links;
389 443 }
@@ -388,16 +442,62 @@
388 442 return $links;
389 443 }
390 444
391 445 /**
446 + * Prints a superseded extension notice below certain TablePress Extension plugins' meta rows on the "Plugins" screen.
447 + *
448 + * @since 2.4.1
449 + *
450 + * @param string $plugin_file Path to the plugin file relative to the plugins directory.
451 + * @param array<int, string|string[]|bool> $plugin_data An array of plugin data.
452 + * @param string $status Status filter currently applied to the plugin list.
453 + */
454 + public function add_superseded_extension_meta_row( string $plugin_file, array $plugin_data, string $status ): void {
455 + if ( ! is_plugin_active( $plugin_file ) ) {
456 + return;
457 + }
458 + ?>
459 + <tr class="plugin-update-tr active">
460 + <td colspan="<?php echo esc_attr( $GLOBALS['wp_list_table']->get_column_count() ); ?>" class="plugin-update colspanchange">
461 + <div class="update-message notice inline notice-error notice-alt">
462 + <?php
463 + if ( tb_tp_fs()->is_free_plan() ) {
464 + echo '<p style="font-size:14px;">';
465 + _e( 'This TablePress Extension was retired.', 'tablepress' );
466 + echo ' ';
467 + _e( '<strong>The plugin does no longer work with TablePress 3</strong> and will no longer receive updates or support!', 'tablepress' );
468 + echo '<br>';
469 + _e( 'Keeping it activated can lead to errors on your website!', 'tablepress' );
470 + 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>';
471 + echo '</p>';
472 + }
473 + ?>
474 + <style>
475 + /* Remove the separator line between the plugin's and the notice's table row. */
476 + .plugins .active[data-plugin="<?php echo $plugin_file; ?>"] th,
477 + .plugins .active[data-plugin="<?php echo $plugin_file; ?>"] td {
478 + box-shadow: none;
479 + }
480 + /* Hide the plugin update row for the Extension as those won't work anymore anyways. */
481 + .plugins .plugin-update-tr[data-plugin="<?php echo $plugin_file; ?>"] {
482 + display: none;
483 + }
484 + </style>
485 + </div>
486 + </td>
487 + </tr>
488 + <?php
489 + }
490 +
491 + /**
392 492 * Prepare the rendering of an admin screen, by determining the current action, loading necessary data and initializing the view.
393 493 *
394 494 * @since 1.0.0
395 495 */
396 - public function load_admin_page() {
496 + public function load_admin_page(): void {
397 497 // Determine the action from either the GET parameter (for sub-menu entries, and the main admin menu entry).
398 498 $action = ( ! empty( $_GET['action'] ) ) ? $_GET['action'] : 'list'; // Default action is list.
399 - if ( $this->is_top_level_page ) {
499 + if ( TablePress::$controller->is_top_level_page ) {
400 500 // Or, for sub-menu entry of an admin menu "TablePress" entry, get it from the "page" GET parameter.
401 501 if ( 'tablepress' !== $_GET['page'] ) {
402 502 // Actions that are top-level entries, but don't have an action GET parameter (action is after last _ in string).
403 503 $action = substr( $_GET['page'], 11 ); // $_GET['page'] has the format 'tablepress_{$action}'
@@ -404,9 +504,9 @@
404 504 }
405 505 }
406 506
407 507 // 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'] ) ) {
508 + 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 509 wp_die( __( 'Sorry, you are not allowed to access this page.', 'default' ), 403 );
410 510 }
411 511
412 512 // Don't load TablePress assets on the Freemius opt-in/activation screen.
@@ -421,9 +521,9 @@
421 521 * Set the `$typenow` global to the current CPT ourselves, as `WP_Screen::get()` does not determine the CPT correctly.
422 522 * This is necessary as the WP Admin Menu can otherwise highlight wrong entries, see https://github.com/TablePress/TablePress/issues/24.
423 523 */
424 524 if ( isset( $_GET['post_type'] ) && post_type_exists( $_GET['post_type'] ) ) {
425 - $GLOBALS['typenow'] = $_GET['post_type'];
525 + $GLOBALS['typenow'] = $_GET['post_type']; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
426 526 }
427 527
428 528 // Pre-define some view data.
429 529 $data = array(
@@ -438,17 +538,16 @@
438 538 case 'list':
439 539 $data['table_id'] = ( ! empty( $_GET['table_id'] ) ) ? $_GET['table_id'] : false;
440 540 // Prime the post meta cache for cached loading of last_editor.
441 541 $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();
542 + $data['messages']['donation_nag'] = $this->maybe_show_donation_message();
543 + $data['messages']['first_visit'] = ! $data['messages']['donation_nag'] && TablePress::$model_options->get( 'message_first_visit' );
544 + $data['messages']['plugin_update'] = TablePress::$model_options->get( 'message_plugin_update' );
545 + $data['messages']['superseded_extensions'] = current_user_can( 'manage_options' ) && TablePress::$model_options->get( 'message_superseded_extensions' );
445 546 $data['table_count'] = count( $data['table_ids'] );
446 547 break;
447 548 case 'about':
448 549 $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 550 break;
452 551 case 'options':
453 552 /*
454 553 * Maybe try saving "Custom CSS" to a file:
@@ -455,9 +554,9 @@
455 554 * (called here, as the credentials form posts to this handler again, due to how `request_filesystem_credentials()` works)
456 555 */
457 556 if ( isset( $_GET['item'] ) && 'save_custom_css' === $_GET['item'] ) {
458 557 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
558 + $action = 'options_custom_css'; // To load a different view.
460 559 // Try saving "Custom CSS" to a file, otherwise this gets the HTML for the credentials form.
461 560 $tablepress_css = TablePress::load_class( 'TablePress_CSS', 'class-css.php', 'classes' );
462 561 $result = $tablepress_css->save_custom_css_to_file_plugin_options( TablePress::$model_options->get( 'custom_css' ), TablePress::$model_options->get( 'custom_css_minified' ) );
463 562 if ( is_string( $result ) ) {
@@ -478,9 +577,9 @@
478 577 break;
479 578 }
480 579 $data['frontend_options']['use_custom_css'] = TablePress::$model_options->get( 'use_custom_css' );
481 580 $data['frontend_options']['custom_css'] = TablePress::$model_options->get( 'custom_css' );
482 - $data['user_options']['parent_page'] = $this->parent_page;
581 + $data['user_options']['parent_page'] = TablePress::$controller->parent_page;
483 582 break;
484 583 case 'edit':
485 584 if ( empty( $_GET['table_id'] ) ) {
486 585 TablePress::redirect( array( 'action' => 'list', 'message' => 'error_no_table' ) );
@@ -495,29 +594,54 @@
495 594 }
496 595 break;
497 596 case 'export':
498 597 // 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 );
598 + $table_ids = TablePress::$model_table->load_all( false );
599 + $data['tables'] = array();
600 + foreach ( $table_ids as $table_id ) {
601 + if ( ! current_user_can( 'tablepress_export_table', $table_id ) ) {
602 + continue;
603 + }
604 + // Load table, without table data, options, and visibility settings.
605 + $table = TablePress::$model_table->load( $table_id, false, false );
606 +
607 + // Skip tables that could not be loaded.
608 + if ( is_wp_error( $table ) ) {
609 + continue;
610 + }
611 +
612 + $data['tables'][ $table['id'] ] = $table['name'];
613 + }
500 614 $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 - }
615 + $data['export_ids'] = ( ! empty( $_GET['table_id'] ) ) ? explode( ',', $_GET['table_id'] ) : array();
507 616 $exporter = TablePress::load_class( 'TablePress_Export', 'class-export.php', 'classes' );
508 617 $data['zip_support_available'] = $exporter->zip_support_available;
509 618 $data['export_formats'] = $exporter->export_formats;
510 619 $data['csv_delimiters'] = $exporter->csv_delimiters;
511 - $data['export_format'] = ( ! empty( $_GET['export_format'] ) ) ? $_GET['export_format'] : false;
620 + $data['export_format'] = ( ! empty( $_GET['export_format'] ) ) ? $_GET['export_format'] : 'csv';
512 621 $data['csv_delimiter'] = ( ! empty( $_GET['csv_delimiter'] ) ) ? $_GET['csv_delimiter'] : _x( ',', 'Default CSV delimiter in the translated language (";", ",", or "tab")', 'tablepress' );
513 622 break;
514 623 case 'import':
515 624 // 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 );
625 + $table_ids = TablePress::$model_table->load_all( false );
626 + $data['tables'] = array();
627 + foreach ( $table_ids as $table_id ) {
628 + if ( ! current_user_can( 'tablepress_edit_table', $table_id ) ) {
629 + continue;
630 + }
631 + // Load table, without table data, options, and visibility settings.
632 + $table = TablePress::$model_table->load( $table_id, false, false );
633 +
634 + // Skip tables that could not be loaded.
635 + if ( is_wp_error( $table ) ) {
636 + continue;
637 + }
638 +
639 + $data['tables'][ $table['id'] ] = $table['name'];
640 + }
641 + $data['table_ids'] = $table_ids; // Backward compatibility for the retired "Table Auto Update" Extension, which still relies on this variable name.
517 642 $data['tables_count'] = TablePress::$model_table->count_tables();
518 643 $importer = TablePress::load_class( 'TablePress_Import', 'class-import.php', 'classes' );
519 - $data['zip_support_available'] = $importer->zip_support_available;
520 644 $data['import_type'] = ( ! empty( $_GET['import_type'] ) ) ? $_GET['import_type'] : 'add';
521 645 $data['import_existing_table'] = ( ! empty( $_GET['import_existing_table'] ) ) ? $_GET['import_existing_table'] : '';
522 646 $data['import_source'] = ( ! empty( $_GET['import_source'] ) ) ? $_GET['import_source'] : 'file-upload';
523 647 $data['import_url'] = ( ! empty( $_GET['import_url'] ) ) ? wp_unslash( $_GET['import_url'] ) : 'https://';
@@ -531,10 +655,10 @@
531 655 * Filters the data that is passed to the current TablePress View.
532 656 *
533 657 * @since 1.0.0
534 658 *
535 - * @param array $data Data for the view.
536 - * @param string $action The current action for the view.
659 + * @param array<string, mixed> $data Data for the view.
660 + * @param string $action The current action for the view.
537 661 */
538 662 $data = apply_filters( 'tablepress_view_data', $data, $action );
539 663
540 664 // Prepare and initialize the view.
@@ -545,9 +669,9 @@
545 669 * Render the view that has been initialized in load_admin_page() (called by WordPress when the actual page content is needed).
546 670 *
547 671 * @since 1.0.0
548 672 */
549 - public function show_admin_page() {
673 + public function show_admin_page(): void {
550 674 $this->view->render();
551 675 }
552 676
553 677 /**
@@ -556,9 +680,9 @@
556 680 * @since 1.0.0
557 681 *
558 682 * @return bool Whether the message shall be shown on the "All Tables" screen.
559 683 */
560 - protected function maybe_show_donation_message() {
684 + protected function maybe_show_donation_message(): bool {
561 685 // Only show the message to plugin admins.
562 686 if ( ! current_user_can( 'tablepress_edit_options' ) ) {
563 687 return false;
564 688 }
@@ -576,9 +700,9 @@
576 700 * Init list of actions that have a view with their titles/names/caps.
577 701 *
578 702 * @since 1.0.0
579 703 */
580 - protected function init_view_actions() {
704 + protected function init_view_actions(): void {
581 705 $this->view_actions = array(
582 706 'list' => array(
583 707 'show_entry' => true,
584 708 'page_title' => __( 'All Tables', 'tablepress' ),
@@ -634,9 +758,9 @@
634 758 * Filters the available TablePres Views/Actions and their parameters.
635 759 *
636 760 * @since 1.0.0
637 761 *
638 - * @param array $view_actions The available Views/Actions and their parameters.
762 + * @param array<string, array<string, bool|string>> $view_actions The available Views/Actions and their parameters.
639 763 */
640 764 $this->view_actions = apply_filters( 'tablepress_admin_view_actions', $this->view_actions );
641 765 }
642 766
@@ -648,9 +772,9 @@
648 772 * Handle Bulk Actions (Copy, Export, Delete) on "All Tables" list screen.
649 773 *
650 774 * @since 1.0.0
651 775 */
652 - public function handle_post_action_list() {
776 + public function handle_post_action_list(): void {
653 777 TablePress::check_nonce( 'list' );
654 778
655 779 if ( isset( $_POST['bulk-action-selector-top'] ) && '-1' !== $_POST['bulk-action-selector-top'] ) {
656 780 $bulk_action = $_POST['bulk-action-selector-top'];
@@ -691,9 +815,9 @@
691 815 * To export, redirect to "Export" screen, with selected table IDs.
692 816 */
693 817 $table_ids = implode( ',', $tables );
694 818 TablePress::redirect( array( 'action' => 'export', 'table_id' => $table_ids ) );
695 - break;
819 + // break; // unreachable.
696 820 case 'delete':
697 821 foreach ( $tables as $table_id ) {
698 822 if ( current_user_can( 'tablepress_delete_table', $table_id ) ) {
699 823 $deleted = TablePress::$model_table->delete( $table_id );
@@ -706,9 +830,9 @@
706 830 }
707 831 break;
708 832 }
709 833
710 - if ( 0 !== count( $no_success ) ) { // @TODO: maybe pass this information to the view?
834 + if ( 0 !== count( $no_success ) ) { // @todo maybe pass this information to the view?
711 835 $message = "error_{$bulk_action}_not_all_tables";
712 836 } else {
713 837 $plural = ( count( $tables ) > 1 ) ? '_plural' : '';
714 838 $message = "success_{$bulk_action}{$plural}";
@@ -733,9 +857,9 @@
733 857 * Add a table, according to the parameters on the "Add new Table" screen.
734 858 *
735 859 * @since 1.0.0
736 860 */
737 - public function handle_post_action_add() {
861 + public function handle_post_action_add(): void {
738 862 TablePress::check_nonce( 'add' );
739 863
740 864 if ( ! current_user_can( 'tablepress_add_tables' ) ) {
741 865 wp_die( __( 'Sorry, you are not allowed to access this page.', 'default' ), 403 );
@@ -746,11 +870,11 @@
746 870 }
747 871
748 872 $add_table = wp_unslash( $_POST['table'] );
749 873
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'] : '';
874 + // Perform confidence checks of posted data.
875 + $name = $add_table['name'] ?? '';
876 + $description = $add_table['description'] ?? '';
753 877 if ( ! isset( $add_table['rows'], $add_table['columns'] ) ) {
754 878 TablePress::redirect( array( 'action' => 'add', 'message' => 'error_add', 'error_details' => 'The HTTP POST data does not contain the table size.' ) );
755 879 }
756 880
@@ -789,9 +913,9 @@
789 913 * Save changed "Plugin Options".
790 914 *
791 915 * @since 1.0.0
792 916 */
793 - public function handle_post_action_options() {
917 + public function handle_post_action_options(): void {
794 918 TablePress::check_nonce( 'options' );
795 919
796 920 if ( ! current_user_can( 'tablepress_access_options_screen' ) ) {
797 921 wp_die( __( 'Sorry, you are not allowed to access this page.', 'default' ), 403 );
@@ -810,10 +934,10 @@
810 934 if ( ! empty( $posted_options['admin_menu_parent_page'] ) && '-' !== $posted_options['admin_menu_parent_page'] ) {
811 935 $new_options['admin_menu_parent_page'] = $posted_options['admin_menu_parent_page'];
812 936 // Re-init parent information, as `TablePress::redirect()` URL might be wrong otherwise.
813 937 /** 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 );
938 + TablePress::$controller->parent_page = apply_filters( 'tablepress_admin_menu_parent_page', $posted_options['admin_menu_parent_page'] );
939 + TablePress::$controller->is_top_level_page = in_array( TablePress::$controller->parent_page, array( 'top', 'middle', 'bottom' ), true );
816 940 }
817 941
818 942 // Custom CSS can only be saved if the user is allowed to do so.
819 943 $update_custom_css_files = false;
@@ -824,13 +948,20 @@
824 948 if ( isset( $posted_options['custom_css'] ) ) {
825 949 $new_options['custom_css'] = $posted_options['custom_css'];
826 950
827 951 $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 952
953 + if ( '' !== $new_options['custom_css'] ) {
954 + // Update "Custom CSS" to use DataTables 2 variants instead of old DataTables 1.x CSS classes.
955 + $new_options['custom_css'] = TablePress::convert_datatables_api_data( $new_options['custom_css'] );
956 + // Sanitize and tidy up Custom CSS.
957 + $new_options['custom_css'] = $tablepress_css->sanitize_css( $new_options['custom_css'] );
958 + // Minify Custom CSS.
959 + $new_options['custom_css_minified'] = $tablepress_css->minify_css( $new_options['custom_css'] );
960 + } else {
961 + $new_options['custom_css_minified'] = '';
962 + }
963 +
833 964 // Maybe update CSS files as well.
834 965 $custom_css_file_contents = $tablepress_css->load_custom_css_from_file( 'normal' );
835 966 if ( false === $custom_css_file_contents ) {
836 967 $custom_css_file_contents = '';
@@ -861,9 +992,9 @@
861 992 * Export selected tables.
862 993 *
863 994 * @since 1.0.0
864 995 */
865 - public function handle_post_action_export() {
996 + public function handle_post_action_export(): void {
866 997 TablePress::check_nonce( 'export' );
867 998
868 999 if ( ! current_user_can( 'tablepress_export_tables' ) ) {
869 1000 wp_die( __( 'Sorry, you are not allowed to access this page.', 'default' ), 403 );
@@ -874,12 +1005,13 @@
874 1005 }
875 1006
876 1007 $export = wp_unslash( $_POST['export'] );
877 1008
878 - if ( empty( $export['tables'] ) ) {
1009 + if ( empty( $export['tables_list'] ) ) {
879 1010 TablePress::redirect( array( 'action' => 'export', 'message' => 'error_export', 'error_details' => 'The HTTP POST data does not contain tables.' ) );
880 1011 }
881 1012
1013 + /** @var TablePress_Export $exporter */ // phpcs:ignore Generic.Commenting.DocComment.MissingShort
882 1014 $exporter = TablePress::load_class( 'TablePress_Export', 'class-export.php', 'classes' );
883 1015
884 1016 if ( empty( $export['format'] ) || ! isset( $exporter->export_formats[ $export['format'] ] ) ) {
885 1017 TablePress::redirect( array( 'action' => 'export', 'message' => 'error_export', 'error_details' => 'The export format is invalid.' ) );
@@ -891,10 +1023,9 @@
891 1023 if ( 'csv' === $export['format'] && ! isset( $exporter->csv_delimiters[ $export['csv_delimiter'] ] ) ) {
892 1024 TablePress::redirect( array( 'action' => 'export', 'message' => 'error_export', 'error_details' => 'The CSV delimiter is invalid.' ) );
893 1025 }
894 1026
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'];
1027 + $tables = explode( ',', $export['tables_list'] );
897 1028
898 1029 // Determine if ZIP file support is available.
899 1030 if ( $exporter->zip_support_available
900 1031 && ( ( isset( $export['zip_file'] ) && 'true' === $export['zip_file'] ) || count( $tables ) > 1 ) ) {
@@ -904,9 +1035,9 @@
904 1035 $export_to_zip = false;
905 1036 }
906 1037
907 1038 if ( ! $export_to_zip ) {
908 - // This is only possible for one table, so take the first one.
1039 + // Exporting without a ZIP file is only possible for one table, so take the first one.
909 1040 if ( ! current_user_can( 'tablepress_export_table', $tables[0] ) ) {
910 1041 wp_die( __( 'Sorry, you are not allowed to access this page.', 'default' ), 403 );
911 1042 }
912 1043 // Load table, with table data, options, and visibility settings.
@@ -937,12 +1068,12 @@
937 1068 * Filters the exported table data.
938 1069 *
939 1070 * @since 1.6.0
940 1071 *
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.
1072 + * @param string $export_data The exported table data.
1073 + * @param array<string, mixed> $table Table to be exported.
1074 + * @param string $export_format Format for the export ('csv', 'html', 'json').
1075 + * @param string $csv_delimiter Delimiter for CSV export.
945 1076 */
946 1077 $export_data = apply_filters( 'tablepress_export_data', $export_data, $table, $export['format'], $export['csv_delimiter'] );
947 1078 $download_data = $export_data;
948 1079 } else {
@@ -957,10 +1088,10 @@
957 1088 /** This filter is documented in controllers/controller-admin.php */
958 1089 $download_filename = apply_filters( 'tablepress_export_filename', $download_filename, '', '', $export['format'], $export_to_zip );
959 1090 $download_filename = sanitize_file_name( $download_filename );
960 1091 $full_filename = wp_tempnam( $download_filename );
961 - if ( true !== $zip_file->open( $full_filename, ZIPARCHIVE::OVERWRITE ) ) {
962 - @unlink( $full_filename );
1092 + if ( true !== $zip_file->open( $full_filename, ZipArchive::OVERWRITE ) ) {
1093 + @unlink( $full_filename ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
963 1094 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 1095 }
965 1096
966 1097 foreach ( $tables as $table_id ) {
@@ -989,11 +1120,11 @@
989 1120 }
990 1121
991 1122 // If something went wrong, or no files were added to the ZIP file, bail out.
992 1123 // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
993 - if ( ! ZIPARCHIVE::ER_OK === $zip_file->status || 0 === $zip_file->numFiles ) {
1124 + if ( ZipArchive::ER_OK !== $zip_file->status || 0 === $zip_file->numFiles ) {
994 1125 $zip_file->close();
995 - @unlink( $full_filename );
1126 + @unlink( $full_filename ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
996 1127 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 1128 }
998 1129 $zip_file->close();
999 1130
@@ -998,9 +1129,13 @@
998 1129 $zip_file->close();
999 1130
1000 1131 // Load contents of the ZIP file, to send it as a download.
1001 1132 $download_data = file_get_contents( $full_filename );
1002 - @unlink( $full_filename );
1133 + if ( false === $download_data ) {
1134 + @unlink( $full_filename ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
1135 + 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.' ) );
1136 + }
1137 + @unlink( $full_filename ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
1003 1138 }
1004 1139
1005 1140 // Send download headers for export file.
1006 1141 header( 'Content-Description: File Transfer' );
@@ -1023,9 +1158,9 @@
1023 1158 * Import data from existing source (Upload, URL, Server, Direct input).
1024 1159 *
1025 1160 * @since 1.0.0
1026 1161 */
1027 - public function handle_post_action_import() {
1162 + public function handle_post_action_import(): void {
1028 1163 TablePress::check_nonce( 'import' );
1029 1164
1030 1165 if ( ! current_user_can( 'tablepress_import_tables' ) ) {
1031 1166 wp_die( __( 'Sorry, you are not allowed to access this page.', 'default' ), 403 );
@@ -1047,10 +1182,17 @@
1047 1182 TablePress::redirect( array( 'action' => 'import', 'message' => 'error_import', 'error_details' => 'You do not have the required access rights.' ) );
1048 1183 }
1049 1184 }
1050 1185
1186 + // For security reasons, the "url" source is only available admins and editors via a custom capability.
1187 + if ( 'url' === $import_config['source'] ) {
1188 + if ( ! current_user_can( 'tablepress_import_tables_url' ) ) {
1189 + TablePress::redirect( array( 'action' => 'import', 'message' => 'error_import', 'error_details' => 'You do not have the required access rights.' ) );
1190 + }
1191 + }
1192 +
1051 1193 // Move file upload data to the main import configuration.
1052 - $import_config['file-upload'] = isset( $_FILES['import_file_upload'] ) ? $_FILES['import_file_upload'] : null;
1194 + $import_config['file-upload'] = $_FILES['import_file_upload'] ?? null;
1053 1195
1054 1196 // Check if the source data for the chosen import source is defined.
1055 1197 if ( empty( $import_config[ $import_config['source'] ] ) ) {
1056 1198 TablePress::redirect( array( 'action' => 'import', 'message' => 'error_import', 'error_details' => 'The HTTP POST data does not contain an import source.' ) );
@@ -1085,9 +1227,9 @@
1085 1227 $redirect_parameters['error_details'] = TablePress::get_wp_error_string( $import );
1086 1228 } elseif ( 0 < count( $import['errors'] ) ) {
1087 1229 $wp_error_strings = array();
1088 1230 foreach ( $import['errors'] as $file ) {
1089 - $wp_error_strings[] = TablePress::get_wp_error_string( $file['error'] );
1231 + $wp_error_strings[] = TablePress::get_wp_error_string( $file->error );
1090 1232 }
1091 1233 $redirect_parameters['error_details'] = implode( ', ', $wp_error_strings );
1092 1234 }
1093 1235 TablePress::redirect( $redirect_parameters );
@@ -1111,9 +1253,9 @@
1111 1253 * Hide a header message on an admin screen.
1112 1254 *
1113 1255 * @since 1.0.0
1114 1256 */
1115 - public function handle_get_action_hide_message() {
1257 + public function handle_get_action_hide_message(): void {
1116 1258 $message_item = ! empty( $_GET['item'] ) ? $_GET['item'] : '';
1117 1259 TablePress::check_nonce( 'hide_message', $message_item );
1118 1260
1119 1261 if ( ! current_user_can( 'tablepress_list_tables' ) ) {
@@ -1130,9 +1272,9 @@
1130 1272 * Delete a table.
1131 1273 *
1132 1274 * @since 1.0.0
1133 1275 */
1134 - public function handle_get_action_delete_table() {
1276 + public function handle_get_action_delete_table(): void {
1135 1277 $table_id = ( ! empty( $_GET['item'] ) ) ? $_GET['item'] : false;
1136 1278 TablePress::check_nonce( 'delete_table', $table_id );
1137 1279
1138 1280 $return = ! empty( $_GET['return'] ) ? $_GET['return'] : 'list';
@@ -1171,9 +1313,9 @@
1171 1313 * Copy a table.
1172 1314 *
1173 1315 * @since 1.0.0
1174 1316 */
1175 - public function handle_get_action_copy_table() {
1317 + public function handle_get_action_copy_table(): void {
1176 1318 $table_id = ( ! empty( $_GET['item'] ) ) ? $_GET['item'] : false;
1177 1319 TablePress::check_nonce( 'copy_table', $table_id );
1178 1320
1179 1321 $return = ! empty( $_GET['return'] ) ? $_GET['return'] : 'list';
@@ -1213,9 +1355,9 @@
1213 1355 * Preview a table.
1214 1356 *
1215 1357 * @since 1.0.0
1216 1358 */
1217 - public function handle_get_action_preview_table() {
1359 + public function handle_get_action_preview_table(): void {
1218 1360 $table_id = ( ! empty( $_GET['item'] ) ) ? $_GET['item'] : false;
1219 1361 TablePress::check_nonce( 'preview_table', $table_id );
1220 1362
1221 1363 // Nonce check should actually catch this already.
@@ -1247,13 +1389,14 @@
1247 1389 $render_options = shortcode_atts( $default_render_options, $table['options'] );
1248 1390 /** This filter is documented in controllers/controller-frontend.php */
1249 1391 $render_options = apply_filters( 'tablepress_shortcode_table_shortcode_atts', $render_options );
1250 1392 $render_options['html_id'] = "tablepress-{$table['id']}";
1393 + $render_options['block_preview'] = true;
1251 1394 $_render->set_input( $table, $render_options );
1252 1395 $view_data = array(
1253 1396 'table_id' => $table_id,
1254 1397 'head_html' => $_render->get_preview_css(),
1255 - 'body_html' => $_render->get_output(),
1398 + 'body_html' => $_render->get_output( 'html' ),
1256 1399 'site_uses_block_editor' => TablePress::site_uses_block_editor(),
1257 1400 );
1258 1401
1259 1402 $custom_css = TablePress::$model_options->get( 'custom_css' );
@@ -1271,9 +1414,9 @@
1271 1414 * Shows a list of tables in the Editor toolbar Thickbox (opened by TinyMCE or Quicktags button).
1272 1415 *
1273 1416 * @since 1.0.0
1274 1417 */
1275 - public function handle_get_action_editor_button_thickbox() {
1418 + public function handle_get_action_editor_button_thickbox(): void {
1276 1419 TablePress::check_nonce( 'editor_button_thickbox' );
1277 1420
1278 1421 if ( ! current_user_can( 'tablepress_list_tables' ) ) {
1279 1422 wp_die( __( 'Sorry, you are not allowed to access this page.', 'default' ), 403 );
@@ -1295,9 +1438,9 @@
1295 1438 * Uninstall TablePress, and delete all tables and options.
1296 1439 *
1297 1440 * @since 1.0.0
1298 1441 */
1299 - public function handle_get_action_uninstall_tablepress() {
1442 + public function handle_get_action_uninstall_tablepress(): void {
1300 1443 TablePress::check_nonce( 'uninstall_tablepress' );
1301 1444
1302 1445 $plugin = TABLEPRESS_BASENAME;
1303 1446
@@ -1317,9 +1460,9 @@
1317 1460
1318 1461 TablePress::$model_table->destroy();
1319 1462 TablePress::$model_options->destroy();
1320 1463
1321 - $output = '<strong>' . __( 'TablePress was uninstalled successfully.', 'tablepress' ) . '</strong><br /><br />';
1464 + $output = '<strong>' . __( 'TablePress was uninstalled successfully.', 'tablepress' ) . '</strong><br><br>';
1322 1465 $output .= __( 'All tables, data, and options were deleted.', 'tablepress' );
1323 1466 if ( is_multisite() ) {
1324 1467 $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 1468 } else {
@@ -1326,9 +1469,9 @@
1326 1469 $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 1470 }
1328 1471 if ( $css_files_deleted ) {
1329 1472 $output .= ' ' . __( 'Your TablePress &#8220;Custom CSS&#8221; files have been deleted automatically.', 'tablepress' );
1330 - } else {
1473 + } else { // phpcs:ignore Universal.ControlStructures.DisallowLonelyIf.Found
1331 1474 if ( is_multisite() ) {
1332 1475 $output .= ' ' . __( 'Please also ask him to delete your TablePress &#8220;Custom CSS&#8221; files from the server.', 'tablepress' );
1333 1476 } else {
1334 1477 $output .= ' ' . __( 'You may now also delete your TablePress &#8220;Custom CSS&#8221; files in the <code>wp-content</code> folder.', 'tablepress' );