PluginProbe
TablePress – Tables in WordPress made easy / 1.9.2
TablePress – Tables in WordPress made easy v1.9.2
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 / view-list.php

view-list.php in TablePress – Tables in WordPress made easy 1.9.2, at views/view-list.php

786 lines 32.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
4 *
5 * @package TablePress
6 * @subpackage Views
7 * @author Tobias Bäthge
8 * @since 1.0.0
9 */
10
11 // Prohibit direct script loading.
12 defined( 'ABSPATH' ) || die( 'No direct script access allowed!' );
13
14 /**
15 * List Tables View class
16 * @package TablePress
17 * @subpackage Views
18 * @author Tobias Bäthge
19 * @since 1.0.0
20 */
21 class TablePress_List_View extends TablePress_View {
22
23 /**
24 * Object for the All Tables List Table.
25 *
26 * @since 1.0.0
27 * @var TablePress_All_Tables_List_Table
28 */
29 protected $wp_list_table;
30
31 /**
32 * Set up the view with data and do things that are specific for this view.
33 *
34 * @since 1.0.0
35 *
36 * @param string $action Action for this view.
37 * @param array $data Data for this view.
38 */
39 public function setup( $action, array $data ) {
40 parent::setup( $action, $data );
41
42 add_thickbox();
43 $this->admin_page->enqueue_script( 'list', array( 'jquery-core' ), array(
44 'list' => array(
45 'shortcode_popup' => __( 'To embed this table into a post or page, use this Shortcode:', 'tablepress' ),
46 'donation-message-already-donated' => __( 'Thank you very much! Your donation is highly appreciated. You just contributed to the further development of TablePress!', 'tablepress' ),
47 'donation-message-maybe-later' => sprintf( __( 'No problem! I still hope you enjoy the benefits that TablePress adds to your site. If you should change your mind, you&#8217;ll always find the &#8220;Donate&#8221; button on the <a href="%s">TablePress website</a>.', 'tablepress' ), 'https://tablepress.org/' ),
48 ),
49 ) );
50
51 if ( $data['messages']['first_visit'] ) {
52 $this->add_header_message(
53 '<strong><em>' . __( 'Welcome!', 'tablepress' ) . '</em></strong><br />'
54 . __( 'Thank you for using TablePress for the first time!', 'tablepress' ) . ' '
55 . sprintf( __( 'If you encounter any questions or problems, please visit the <a href="%1$s">FAQ</a>, the <a href="%2$s">documentation</a>, and the <a href="%3$s">Support</a> section on the <a href="%4$s">plugin website</a>.', 'tablepress' ), 'https://tablepress.org/faq/', 'https://tablepress.org/documentation/', 'https://tablepress.org/support/', 'https://tablepress.org/' ) . '<br /><br />'
56 . $this->ajax_link( array( 'action' => 'hide_message', 'item' => 'first_visit', 'return' => 'list' ), __( 'Hide this message', 'tablepress' ) ),
57 'notice-info not-dismissible'
58 );
59 }
60
61 if ( $data['messages']['wp_table_reloaded_warning'] ) {
62 $this->add_header_message(
63 '<strong><em>' . __( 'Attention!', 'tablepress' ) . '</em></strong><br />'
64 . __( 'You have activated the plugin WP-Table Reloaded, which can not be used together with TablePress.', 'tablepress' ) . '<br />'
65 . __( 'It is strongly recommended that you switch from WP-Table Reloaded to TablePress, which not only fixes many problems, but also has more and better features than WP-Table Reloaded.', 'tablepress' ) . '<br />'
66 . sprintf( __( 'Please follow the <a href="%s">migration guide</a> to move your tables and then deactivate WP-Table Reloaded!', 'tablepress' ), 'https://tablepress.org/migration-from-wp-table-reloaded/' ) . '<br />'
67 . '<a href="' . TablePress::url( array( 'action' => 'import' ) ) . '" class="button button-primary button-large" style="color:#ffffff;margin-top:5px;">' . __( 'Import your tables from WP-Table Reloaded', 'tablepress' ) . '</a>',
68 'notice-error not-dismissible'
69 );
70 }
71
72 if ( $data['messages']['donation_message'] ) {
73 $this->add_header_message(
74 '<img alt="' . esc_attr__( 'Tobias Bäthge, developer of TablePress', 'tablepress' ) . '" src="https://secure.gravatar.com/avatar/50f1cff2e27a1f522b18ce229c057bc5?s=110" height="110" width="110" style="float:left;margin:1px 10px 40px 0;" />'
75 . __( 'Hi, my name is Tobias, I&#8217;m the developer of the TablePress plugin.', 'tablepress' ) . '<br /><br />'
76 . __( 'Thanks for using it! You&#8217;ve installed TablePress over a month ago.', 'tablepress' ) . ' '
77 . sprintf( _n( 'If everything works and you are satisfied with the results of managing your %s table, isn&#8217;t that worth a coffee or two?', 'If everything works and you are satisfied with the results of managing your %s tables, isn&#8217;t that worth a coffee or two?', $data['table_count'], 'tablepress' ), esc_html( $data['table_count'] ) ) . '<br />'
78 . sprintf( __( '<a href="%s">Donations</a> help me to continue user support and development of this <em>free</em> software &mdash; things for which I spend countless hours of my free time! Thank you very much!', 'tablepress' ), 'https://tablepress.org/donate/' ) . '<br /><br />'
79 . __( 'Sincerely, Tobias', 'tablepress' ) . '<br /><br />'
80 . sprintf( '<a href="%s" target="_blank"><strong>%s</strong></a>', 'https://tablepress.org/donate/', __( 'Sure, I&#8217;ll buy you a coffee and support TablePress!', 'tablepress' ) ) . '&nbsp;&nbsp;&nbsp;&nbsp;&middot;&nbsp;&nbsp;&nbsp;&nbsp;'
81 . $this->ajax_link( array( 'action' => 'hide_message', 'item' => 'donation_nag', 'return' => 'list', 'target' => 'already-donated' ), __( 'I already donated.', 'tablepress' ) ) . '&nbsp;&nbsp;&nbsp;&nbsp;&middot;&nbsp;&nbsp;&nbsp;&nbsp;'
82 . $this->ajax_link( array( 'action' => 'hide_message', 'item' => 'donation_nag', 'return' => 'list', 'target' => 'maybe-later' ), __( 'No, thanks. Don&#8217;t ask again.', 'tablepress' ) ),
83 'notice-success not-dismissible'
84 );
85 }
86
87 if ( $data['messages']['plugin_update_message'] ) {
88 $this->add_header_message(
89 '<strong><em>' . sprintf( __( 'Thank you for updating to TablePress %s!', 'tablepress' ), TablePress::version ) . '</em></strong><br />'
90 . sprintf( __( 'Please read the <a href="%s">release announcement</a> for more information.', 'tablepress' ), 'https://tablepress.org/news/' ) . ' '
91 . sprintf( __( 'If you like the new features and enhancements, <a href="%s">giving a donation</a> towards the further support and development of TablePress is recommended. Thank you!', 'tablepress' ), 'https://tablepress.org/donate/' )
92 . '<br /><br />'
93 . $this->ajax_link( array( 'action' => 'hide_message', 'item' => 'plugin_update', 'return' => 'list' ), __( 'Hide this message', 'tablepress' ) ),
94 'notice-info not-dismissible'
95 );
96 }
97
98 $this->process_action_messages( array(
99 'success_delete' => _n( 'The table was deleted successfully.', 'The tables were deleted successfully.', 1, 'tablepress' ),
100 'success_delete_plural' => _n( 'The table was deleted successfully.', 'The tables were deleted successfully.', 2, 'tablepress' ),
101 'error_delete' => __( 'Error: The table could not be deleted.', 'tablepress' ),
102 'error_save' => __( 'Error: The table could not be saved.', 'tablepress' ),
103 'success_copy' => _n( 'The table was copied successfully.', 'The tables were copied successfully.', 1, 'tablepress' ) . ( ( false !== $data['table_id'] ) ? ' ' . sprintf( __( 'The copied table has the table ID &#8220;%s&#8221;.', 'tablepress' ), esc_html( $data['table_id'] ) ) : '' ),
104 'success_copy_plural' => _n( 'The table was copied successfully.', 'The tables were copied successfully.', 2, 'tablepress' ),
105 'error_copy' => __( 'Error: The table could not be copied.', 'tablepress' ),
106 'error_no_table' => __( 'Error: You did not specify a valid table ID.', 'tablepress' ),
107 'error_load_table' => __( 'Error: This table could not be loaded!', 'tablepress' ),
108 'error_bulk_action_invalid' => __( 'Error: This bulk action is invalid!', 'tablepress' ),
109 'error_no_selection' => __( 'Error: You did not select any tables!', 'tablepress' ),
110 'error_delete_not_all_tables' => __( 'Notice: Not all selected tables could be deleted!', 'tablepress' ),
111 'error_copy_not_all_tables' => __( 'Notice: Not all selected tables could be copied!', 'tablepress' ),
112 'success_import' => __( 'The tables were imported successfully.', 'tablepress' ),
113 'success_import_wp_table_reloaded' => __( 'The tables were imported successfully from WP-Table Reloaded.', 'tablepress' ),
114 ) );
115
116 $this->add_text_box( 'head', array( $this, 'textbox_head' ), 'normal' );
117 $this->add_text_box( 'tables-list', array( $this, 'textbox_tables_list' ), 'normal' );
118
119 add_screen_option( 'per_page', array( 'label' => __( 'Tables', 'tablepress' ), 'default' => 20 ) ); // Admin_Controller contains function to allow changes to this in the Screen Options to be saved
120 $this->wp_list_table = new TablePress_All_Tables_List_Table();
121 $this->wp_list_table->set_items( $this->data['table_ids'] );
122 $this->wp_list_table->prepare_items();
123
124 // Cleanup Request URI string, which WP_List_Table uses to generate the sort URLs.
125 $_SERVER['REQUEST_URI'] = remove_query_arg( array( 'message', 'table_id' ), $_SERVER['REQUEST_URI'] );
126 }
127
128 /**
129 * Render the current view (in this view: without form tag).
130 *
131 * @since 1.0.0
132 */
133 public function render() {
134 ?>
135 <div id="tablepress-page" class="wrap">
136 <?php
137 $this->print_nav_tab_menu();
138 // Print all header messages.
139 foreach ( $this->header_messages as $message ) {
140 echo $message;
141 }
142
143 // For this screen, this is done in textbox_tables_list(), to get the fields into the correct <form>:
144 // $this->do_text_boxes( 'header' );
145 ?>
146 <div id="poststuff">
147 <div id="post-body" class="metabox-holder columns-<?php echo ( isset( $GLOBALS['screen_layout_columns'] ) && ( 2 === $GLOBALS['screen_layout_columns'] ) ) ? '2' : '1'; ?>">
148 <div id="postbox-container-2" class="postbox-container">
149 <?php
150 $this->do_text_boxes( 'normal' );
151 $this->do_meta_boxes( 'normal' );
152
153 $this->do_text_boxes( 'additional' );
154 $this->do_meta_boxes( 'additional' );
155
156 // Print all submit buttons.
157 $this->do_text_boxes( 'submit' );
158 ?>
159 </div>
160 <div id="postbox-container-1" class="postbox-container">
161 <?php
162 // Print all boxes in the sidebar.
163 $this->do_text_boxes( 'side' );
164 $this->do_meta_boxes( 'side' );
165 ?>
166 </div>
167 </div>
168 <br class="clear" />
169 </div>
170 </div>
171 <?php
172 }
173
174 /**
175 * Print the screen head text.
176 *
177 * @since 1.0.0
178 *
179 * @param array $data Data for this screen.
180 * @param array $box Information about the text box.
181 */
182 public function textbox_head( array $data, array $box ) {
183 ?>
184 <p>
185 <?php _e( 'This is a list of your tables.', 'tablepress' ); ?>
186 <?php _e( 'Click the corresponding links within the list to edit, copy, delete, or preview a table.', 'tablepress' ); ?>
187 </p>
188 <p>
189 <label class="screen-reader-only" for="table-shortcode-inline"><?php esc_html_e( 'Shortcode text for editor', 'tablepress' ); ?></label>
190
191 <?php printf( __( 'To insert a table into a page, post, or text widget, copy its Shortcode %s and paste it at the desired place in the editor.', 'tablepress' ), '<input type="text" class="table-shortcode table-shortcode-inline" id="table-shortcode-inline" value="' . esc_attr( '[' . TablePress::$shortcode . ' id=<ID> /]' ) . '" readonly="readonly" />' ); ?>
192 <?php _e( 'Each table has a unique ID that needs to be adjusted in that Shortcode.', 'tablepress' ); ?>
193 <?php printf( __( 'You can also click the &#8220;%s&#8221; button in the editor toolbar to select and insert a table.', 'tablepress' ), __( 'Table', 'tablepress' ) ); ?>
194 </p>
195 <?php
196 }
197
198 /**
199 * Print the content of the "All Tables" text box.
200 *
201 * @since 1.0.0
202 *
203 * @param array $data Data for this screen.
204 * @param array $box Information about the text box.
205 */
206 public function textbox_tables_list( array $data, array $box ) {
207 if ( ! empty( $_GET['s'] ) ) {
208 printf( '<span class="subtitle">' . __( 'Search results for &#8220;%s&#8221;', 'tablepress' ) . '</span>', esc_html( wp_unslash( $_GET['s'] ) ) );
209 }
210 ?>
211 <form method="get" action="">
212 <?php
213 if ( isset( $_GET['page'] ) ) {
214 echo '<input type="hidden" name="page" value="' . esc_attr( $_GET['page'] ) . '" />' . "\n";
215 }
216 $this->wp_list_table->search_box( __( 'Search Tables', 'tablepress' ), 'tables_search' );
217 ?>
218 </form>
219 <form action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" method="post">
220 <?php
221 // This prints the nonce and action fields for this screen (done here instead of render(), due to moved <form>).
222 $this->do_text_boxes( 'header' );
223 $this->wp_list_table->display();
224 ?>
225 </form>
226 <?php
227 }
228
229 /**
230 * Create HTML code for an AJAXified link.
231 *
232 * @since 1.0.0
233 *
234 * @param array $params Parameters for the URL.
235 * @param string $text Text for the link.
236 * @return string HTML code for the link.
237 */
238 protected function ajax_link( array $params = array( 'action' => 'list', 'item' => '' ), $text ) {
239 $url = TablePress::url( $params, true, 'admin-post.php' );
240 $action = esc_attr( $params['action'] );
241 $item = esc_attr( $params['item'] );
242 $target = isset( $params['target'] ) ? esc_attr( $params['target'] ) : '';
243 return "<a class=\"ajax-link\" href=\"{$url}\" data-action=\"{$action}\" data-item=\"{$item}\" data-target=\"{$target}\">{$text}</a>";
244 }
245
246 } // class TablePress_List_View
247
248 /**
249 * TablePress All Tables List Table Class
250 * @package TablePress
251 * @subpackage Views
252 * @author Tobias Bäthge
253 * @link https://codex.wordpress.org/Class_Reference/WP_List_Table
254 * @since 1.0.0
255 */
256 class TablePress_All_Tables_List_Table extends WP_List_Table {
257
258 /**
259 * Number of items of the initial data set (before sort, search, and pagination).
260 *
261 * @since 1.0.0
262 * @var int
263 */
264 protected $items_count = 0;
265
266 /**
267 * Initialize the List Table.
268 *
269 * @since 1.0.0
270 */
271 public function __construct() {
272 $screen = get_current_screen();
273
274 // Hide "Last Modified By" column by default.
275 if ( false === get_user_option( 'manage' . $screen->id . 'columnshidden' ) ) {
276 update_user_option( get_current_user_id(), 'manage' . $screen->id . 'columnshidden', array( 'table_last_modified_by' ), true );
277 }
278
279 parent::__construct( array(
280 'singular' => 'tablepress-table', // Singular name of the listed records.
281 'plural' => 'tablepress-all-tables', // Plural name of the listed records.
282 'ajax' => false, // Does this list table support AJAX?
283 'screen' => $screen, // WP_Screen object.
284 ) );
285 }
286
287 /**
288 * Set the data items (here: tables) that are to be displayed by the List Tables, and their original count.
289 *
290 * @since 1.0.0
291 *
292 * @param array $items Tables to be displayed in the List Table.
293 */
294 public function set_items( array $items ) {
295 $this->items = $items;
296 $this->items_count = count( $items );
297 }
298
299 /**
300 * Check whether the user has permissions for certain AJAX actions.
301 * (not used, but must be implemented in this child class)
302 *
303 * @since 1.0.0
304 *
305 * @return bool true (Default value).
306 */
307 public function ajax_user_can() {
308 return true;
309 }
310
311 /**
312 * Get a list of columns in this List Table.
313 *
314 * Format: 'internal-name' => 'Column Title'.
315 *
316 * @since 1.0.0
317 *
318 * @return array List of columns in this List Table.
319 */
320 public function get_columns() {
321 $columns = array(
322 'cb' => $this->has_items() ? '<input type="checkbox" />' : '', // Checkbox for "Select all", but only if there are items in the table.
323 // "name" is special in WP, which is why we prefix every entry here, to be safe!
324 'table_id' => __( 'ID', 'tablepress' ),
325 'table_name' => __( 'Table Name', 'tablepress' ),
326 'table_description' => __( 'Description', 'tablepress' ),
327 'table_author' => __( 'Author', 'tablepress' ),
328 'table_last_modified_by' => __( 'Last Modified By', 'tablepress' ),
329 'table_last_modified' => __( 'Last Modified', 'tablepress' ),
330 );
331 return $columns;
332 }
333
334 /**
335 * Get a list of columns that are sortable.
336 *
337 * Format: 'internal-name' => array( $field for $item[ $field ], true for already sorted ).
338 *
339 * @since 1.0.0
340 *
341 * @return array List of sortable columns in this List Table.
342 */
343 protected function get_sortable_columns() {
344 // No sorting on the Empty List placeholder.
345 if ( ! $this->has_items() ) {
346 return array();
347 }
348
349 $sortable_columns = array(
350 'table_id' => array( 'id', true ), // true means its already sorted
351 'table_name' => array( 'name', false ),
352 'table_description' => array( 'description', false ),
353 'table_author' => array( 'author', false ),
354 'table_last_modified_by' => array( 'last_modified_by', false ),
355 'table_last_modified' => array( 'last_modified', false ),
356 );
357 return $sortable_columns;
358 }
359
360 /**
361 * Gets the name of the default primary column.
362 *
363 * @since 1.7.0
364 *
365 * @return string Name of the default primary column, in this case, the table name.
366 */
367 protected function get_default_primary_column_name() {
368 return 'table_name';
369 }
370
371 /**
372 * Render a cell in the "cb" column.
373 *
374 * @since 1.0.0
375 *
376 * @param array $item Data item for the current row.
377 * @return string HTML content of the cell.
378 */
379 protected function column_cb( /* array */ $item ) { // No `array` type hint to prevent a Strict Standards notice, as the method is inherited.
380 $user_can_copy_table = current_user_can( 'tablepress_copy_table', $item['id'] );
381 $user_can_delete_table = current_user_can( 'tablepress_delete_table', $item['id'] );
382 $user_can_export_table = current_user_can( 'tablepress_export_table', $item['id'] );
383
384 if ( $user_can_copy_table || $user_can_delete_table || $user_can_export_table ) {
385 return sprintf(
386 '<label><span class="screen-reader-text">%1$s</span><input type="checkbox" name="table[]" value="%2$s" /></label>',
387 esc_html__( 'Bulk action selector', 'tablepress' ),
388 esc_attr( $item['id'] )
389 );
390 } else {
391 return '';
392 }
393 }
394
395 /**
396 * Render a cell in the "table_id" column.
397 *
398 * @since 1.0.0
399 *
400 * @param array $item Data item for the current row.
401 * @return string HTML content of the cell.
402 */
403 protected function column_table_id( array $item ) {
404 return esc_html( $item['id'] );
405 }
406
407 /**
408 * Render a cell in the "table_name" column.
409 *
410 * @since 1.0.0
411 *
412 * @param array $item Data item for the current row.
413 * @return string HTML content of the cell.
414 */
415 protected function column_table_name( array $item ) {
416 $user_can_edit_table = current_user_can( 'tablepress_edit_table', $item['id'] );
417 $user_can_copy_table = current_user_can( 'tablepress_copy_table', $item['id'] );
418 $user_can_export_table = current_user_can( 'tablepress_export_table', $item['id'] );
419 $user_can_delete_table = current_user_can( 'tablepress_delete_table', $item['id'] );
420 $user_can_preview_table = current_user_can( 'tablepress_preview_table', $item['id'] );
421
422 $edit_url = TablePress::url( array( 'action' => 'edit', 'table_id' => $item['id'] ) );
423 $copy_url = TablePress::url( array( 'action' => 'copy_table', 'item' => $item['id'], 'return' => 'list', 'return_item' => $item['id'] ), true, 'admin-post.php' );
424 $export_url = TablePress::url( array( 'action' => 'export', 'table_id' => $item['id'] ) );
425 $delete_url = TablePress::url( array( 'action' => 'delete_table', 'item' => $item['id'], 'return' => 'list', 'return_item' => $item['id'] ), true, 'admin-post.php' );
426 $preview_url = TablePress::url( array( 'action' => 'preview_table', 'item' => $item['id'], 'return' => 'list', 'return_item' => $item['id'] ), true, 'admin-post.php' );
427
428 if ( '' === trim( $item['name'] ) ) {
429 $item['name'] = __( '(no name)', 'tablepress' );
430 }
431
432 if ( $user_can_edit_table ) {
433 $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>';
434 } else {
435 $row_text = '<strong>' . esc_html( $item['name'] ) . '</strong>';
436 }
437
438 $row_actions = array();
439 if ( $user_can_edit_table ) {
440 $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' ) );
441 }
442 $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' ) );
443 if ( $user_can_copy_table ) {
444 $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' ) );
445 }
446 if ( $user_can_export_table ) {
447 $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' ) );
448 }
449 if ( $user_can_delete_table ) {
450 $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' ) );
451 }
452 if ( $user_can_preview_table ) {
453 $row_actions['table-preview'] = sprintf( '<a href="%1$s" title="%2$s" target="_blank">%3$s</a>', $preview_url, esc_attr( sprintf( __( 'Show a preview of &#8220;%s&#8221;', 'tablepress' ), $item['name'] ) ), __( 'Preview', 'tablepress' ) );
454 }
455
456 return $row_text . $this->row_actions( $row_actions );
457 }
458
459 /**
460 * Render a cell in the "table_description" column.
461 *
462 * @since 1.0.0
463 *
464 * @param array $item Data item for the current row.
465 * @return string HTML content of the cell.
466 */
467 protected function column_table_description( array $item ) {
468 if ( '' === trim( $item['description'] ) ) {
469 $item['description'] = __( '(no description)', 'tablepress' );
470 }
471 return esc_html( $item['description'] );
472 }
473
474 /**
475 * Render a cell in the "table_author" column.
476 *
477 * @since 1.0.0
478 *
479 * @param array $item Data item for the current row.
480 * @return string HTML content of the cell.
481 */
482 protected function column_table_author( array $item ) {
483 return TablePress::get_user_display_name( $item['author'] );
484 }
485
486 /**
487 * Render a cell in the "last_modified_by" column.
488 *
489 * @since 1.0.0
490 *
491 * @param array $item Data item for the current row.
492 * @return string HTML content of the cell.
493 */
494 protected function column_table_last_modified_by( array $item ) {
495 return TablePress::get_user_display_name( $item['options']['last_editor'] );
496 }
497
498 /**
499 * Render a cell in the "table_last_modified" column.
500 *
501 * @since 1.0.0
502 *
503 * @param array $item Data item for the current row.
504 * @return string HTML content of the cell.
505 */
506 protected function column_table_last_modified( array $item ) {
507 $modified_timestamp = strtotime( $item['last_modified'] );
508 $current_timestamp = current_time( 'timestamp' );
509 $time_diff = $current_timestamp - $modified_timestamp;
510 // Time difference is only shown up to one day.
511 if ( $time_diff >= 0 && $time_diff < DAY_IN_SECONDS ) {
512 $time_diff = sprintf( __( '%s ago', 'tablepress' ), human_time_diff( $modified_timestamp, $current_timestamp ) );
513 } else {
514 $time_diff = TablePress::format_datetime( $item['last_modified'], 'mysql', '<br />' );
515 }
516
517 $readable_time = TablePress::format_datetime( $item['last_modified'], 'mysql', ' ' );
518 return '<abbr title="' . esc_attr( $readable_time ) . '">' . $time_diff . '</abbr>';
519 }
520
521 /**
522 * Handles output for the default column.
523 *
524 * @since 1.8.0
525 *
526 * @param array $item Data item for the current row.
527 * @param string $column_name Current column name.
528 */
529 protected function column_default( /* array */ $item, $column_name ) { // No `array` type hint to prevent a Strict Standards notice, as the method is inherited.
530 /**
531 * Fires inside each custom column of the TablePress list table.
532 *
533 * @since 1.8.0
534 *
535 * @param array $column_name Current column name.
536 * @param string $item Data item for the current row.
537 */
538 do_action( 'manage_tablepress_list_custom_column', $column_name, $item );
539 }
540
541 /**
542 * Get a list (name => title) bulk actions that are available.
543 *
544 * @since 1.0.0
545 *
546 * @return array Bulk actions for this table.
547 */
548 protected function get_bulk_actions() {
549 $bulk_actions = array();
550
551 if ( current_user_can( 'tablepress_copy_tables' ) ) {
552 $bulk_actions['copy'] = _x( 'Copy', 'bulk action', 'tablepress' );
553 }
554 if ( current_user_can( 'tablepress_export_tables' ) ) {
555 $bulk_actions['export'] = _x( 'Export', 'bulk action', 'tablepress' );
556 }
557 if ( current_user_can( 'tablepress_delete_tables' ) ) {
558 $bulk_actions['delete'] = _x( 'Delete', 'bulk action', 'tablepress' );
559 }
560
561 return $bulk_actions;
562 }
563
564 /**
565 * Render the bulk actions dropdown.
566 *
567 * In comparison with parent class, this has modified HTML (especially no field named "action" as that's being used already)!
568 *
569 * @since 1.0.0
570 *
571 * @param string $which The location of the bulk actions: 'top' or 'bottom'.
572 * This is designated as optional for backwards-compatibility.
573 */
574 protected function bulk_actions( $which = '' ) {
575 if ( is_null( $this->_actions ) ) {
576 $no_new_actions = $this->_actions = $this->get_bulk_actions();
577 /** This filter is documented in the WordPress function WP_List_Table::bulk_actions() in wp-admin/includes/class-wp-list-table.php */
578 $this->_actions = apply_filters( 'bulk_actions-' . $this->screen->id, $this->_actions );
579 $this->_actions = array_intersect_assoc( $this->_actions, $no_new_actions );
580 $two = '';
581 } else {
582 $two = '2';
583 }
584
585 if ( empty( $this->_actions ) ) {
586 return;
587 }
588
589 $name_id = "bulk-action-{$which}";
590 echo "<label for='{$name_id}' class='screen-reader-text'>" . __( 'Select Bulk Action', 'tablepress' ) . "</label>\n";
591 echo "<select name='{$name_id}' id='{$name_id}'>\n";
592 echo "<option value='-1' selected='selected'>" . __( 'Bulk Actions', 'tablepress' ) . "</option>\n";
593 foreach ( $this->_actions as $name => $title ) {
594 echo "\t<option value='{$name}'>{$title}</option>\n";
595 }
596 echo "</select>\n";
597 submit_button( __( 'Apply', 'tablepress' ), 'action', '', false, array( 'id' => "doaction{$two}" ) );
598 echo "\n";
599 }
600
601 /**
602 * Holds the message to be displayed when there are no items in the table.
603 *
604 * @since 1.0.0
605 */
606 public function no_items() {
607 _e( 'No tables found.', 'tablepress' );
608 if ( 0 === $this->items_count ) {
609 $user_can_add_tables = current_user_can( 'tablepress_add_tables' );
610 $user_can_import_tables = current_user_can( 'tablepress_import_tables' );
611
612 $add_url = TablePress::url( array( 'action' => 'add' ) );
613 $import_url = TablePress::url( array( 'action' => 'import' ) );
614
615 if ( $user_can_add_tables && $user_can_import_tables ) {
616 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 );
617 } elseif ( $user_can_add_tables ) {
618 echo ' ' . sprintf( __( 'You should <a href="%s">add</a> a table to get started!', 'tablepress' ), $add_url );
619 } elseif ( $user_can_import_tables ) {
620 echo ' ' . sprintf( __( 'You should <a href="%s">import</a> a table to get started!', 'tablepress' ), $import_url );
621 }
622 }
623 }
624
625 /**
626 * Generate the elements above or below the table (like bulk actions and pagination).
627 *
628 * In comparison with parent class, this has modified HTML (no nonce field), and a check whether there are items.
629 *
630 * @since 1.0.0
631 *
632 * @param string $which Location ("top" or "bottom").
633 */
634 protected function display_tablenav( $which ) {
635 if ( ! $this->has_items() ) {
636 return;
637 }
638 ?>
639 <div class="tablenav <?php echo esc_attr( $which ); ?>">
640 <div class="alignleft actions">
641 <?php $this->bulk_actions( $which ); ?>
642 </div>
643 <?php
644 $this->extra_tablenav( $which );
645 $this->pagination( $which );
646 ?>
647 <br class="clear" />
648 </div>
649 <?php
650 }
651
652 /**
653 * Callback to determine whether the given $item contains the search term.
654 *
655 * @since 1.0.0
656 *
657 * @param string $item Table ID that shall be searched.
658 * @return bool Whether the search term was found or not.
659 */
660 protected function _search_callback( $item ) {
661 static $term, $json_encoded_term;
662 if ( is_null( $term ) || is_null( $json_encoded_term ) ) {
663 $term = wp_unslash( $_GET['s'] );
664 $json_encoded_term = substr( wp_json_encode( $term, TABLEPRESS_JSON_OPTIONS ), 1, -1 );
665 }
666
667 static $debug;
668 if ( is_null( $debug ) ) {
669 // Set debug variable to allow searching in corrupted tables.
670 $debug = isset( $_GET['debug'] ) ? ( 'true' === $_GET['debug'] ) : WP_DEBUG;
671 }
672
673 // load table again, with data and options (for last_editor).
674 $item = TablePress::$model_table->load( $item, true, true );
675
676 // Don't search corrupted tables, except when debug mode is enabled via $_GET parameter or WP_DEBUG constant.
677 if ( ! $debug && isset( $item['is_corrupted'] ) && $item['is_corrupted'] ) {
678 return false;
679 }
680
681 // Search from easy to hard, so that "expensive" code maybe doesn't have to run.
682 if ( false !== stripos( $item['id'], $term )
683 || false !== stripos( $item['name'], $term )
684 || false !== stripos( $item['description'], $term )
685 || false !== stripos( TablePress::get_user_display_name( $item['author'] ), $term )
686 || false !== stripos( TablePress::get_user_display_name( $item['options']['last_editor'] ), $term )
687 || false !== stripos( TablePress::format_datetime( $item['last_modified'], 'mysql', ' ' ), $term )
688 || false !== stripos( wp_json_encode( $item['data'], TABLEPRESS_JSON_OPTIONS ), $json_encoded_term ) ) {
689 return true;
690 }
691
692 return false;
693 }
694
695 /**
696 * Callback to for the array sort function.
697 *
698 * @since 1.0.0
699 *
700 * @param array $item_a First item that shall be compared to.
701 * @param array $item_b The second item for the comparison.
702 * @return int (-1, 0, 1) depending on which item sorts "higher".
703 */
704 protected function _order_callback( array $item_a, array $item_b ) {
705 global $orderby, $order;
706
707 if ( 'last_modified_by' !== $orderby ) {
708 if ( $item_a[ $orderby ] === $item_b[ $orderby ] ) {
709 return 0;
710 }
711 } else {
712 if ( $item_a['options']['last_editor'] === $item_b['options']['last_editor'] ) {
713 return 0;
714 }
715 }
716
717 // Certain fields require some extra work before being sortable.
718 switch ( $orderby ) {
719 case 'last_modified':
720 // Compare UNIX timestamps for "last modified", which actually is a mySQL datetime string.
721 $result = ( strtotime( $item_a['last_modified'] ) > strtotime( $item_b['last_modified'] ) ) ? 1 : -1;
722 break;
723 case 'author':
724 // Get the actual author name, plain value is just the user ID.
725 $result = strnatcasecmp( TablePress::get_user_display_name( $item_a['author'] ), TablePress::get_user_display_name( $item_b['author'] ) );
726 break;
727 case 'last_modified_by':
728 // Get the actual last editor name, plain value is just the user ID.
729 $result = strnatcasecmp( TablePress::get_user_display_name( $item_a['options']['last_editor'] ), TablePress::get_user_display_name( $item_b['options']['last_editor'] ) );
730 break;
731 default:
732 // Other fields (ID, name, description) are sorted as strings.
733 $result = strnatcasecmp( $item_a[ $orderby ], $item_b[ $orderby ] );
734 }
735
736 return ( 'asc' === $order ) ? $result : - $result;
737 }
738
739 /**
740 * Prepares the list of items for displaying, by maybe searching and sorting, and by doing pagination.
741 *
742 * @since 1.0.0
743 */
744 public function prepare_items() {
745 global $orderby, $order, $s;
746 wp_reset_vars( array( 'orderby', 'order', 's' ) );
747
748 // Maybe search in the items.
749 if ( $s ) {
750 $this->items = array_filter( $this->items, array( $this, '_search_callback' ) );
751 }
752
753 // Load actual tables after search for less memory consumption.
754 foreach ( $this->items as &$item ) {
755 // Don't load data, but load table options for access to last_editor.
756 $item = TablePress::$model_table->load( $item, false, true );
757 }
758 // Break reference in foreach iterator.
759 unset( $item );
760
761 // Maybe sort the items.
762 $_sortable_columns = $this->get_sortable_columns();
763 if ( $orderby && ! empty( $this->items ) && isset( $_sortable_columns[ "table_{$orderby}" ] ) ) {
764 usort( $this->items, array( $this, '_order_callback' ) );
765 }
766
767 // Number of records to show per page.
768 $per_page = $this->get_items_per_page( 'tablepress_list_per_page', 20 ); // hard-coded, as in filter in Admin_Controller
769 // Page number the user is currently viewing.
770 $current_page = $this->get_pagenum();
771 // Number of records in the array.
772 $total_items = count( $this->items );
773
774 // Slice items array to hold only items for the current page.
775 $this->items = array_slice( $this->items, ( ( $current_page - 1 ) * $per_page ), $per_page );
776
777 // Register pagination options and calculation results.
778 $this->set_pagination_args( array(
779 'total_items' => $total_items, // Total number of records/items
780 'per_page' => $per_page, // Number of items per page
781 'total_pages' => ceil( $total_items / $per_page ), // Total number of pages
782 ) );
783 }
784
785 } // class TablePress_All_Tables_List_Table
786