PluginProbe
TablePress – Tables in WordPress made easy / 2.0.4
TablePress – Tables in WordPress made easy v2.0.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 2.0.4, at views/class-all-tables-list-table.php

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