PluginProbe
TablePress – Tables in WordPress made easy / 3.3.4
TablePress – Tables in WordPress made easy v3.3.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.3.4, at views/class-all-tables-list-table.php

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