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-editor-button-thickbox-list-table.php

class-editor-button-thickbox-list-table.php in TablePress – Tables in WordPress made easy 3.4, at views/class-editor-button-thickbox-list-table.php

379 lines 11.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Editor Button Thickbox 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 Editor Button Thickbox List Table Class
18 *
19 * @package TablePress
20 * @subpackage Views
21 * @author Tobias Bäthge
22 * @since 1.0.0
23 */
24 class TablePress_Editor_Button_Thickbox_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 * Initializes the List Table.
35 *
36 * @since 1.0.0
37 */
38 public function __construct() {
39 // @phpstan-ignore argument.type (WordPress Core's docblocks state wrong argument types in some places.)
40 parent::__construct( array(
41 'singular' => 'tablepress-table', // Singular name of the listed records.
42 'plural' => 'tablepress-editor-button-list', // Plural name of the listed records.
43 'ajax' => false, // Does this list table support AJAX?
44 'screen' => get_current_screen(), // WP_Screen object.
45 ) );
46 }
47
48 /**
49 * Sets the data items (here: tables) that are to be displayed by the List Tables, and their original count.
50 *
51 * @since 1.0.0
52 *
53 * @param string[] $items Tables to be displayed in the List Table.
54 */
55 public function set_items( array $items ): void {
56 $this->items = $items;
57 $this->items_count = count( $items );
58 }
59
60 /**
61 * Checks whether the user has permissions for certain AJAX actions.
62 * (not used, but must be implemented in this child class)
63 *
64 * @since 1.0.0
65 *
66 * @return bool true (Default value).
67 */
68 #[\Override]
69 public function ajax_user_can(): bool {
70 return true;
71 }
72
73 /**
74 * Gets a list of columns in this List Table.
75 *
76 * Format: 'internal-name' => 'Column Title'.
77 *
78 * @since 1.0.0
79 *
80 * @return array<string, string> List of columns in this List Table.
81 */
82 #[\Override]
83 public function get_columns(): array {
84 $columns = array(
85 // "name" is special in WP, which is why we prefix every entry here, to be safe!
86 'table_id' => __( 'ID', 'tablepress' ),
87 'table_name' => __( 'Table Name', 'tablepress' ),
88 'table_description' => __( 'Description', 'tablepress' ),
89 'table_action' => __( 'Action', 'tablepress' ),
90 );
91 return $columns;
92 }
93
94 /**
95 * Gets a list of columns that are sortable.
96 *
97 * Format: 'internal-name' => array( $field for $item[ $field ], true for already sorted ).
98 *
99 * @since 1.0.0
100 *
101 * @return array<string, array{string, bool}> List of sortable columns in this List Table.
102 */
103 #[\Override]
104 protected function get_sortable_columns(): array {
105 // No sorting on the Empty List placeholder.
106 if ( ! $this->has_items() ) {
107 return array();
108 }
109
110 $sortable_columns = array(
111 'table_id' => array( 'id', true ), // true means its already sorted.
112 'table_name' => array( 'name', false ),
113 'table_description' => array( 'description', false ),
114 );
115 return $sortable_columns;
116 }
117
118 /**
119 * Gets the name of the default primary column.
120 *
121 * @since 1.7.0
122 *
123 * @return string Name of the default primary column, in this case, the table name.
124 */
125 #[\Override]
126 protected function get_default_primary_column_name(): string {
127 return 'table_name';
128 }
129
130 /**
131 * Renders a cell in the "table_id" column.
132 *
133 * @since 1.0.0
134 *
135 * @param array<string, mixed> $item Data item for the current row.
136 * @return string HTML content of the cell.
137 */
138 protected function column_table_id( array $item ): string {
139 return esc_html( $item['id'] );
140 }
141
142 /**
143 * Returns a clean label for the primary (Table Name) column's row header `aria-label`.
144 *
145 * Provides screen readers with just the table name as the row header name,
146 * preventing them from computing the name from the full cell content.
147 *
148 * @since 3.3.4
149 *
150 * @param array<string, mixed> $item Data item for the current row.
151 * @return string The table name.
152 */
153 protected function get_primary_column_aria_label( /* array */ $item ): string {
154 // Don't use type hints in the method declaration to prevent PHP errors, as the method is inherited.
155
156 return trim( $item['name'] );
157 }
158
159 /**
160 * Renders a cell in the "table_name" column.
161 *
162 * @since 1.0.0
163 *
164 * @param array<string, mixed> $item Data item for the current row.
165 * @return string HTML content of the cell.
166 */
167 protected function column_table_name( array $item ): string {
168 if ( '' === trim( $item['name'] ) ) {
169 $item['name'] = __( '(no name)', 'tablepress' );
170 }
171 return esc_html( $item['name'] );
172 }
173
174 /**
175 * Renders a cell in the "table_description" column.
176 *
177 * @since 1.0.0
178 *
179 * @param array<string, mixed> $item Data item for the current row.
180 * @return string HTML content of the cell.
181 */
182 protected function column_table_description( array $item ): string {
183 if ( '' === trim( $item['description'] ) ) {
184 $item['description'] = __( '(no description)', 'tablepress' );
185 }
186 return esc_html( $item['description'] );
187 }
188
189 /**
190 * Renders a cell in the "table_action" column, i.e. the "Insert" link.
191 *
192 * @since 1.0.0
193 *
194 * @param array<string, mixed> $item Data item for the current row.
195 * @return string HTML content of the cell.
196 */
197 protected function column_table_action( array $item ): string {
198 return '<input type="button" class="insert-shortcode button button-compact" title="' . esc_attr( '[' . TablePress::$shortcode . " id={$item['id']} /]" ) . '" value="' . esc_attr__( 'Insert Shortcode', 'tablepress' ) . '">';
199 }
200
201 /**
202 * Holds the message to be displayed when there are no items in the table.
203 *
204 * @since 1.0.0
205 */
206 #[\Override]
207 public function no_items(): void {
208 _e( 'No tables found.', 'tablepress' );
209 if ( 0 === $this->items_count ) {
210 echo ' ' . __( 'You should add or import a table on the TablePress screens to get started!', 'tablepress' );
211 }
212 }
213
214 /**
215 * Generates the elements above or below the table (like bulk actions and pagination).
216 *
217 * In comparison with parent class, this has modified HTML (no nonce field), and a check whether there are items.
218 *
219 * @since 1.0.0
220 *
221 * @param 'top'|'bottom' $which Location ("top" or "bottom").
222 */
223 #[\Override]
224 protected function display_tablenav( /* string */ $which ): void {
225 // Don't use type hints in the method declaration to prevent PHP errors, as the method is inherited.
226
227 if ( ! $this->has_items() ) {
228 return;
229 }
230 ?>
231 <div class="tablenav <?php echo esc_attr( $which ); ?>">
232 <div class="alignleft actions">
233 <?php $this->bulk_actions( $which ); ?>
234 </div>
235 <?php
236 $this->extra_tablenav( $which );
237
238 add_filter( 'ngettext_default', array( $this, 'change_pagination_items_string' ), 10, 5 );
239 $this->pagination( $which );
240 remove_filter( 'ngettext_default', array( $this, 'change_pagination_items_string' ), 10 );
241 ?>
242 <br class="clear">
243 </div>
244 <?php
245 }
246
247 /**
248 * Replaces the "%s item/%s items" string in the pagination with "%s table/%s tables".
249 *
250 * @since 2.0.0
251 *
252 * @param string $translation The current translation of a singular or plural form.
253 * @param string $single The text to be used if the number is singular.
254 * @param string $plural The text to be used if the number is plural.
255 * @param int $number The number to compare against to use either the singular or plural form.
256 * @param string $domain Text domain. Defaults to 'default'.
257 * @return string The changed translation.
258 */
259 public function change_pagination_items_string( string $translation, string $single, string $plural, int $number, string $domain ): string {
260 if ( '%s item' === $single && '%s items' === $plural ) {
261 /* translators: %s: Number of tables */
262 $translation = _n( '%s table', '%s tables', $number, 'tablepress' );
263 }
264 return $translation;
265 }
266
267 /**
268 * Callback to determine whether the given $item contains the search term.
269 *
270 * @since 1.0.0
271 *
272 * @param string $item Table ID that shall be searched.
273 * @return bool Whether the search term was found or not.
274 */
275 protected function _search_callback( string $item ): bool {
276 static $term;
277 static $json_encoded_term;
278 if ( is_null( $term ) || is_null( $json_encoded_term ) ) {
279 $term = wp_unslash( $_GET['s'] );
280 $json_encoded_term = substr( wp_json_encode( $term, TABLEPRESS_JSON_OPTIONS ), 1, -1 ); // @phpstan-ignore argument.type
281 }
282
283 // Load table again, with table data, but without options and visibility settings.
284 $item = TablePress::$model_table->load( $item, true, false );
285
286 if ( is_wp_error( $item ) ) {
287 return false;
288 }
289
290 // Don't search corrupted tables.
291 if ( isset( $item['is_corrupted'] ) && $item['is_corrupted'] ) {
292 return false;
293 }
294
295 $fn_stripos = function_exists( 'mb_stripos' ) ? 'mb_stripos' : 'stripos';
296
297 // Search from easy to hard, so that "expensive" code maybe doesn't have to run.
298 if ( false !== $fn_stripos( $item['id'], (string) $term )
299 || false !== $fn_stripos( $item['name'], (string) $term )
300 || false !== $fn_stripos( $item['description'], (string) $term )
301 || false !== $fn_stripos( TablePress::get_user_display_name( $item['author'] ), (string) $term )
302 || false !== $fn_stripos( TablePress::format_datetime( $item['last_modified'] ), (string) $term )
303 || false !== $fn_stripos( wp_json_encode( $item['data'], TABLEPRESS_JSON_OPTIONS ), (string) $json_encoded_term ) ) { // @phpstan-ignore argument.type
304 return true;
305 }
306
307 return false;
308 }
309
310 /**
311 * Callback to for the array sort function.
312 *
313 * @since 1.0.0
314 *
315 * @param array<string, mixed> $item_a First item that shall be compared to.
316 * @param array<string, mixed> $item_b The second item for the comparison.
317 * @return int (-1, 0, 1) depending on which item sorts "higher".
318 */
319 protected function _order_callback( array $item_a, array $item_b ): int {
320 global $orderby, $order;
321
322 if ( $item_a[ $orderby ] === $item_b[ $orderby ] ) {
323 return 0;
324 }
325
326 // Fields in this list table are all strings.
327 $result = strnatcasecmp( $item_a[ $orderby ], $item_b[ $orderby ] );
328
329 return ( 'asc' === $order ) ? $result : - $result;
330 }
331
332 /**
333 * Prepares the list of items for displaying, by maybe searching and sorting, and by doing pagination.
334 *
335 * @since 1.0.0
336 */
337 #[\Override]
338 public function prepare_items(): void {
339 global $orderby, $order, $s;
340 wp_reset_vars( array( 'orderby', 'order', 's' ) );
341
342 // Maybe search in the items.
343 if ( $s ) {
344 $this->items = array_filter( $this->items, array( $this, '_search_callback' ) );
345 }
346
347 // Load actual tables after search for less memory consumption.
348 foreach ( $this->items as &$item ) {
349 // Don't load data nor table options.
350 $item = TablePress::$model_table->load( $item, false, false );
351 }
352 unset( $item ); // Unset use-by-reference parameter of foreach loop.
353
354 // Maybe sort the items.
355 $_sortable_columns = $this->get_sortable_columns();
356 if ( $orderby && ! empty( $this->items ) && isset( $_sortable_columns[ "table_{$orderby}" ] ) ) {
357 usort( $this->items, array( $this, '_order_callback' ) );
358 }
359
360 // Number of records to show per page.
361 $per_page = 20; // Hard-coded, as there's no possibility to change this in the Thickbox.
362 // Page number the user is currently viewing.
363 $current_page = $this->get_pagenum();
364 // Number of records in the array.
365 $total_items = count( $this->items );
366
367 // Slice items array to hold only items for the current page.
368 $this->items = array_slice( $this->items, ( ( $current_page - 1 ) * $per_page ), $per_page );
369
370 // Register pagination options and calculation results.
371 $this->set_pagination_args( array(
372 'total_items' => $total_items, // Total number of records/items.
373 'per_page' => $per_page, // Number of items per page.
374 'total_pages' => (int) ceil( $total_items / $per_page ), // Total number of pages.
375 ) );
376 }
377
378 } // class TablePress_Editor_Button_Thickbox_List_Table
379