PluginProbe
TablePress – Tables in WordPress made easy / 3.4
TablePress – Tables in WordPress made easy v3.4
3.4 3.3.4 3.3.3 3.3.2 3.3.1 trunk 1.12 1.14 1.9.2 2.0.4 2.1.7 2.1.8 2.2 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.3 2.3.1 2.3.2 2.4 2.4.1 2.4.2 2.4.3 All 45 releases
tablepress / views / class-all-tables-list-table.php

class-all-tables-list-table.php in TablePress – Tables in WordPress made easy 3.4, at views/class-all-tables-list-table.php

662 lines 24.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * List Tables View List Table
4 *
5 * @package TablePress
6 * @subpackage Views
7 * @author Tobias Bäthge
8 * @since 2.0.0
9 */
10
11 declare(strict_types=1);
12
13 // Prohibit direct script loading.
14 defined( 'ABSPATH' ) || die( 'No direct script access allowed!' );
15
16 /**
17 * TablePress All Tables List Table class
18 *
19 * @package TablePress
20 * @subpackage Views
21 * @author Tobias Bäthge
22 * @since 1.0.0
23 */
24 class TablePress_All_Tables_List_Table extends WP_List_Table {
25
26 /**
27 * Number of items of the initial data set (before sort, search, and pagination).
28 *
29 * @since 1.0.0
30 */
31 protected int $items_count = 0;
32
33 /**
34 * Cached bulk actions.
35 *
36 * This property has to be declared here, as it's `private` in the parent class
37 * and thus can't be inherited.
38 *
39 * @since 1.0.0
40 * @var array<string, string>
41 */
42 protected array $_actions; // phpcs:ignore PSR2.Classes.PropertyDeclaration.Underscore
43
44 /**
45 * Initialize the List Table.
46 *
47 * @since 1.0.0
48 */
49 public function __construct() {
50 $screen = get_current_screen();
51
52 // Hide "Last Modified By" column by default.
53 if ( false === get_user_option( "manage{$screen->id}columnshidden" ) ) { // @phpstan-ignore property.nonObject
54 update_user_option( get_current_user_id(), "manage{$screen->id}columnshidden", array( 'table_last_modified_by' ), true ); // @phpstan-ignore property.nonObject
55 }
56
57 // @phpstan-ignore argument.type (WordPress Core's docblocks state wrong argument types in some places.)
58 parent::__construct( array(
59 'singular' => 'tablepress-table', // Singular name of the listed records.
60 'plural' => 'tablepress-all-tables', // Plural name of the listed records.
61 'ajax' => false, // Does this list table support AJAX?
62 'screen' => $screen, // WP_Screen object.
63 ) );
64 }
65
66 /**
67 * Set the data items (here: tables) that are to be displayed by the List Tables, and their original count.
68 *
69 * @since 1.0.0
70 *
71 * @param string[] $items Tables to be displayed in the List Table.
72 */
73 public function set_items( array $items ): void {
74 $this->items = $items;
75 $this->items_count = count( $items );
76 }
77
78 /**
79 * Check whether the user has permissions for certain AJAX actions.
80 * (not used, but must be implemented in this child class)
81 *
82 * @since 1.0.0
83 *
84 * @return bool true (Default value).
85 */
86 #[\Override]
87 public function ajax_user_can(): bool {
88 return true;
89 }
90
91 /**
92 * Get a list of columns in this List Table.
93 *
94 * Format: 'internal-name' => 'Column Title'.
95 *
96 * @since 1.0.0
97 *
98 * @return array<string, string> List of columns in this List Table.
99 */
100 #[\Override]
101 public function get_columns(): array {
102 $columns = array(
103 'cb' => $this->has_items() ? '<input type="checkbox">' : '', // Checkbox for "Select all", but only if there are items in the table.
104 // "name" is special in WP, which is why we prefix every entry here, to be safe!
105 'table_id' => __( 'ID', 'tablepress' ),
106 'table_name' => __( 'Table Name', 'tablepress' ),
107 'table_description' => __( 'Description', 'tablepress' ),
108 'table_author' => __( 'Author', 'tablepress' ),
109 'table_last_modified_by' => __( 'Last Modified By', 'tablepress' ),
110 'table_last_modified' => __( 'Last Modified', 'tablepress' ),
111 );
112 return $columns;
113 }
114
115 /**
116 * Get a list of columns that are sortable.
117 *
118 * Format: 'internal-name' => array( $field for $item[ $field ], true for already sorted ).
119 *
120 * @since 1.0.0
121 *
122 * @return array<string, array{string, bool}> List of sortable columns in this List Table.
123 */
124 #[\Override]
125 protected function get_sortable_columns(): array {
126 // No sorting on the Empty List placeholder.
127 if ( ! $this->has_items() ) {
128 return array();
129 }
130
131 $sortable_columns = array(
132 'table_id' => array( 'id', true ), // true means its already sorted.
133 'table_name' => array( 'name', false ),
134 'table_description' => array( 'description', false ),
135 'table_author' => array( 'author', false ),
136 'table_last_modified_by' => array( 'last_modified_by', false ),
137 'table_last_modified' => array( 'last_modified', false ),
138 );
139 return $sortable_columns;
140 }
141
142 /**
143 * Gets the name of the default primary column.
144 *
145 * @since 1.7.0
146 *
147 * @return string Name of the default primary column, in this case, the table name.
148 */
149 #[\Override]
150 protected function get_default_primary_column_name(): string {
151 return 'table_name';
152 }
153
154 /**
155 * Render a cell in the "cb" column.
156 *
157 * @since 1.0.0
158 *
159 * @param array<string, mixed> $item Data item for the current row.
160 * @return string HTML content of the cell.
161 */
162 #[\Override]
163 protected function column_cb( /* array */ $item ): string {
164 // Don't use type hints in the method declaration to prevent PHP errors, as the method is inherited.
165
166 $user_can_copy_table = current_user_can( 'tablepress_copy_table', $item['id'] );
167 $user_can_delete_table = current_user_can( 'tablepress_delete_table', $item['id'] );
168 $user_can_export_table = current_user_can( 'tablepress_export_table', $item['id'] );
169
170 if ( ! ( $user_can_copy_table || $user_can_delete_table || $user_can_export_table ) ) {
171 return '';
172 }
173
174 if ( '' === trim( $item['name'] ) ) {
175 $item['name'] = __( '(no name)', 'tablepress' );
176 }
177
178 return sprintf(
179 '<input type="checkbox" id="cb-select-%1$s" name="table[]" value="%1$s"><label for="cb-select-%1$s"><span class="screen-reader-text">%2$s</span></label>',
180 esc_attr( $item['id'] ),
181 esc_html( sprintf( __( 'Select table “%s”', 'tablepress' ), $item['name'] ) ),
182 );
183 }
184
185 /**
186 * Render a cell in the "table_id" column.
187 *
188 * @since 1.0.0
189 *
190 * @param array<string, mixed> $item Data item for the current row.
191 * @return string HTML content of the cell.
192 */
193 protected function column_table_id( array $item ): string {
194 return esc_html( $item['id'] );
195 }
196
197 /**
198 * Returns a clean label for the primary (Table Name) column's row header `aria-label`.
199 *
200 * Provides screen readers with just the table name as the row header name,
201 * preventing them from computing the name from the full cell content.
202 *
203 * @since 3.3.4
204 *
205 * @param array<string, mixed> $item Data item for the current row.
206 * @return string The table name.
207 */
208 protected function get_primary_column_aria_label( /* array */ $item ): string {
209 // Don't use type hints in the method declaration to prevent PHP errors, as the method is inherited.
210
211 return trim( $item['name'] );
212 }
213
214 /**
215 * Render a cell in the "table_name" column.
216 *
217 * @since 1.0.0
218 *
219 * @param array<string, mixed> $item Data item for the current row.
220 * @return string HTML content of the cell.
221 */
222 protected function column_table_name( array $item ): string {
223 $user_can_edit_table = current_user_can( 'tablepress_edit_table', $item['id'] );
224 $user_can_copy_table = current_user_can( 'tablepress_copy_table', $item['id'] );
225 $user_can_export_table = current_user_can( 'tablepress_export_table', $item['id'] );
226 $user_can_delete_table = current_user_can( 'tablepress_delete_table', $item['id'] );
227 $user_can_preview_table = current_user_can( 'tablepress_preview_table', $item['id'] );
228
229 $edit_url = TablePress::url( array( 'action' => 'edit', 'table_id' => $item['id'] ) );
230 $copy_url = TablePress::url( array( 'action' => 'copy_table', 'item' => $item['id'], 'return' => 'list', 'return_item' => $item['id'] ), true, 'admin-post.php' );
231 $export_url = TablePress::url( array( 'action' => 'export', 'table_id' => $item['id'] ) );
232 $delete_url = TablePress::url( array( 'action' => 'delete_table', 'item' => $item['id'], 'return' => 'list', 'return_item' => $item['id'] ), true, 'admin-post.php' );
233 $preview_url = TablePress::url( array( 'action' => 'preview_table', 'item' => $item['id'], 'return' => 'list', 'return_item' => $item['id'] ), true, 'admin-post.php' );
234
235 if ( '' === trim( $item['name'] ) ) {
236 $item['name'] = __( '(no name)', 'tablepress' );
237 }
238
239 if ( $user_can_edit_table ) {
240 $row_text = '<strong><a title="' . esc_attr( sprintf( __( 'Edit &#8220;%s&#8221;', 'tablepress' ), esc_attr( $item['name'] ) ) ) . '" class="row-title" href="' . $edit_url . '">' . esc_html( $item['name'] ) . '</a></strong>';
241 } else {
242 $row_text = '<strong>' . esc_html( $item['name'] ) . '</strong>';
243 }
244
245 $row_actions = array();
246 if ( $user_can_edit_table ) {
247 $row_actions['edit'] = sprintf( '<a href="%1$s" title="%2$s">%3$s</a>', $edit_url, esc_attr( sprintf( __( 'Edit &#8220;%s&#8221;', 'tablepress' ), $item['name'] ) ), __( 'Edit', 'tablepress' ) );
248 }
249 $row_actions['shortcode hide-if-no-js'] = sprintf( '<a href="%1$s" title="%2$s">%3$s</a>', '#', esc_attr( '[' . TablePress::$shortcode . " id={$item['id']} /]" ), __( 'Show Shortcode', 'tablepress' ) );
250 if ( $user_can_copy_table ) {
251 $row_actions['copy'] = sprintf( '<a href="%1$s" title="%2$s">%3$s</a>', $copy_url, esc_attr( sprintf( __( 'Copy &#8220;%s&#8221;', 'tablepress' ), $item['name'] ) ), __( 'Copy', 'tablepress' ) );
252 }
253 if ( $user_can_export_table ) {
254 $row_actions['export'] = sprintf( '<a href="%1$s" title="%2$s">%3$s</a>', $export_url, esc_attr( sprintf( __( 'Export &#8220;%s&#8221;', 'tablepress' ), $item['name'] ) ), _x( 'Export', 'row action', 'tablepress' ) );
255 }
256 if ( $user_can_delete_table ) {
257 $row_actions['delete'] = sprintf( '<a href="%1$s" title="%2$s">%3$s</a>', $delete_url, esc_attr( sprintf( __( 'Delete “%1$s” (ID %2$s)', 'tablepress' ), $item['name'], $item['id'] ) ), __( 'Delete', 'tablepress' ) );
258 }
259 if ( $user_can_preview_table ) {
260 $row_actions['table-preview'] = sprintf( '<a href="%1$s" title="%2$s">%3$s</a>', $preview_url, esc_attr( sprintf( __( 'Preview of table “%1$s” (ID %2$s)', 'tablepress' ), $item['name'], $item['id'] ) ), __( 'Preview', 'tablepress' ) );
261 }
262
263 return $row_text . $this->row_actions( $row_actions );
264 }
265
266 /**
267 * Render a cell in the "table_description" column.
268 *
269 * @since 1.0.0
270 *
271 * @param array<string, mixed> $item Data item for the current row.
272 * @return string HTML content of the cell.
273 */
274 protected function column_table_description( array $item ): string {
275 if ( '' === trim( $item['description'] ) ) {
276 $item['description'] = __( '(no description)', 'tablepress' );
277 }
278 return esc_html( $item['description'] );
279 }
280
281 /**
282 * Render a cell in the "table_author" column.
283 *
284 * @since 1.0.0
285 *
286 * @param array<string, mixed> $item Data item for the current row.
287 * @return string HTML content of the cell.
288 */
289 protected function column_table_author( array $item ): string {
290 return TablePress::get_user_display_name( $item['author'] );
291 }
292
293 /**
294 * Render a cell in the "last_modified_by" column.
295 *
296 * @since 1.0.0
297 *
298 * @param array<string, mixed> $item Data item for the current row.
299 * @return string HTML content of the cell.
300 */
301 protected function column_table_last_modified_by( array $item ): string {
302 return TablePress::get_user_display_name( $item['options']['last_editor'] );
303 }
304
305 /**
306 * Render a cell in the "table_last_modified" column.
307 *
308 * @since 1.0.0
309 *
310 * @param array<string, mixed> $item Data item for the current row.
311 * @return string HTML content of the cell.
312 */
313 protected function column_table_last_modified( array $item ): string {
314 $modified_timestamp = date_create( $item['last_modified'], wp_timezone() );
315 if ( false === $modified_timestamp ) {
316 $modified_timestamp = $item['last_modified'];
317 } else {
318 $modified_timestamp = $modified_timestamp->getTimestamp();
319 }
320 $current_timestamp = time();
321 $time_diff = $current_timestamp - $modified_timestamp;
322 // Time difference is only shown up to one week.
323 if ( $time_diff >= 0 && $time_diff < WEEK_IN_SECONDS ) {
324 $time_diff = sprintf( __( '%s ago', 'default' ), human_time_diff( $modified_timestamp, $current_timestamp ) );
325 } else {
326 $time_diff = TablePress::format_datetime( $item['last_modified'], '<br>' );
327 }
328 $readable_time = TablePress::format_datetime( $item['last_modified'] );
329 return '<abbr title="' . esc_attr( $readable_time ) . '">' . $time_diff . '</abbr>';
330 }
331
332 /**
333 * Handles output for the default column.
334 *
335 * @since 1.8.0
336 *
337 * @param array<string, mixed> $item Data item for the current row.
338 * @param string $column_name Current column name.
339 */
340 #[\Override]
341 protected function column_default( /* array */ $item, /* string */ $column_name ): void {
342 // Don't use type hints in the method declaration to prevent PHP errors, as the method is inherited.
343
344 /**
345 * Fires inside each custom column of the TablePress list table.
346 *
347 * @since 1.8.0
348 *
349 * @param string $column_name Current column name.
350 * @param array<string, mixed> $item Data item for the current row.
351 */
352 do_action( 'manage_tablepress_list_custom_column', $column_name, $item );
353 }
354
355 /**
356 * Generates and display row actions links for the list table.
357 *
358 * The "Table Name" column already gets these, so return an empty string here to prevent them from being duplicated.
359 *
360 * @since 2.0.0
361 *
362 * @param object|array<string, mixed> $item The item being acted upon.
363 * @param string $column_name Current column name.
364 * @param string $primary Primary column name.
365 * @return string The row actions HTML, or an empty string if the current column is not the primary column.
366 */
367 #[\Override]
368 protected function handle_row_actions( /* object|array */ $item, /* string */ $column_name, /* string */ $primary ): string {
369 // Don't use type hints in the method declaration to prevent PHP errors, as the method is inherited.
370
371 return '';
372 }
373
374 /**
375 * Get a list (name => title) bulk actions that are available.
376 *
377 * @since 1.0.0
378 *
379 * @return array<string, string> Bulk actions for this table.
380 */
381 #[\Override]
382 protected function get_bulk_actions(): array {
383 $bulk_actions = array();
384
385 if ( current_user_can( 'tablepress_copy_tables' ) ) {
386 $bulk_actions['copy'] = _x( 'Copy', 'bulk action', 'tablepress' );
387 }
388 if ( current_user_can( 'tablepress_export_tables' ) ) {
389 $bulk_actions['export'] = _x( 'Export', 'bulk action', 'tablepress' );
390 }
391 if ( current_user_can( 'tablepress_delete_tables' ) ) {
392 $bulk_actions['delete'] = _x( 'Delete', 'bulk action', 'tablepress' );
393 }
394
395 return $bulk_actions;
396 }
397
398 /**
399 * Render the bulk actions dropdown.
400 *
401 * In comparison with parent class, this has modified HTML (especially no field named "action" as that's being used already)!
402 *
403 * @since 1.0.0
404 *
405 * @param 'top'|'bottom' $which The location of the bulk actions: 'top' or 'bottom'.
406 * This is designated as optional for backwards-compatibility.
407 */
408 #[\Override]
409 protected function bulk_actions( /* string */ $which = 'top' ): void {
410 // Don't use type hints in the method declaration to prevent PHP errors, as the method is inherited.
411
412 if ( ! isset( $this->_actions ) ) {
413 $this->_actions = $this->get_bulk_actions();
414 $no_new_actions = $this->_actions;
415 /** This filter is documented in the WordPress function WP_List_Table::bulk_actions() in wp-admin/includes/class-wp-list-table.php */
416 $this->_actions = apply_filters( 'bulk_actions-' . $this->screen->id, $this->_actions ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
417 $this->_actions = array_intersect_assoc( $this->_actions, $no_new_actions );
418 $two = '';
419 } else {
420 $two = '2';
421 }
422
423 if ( empty( $this->_actions ) ) {
424 return;
425 }
426
427 $name_id = "bulk-action-selector-{$which}";
428 echo "<label for='{$name_id}' class='screen-reader-text'>" .
429 /* translators: Hidden accessibility text. */
430 __( 'Select bulk action', 'default' ) .
431 '</label>';
432 echo "<select name='{$name_id}' id='{$name_id}'>\n";
433 echo "<option value='-1' selected='selected'>" . __( 'Bulk actions', 'default' ) . "</option>\n";
434 foreach ( $this->_actions as $name => $title ) {
435 echo "\t<option value='{$name}'>{$title}</option>\n";
436 }
437 echo "</select>\n";
438 submit_button( __( 'Apply', 'default' ), 'action', 'bulk_action', false, array( 'id' => "doaction{$two}" ) );
439 echo "\n";
440 }
441
442 /**
443 * Holds the message to be displayed when there are no items in the table.
444 *
445 * @since 1.0.0
446 */
447 #[\Override]
448 public function no_items(): void {
449 _e( 'No tables found.', 'tablepress' );
450 if ( 0 === $this->items_count ) {
451 $user_can_add_tables = current_user_can( 'tablepress_add_tables' );
452 $user_can_import_tables = current_user_can( 'tablepress_import_tables' );
453
454 $add_url = TablePress::url( array( 'action' => 'add' ) );
455 $import_url = TablePress::url( array( 'action' => 'import' ) );
456
457 if ( $user_can_add_tables && $user_can_import_tables ) {
458 echo ' ' . sprintf( __( 'You should <a href="%1$s">add</a> or <a href="%2$s">import</a> a table to get started!', 'tablepress' ), $add_url, $import_url );
459 } elseif ( $user_can_add_tables ) {
460 echo ' ' . sprintf( __( 'You should <a href="%s">add</a> a table to get started!', 'tablepress' ), $add_url );
461 } elseif ( $user_can_import_tables ) {
462 echo ' ' . sprintf( __( 'You should <a href="%s">import</a> a table to get started!', 'tablepress' ), $import_url );
463 }
464 }
465 }
466
467 /**
468 * Generate the elements above or below the table (like bulk actions and pagination).
469 *
470 * In comparison with parent class, this has no nonce field in its HTML code and a one-time filter around the pagination call, to change a string.
471 *
472 * @since 1.0.0
473 *
474 * @param 'top'|'bottom' $which Location ("top" or "bottom").
475 */
476 #[\Override]
477 protected function display_tablenav( /* string */ $which ): void {
478 // Don't use type hints in the method declaration to prevent PHP errors, as the method is inherited.
479
480 ?>
481 <div class="tablenav <?php echo esc_attr( $which ); ?>">
482
483 <?php if ( $this->has_items() ) : ?>
484 <div class="alignleft actions bulkactions">
485 <?php $this->bulk_actions( $which ); ?>
486 </div>
487 <?php
488 endif;
489 $this->extra_tablenav( $which );
490
491 add_filter( 'ngettext_default', array( $this, 'change_pagination_items_string' ), 10, 5 );
492 $this->pagination( $which );
493 remove_filter( 'ngettext_default', array( $this, 'change_pagination_items_string' ), 10 );
494 ?>
495
496 <br class="clear">
497 </div>
498 <?php
499 }
500
501 /**
502 * Replaces the "%s item/%s items" string in the pagination with "%s table/%s tables".
503 *
504 * @since 2.0.0
505 *
506 * @param string $translation The current translation of a singular or plural form.
507 * @param string $single The text to be used if the number is singular.
508 * @param string $plural The text to be used if the number is plural.
509 * @param int $number The number to compare against to use either the singular or plural form.
510 * @param string $domain Text domain. Defaults to 'default'.
511 * @return string The changed translation.
512 */
513 public function change_pagination_items_string( string $translation, string $single, string $plural, int $number, string $domain ): string {
514 if ( '%s item' === $single && '%s items' === $plural ) {
515 /* translators: %s: Number of tables */
516 $translation = _n( '%s table', '%s tables', $number, 'tablepress' );
517 }
518 return $translation;
519 }
520
521 /**
522 * Callback to determine whether the given $item contains the search term.
523 *
524 * @since 1.0.0
525 *
526 * @param string $item Table ID that shall be searched.
527 * @return bool Whether the search term was found or not.
528 */
529 protected function _search_callback( string $item ): bool {
530 static $term;
531 static $json_encoded_term;
532 if ( is_null( $term ) || is_null( $json_encoded_term ) ) {
533 $term = wp_unslash( $_GET['s'] );
534 $json_encoded_term = substr( wp_json_encode( $term, TABLEPRESS_JSON_OPTIONS ), 1, -1 ); // @phpstan-ignore argument.type
535 }
536
537 static $debug;
538 if ( is_null( $debug ) ) {
539 // Set debug variable to allow searching in corrupted tables.
540 $debug = isset( $_GET['debug'] ) ? ( 'true' === $_GET['debug'] ) : WP_DEBUG;
541 }
542
543 // Load table again, with data and options (for last_editor).
544 $item = TablePress::$model_table->load( $item, true, true );
545
546 if ( is_wp_error( $item ) ) {
547 return false;
548 }
549
550 // Don't search corrupted tables, except when debug mode is enabled via $_GET parameter or WP_DEBUG constant.
551 if ( ! $debug && isset( $item['is_corrupted'] ) && $item['is_corrupted'] ) {
552 return false;
553 }
554
555 $fn_stripos = function_exists( 'mb_stripos' ) ? 'mb_stripos' : 'stripos';
556
557 // Search from easy to hard, so that "expensive" code maybe doesn't have to run.
558 if ( false !== $fn_stripos( $item['id'], (string) $term )
559 || false !== $fn_stripos( $item['name'], (string) $term )
560 || false !== $fn_stripos( $item['description'], (string) $term )
561 || false !== $fn_stripos( TablePress::get_user_display_name( $item['author'] ), (string) $term )
562 || false !== $fn_stripos( TablePress::get_user_display_name( $item['options']['last_editor'] ), (string) $term )
563 || false !== $fn_stripos( TablePress::format_datetime( $item['last_modified'] ), (string) $term )
564 || false !== $fn_stripos( wp_json_encode( $item['data'], TABLEPRESS_JSON_OPTIONS ), (string) $json_encoded_term ) ) { // @phpstan-ignore argument.type
565 return true;
566 }
567
568 return false;
569 }
570
571 /**
572 * Callback to for the array sort function.
573 *
574 * @since 1.0.0
575 *
576 * @param array<string, mixed> $item_a First item that shall be compared to.
577 * @param array<string, mixed> $item_b The second item for the comparison.
578 * @return int (-1, 0, 1) depending on which item sorts "higher".
579 */
580 protected function _order_callback( array $item_a, array $item_b ): int {
581 global $orderby, $order;
582
583 if ( 'last_modified_by' !== $orderby ) {
584 if ( $item_a[ $orderby ] === $item_b[ $orderby ] ) {
585 return 0;
586 }
587 } else { // phpcs:ignore Universal.ControlStructures.DisallowLonelyIf.Found
588 if ( $item_a['options']['last_editor'] === $item_b['options']['last_editor'] ) {
589 return 0;
590 }
591 }
592
593 // Certain fields require some extra work before being sortable.
594 switch ( $orderby ) {
595 case 'last_modified':
596 // Compare UNIX timestamps for "last modified", which actually is a mySQL datetime string.
597 $result = ( strtotime( $item_a['last_modified'] ) > strtotime( $item_b['last_modified'] ) ) ? 1 : -1;
598 break;
599 case 'author':
600 // Get the actual author name, plain value is just the user ID.
601 $result = strnatcasecmp( TablePress::get_user_display_name( $item_a['author'] ), TablePress::get_user_display_name( $item_b['author'] ) );
602 break;
603 case 'last_modified_by':
604 // Get the actual last editor name, plain value is just the user ID.
605 $result = strnatcasecmp( TablePress::get_user_display_name( $item_a['options']['last_editor'] ), TablePress::get_user_display_name( $item_b['options']['last_editor'] ) );
606 break;
607 default:
608 // Other fields (ID, name, description) are sorted as strings.
609 $result = strnatcasecmp( $item_a[ $orderby ], $item_b[ $orderby ] );
610 }
611
612 return ( 'asc' === $order ) ? $result : - $result;
613 }
614
615 /**
616 * Prepares the list of items for displaying, by maybe searching and sorting, and by doing pagination.
617 *
618 * @since 1.0.0
619 */
620 #[\Override]
621 public function prepare_items(): void {
622 global $orderby, $order, $s;
623 wp_reset_vars( array( 'orderby', 'order', 's' ) );
624
625 // Maybe search in the items.
626 if ( $s ) {
627 $this->items = array_filter( $this->items, array( $this, '_search_callback' ) );
628 }
629
630 // Load actual tables after search for less memory consumption.
631 foreach ( $this->items as &$item ) {
632 // Don't load data, but load table options for access to last_editor.
633 $item = TablePress::$model_table->load( $item, false, true );
634 }
635 unset( $item ); // Unset use-by-reference parameter of foreach loop.
636
637 // Maybe sort the items.
638 $_sortable_columns = $this->get_sortable_columns();
639 if ( $orderby && ! empty( $this->items ) && isset( $_sortable_columns[ "table_{$orderby}" ] ) ) {
640 usort( $this->items, array( $this, '_order_callback' ) );
641 }
642
643 // Number of records to show per page.
644 $per_page = $this->get_items_per_page( 'tablepress_list_per_page', 20 ); // Hard-coded, as in filter in Admin_Controller.
645 // Page number the user is currently viewing.
646 $current_page = $this->get_pagenum();
647 // Number of records in the array.
648 $total_items = count( $this->items );
649
650 // Slice items array to hold only items for the current page.
651 $this->items = array_slice( $this->items, ( ( $current_page - 1 ) * $per_page ), $per_page );
652
653 // Register pagination options and calculation results.
654 $this->set_pagination_args( array(
655 'total_items' => $total_items, // Total number of records/items.
656 'per_page' => $per_page, // Number of items per page.
657 'total_pages' => (int) ceil( $total_items / $per_page ), // Total number of pages.
658 ) );
659 }
660
661 } // class TablePress_All_Tables_List_Table
662