Diff: STRATO-apps/wordpress_03/app/wp-admin/includes/class-wp-privacy-requests-table.php

Keine Baseline-Datei – Diff nur gegen leer.
Zur Liste
1 -
1 + <?php
2 + /**
3 + * List Table API: WP_Privacy_Requests_Table class
4 + *
5 + * @package WordPress
6 + * @subpackage Administration
7 + * @since 4.9.6
8 + */
9 +
10 + abstract class WP_Privacy_Requests_Table extends WP_List_Table {
11 +
12 + /**
13 + * Action name for the requests this table will work with. Classes
14 + * which inherit from WP_Privacy_Requests_Table should define this.
15 + *
16 + * Example: 'export_personal_data'.
17 + *
18 + * @since 4.9.6
19 + *
20 + * @var string $request_type Name of action.
21 + */
22 + protected $request_type = 'INVALID';
23 +
24 + /**
25 + * Post type to be used.
26 + *
27 + * @since 4.9.6
28 + *
29 + * @var string $post_type The post type.
30 + */
31 + protected $post_type = 'INVALID';
32 +
33 + /**
34 + * Gets columns to show in the list table.
35 + *
36 + * @since 4.9.6
37 + *
38 + * @return string[] Array of column titles keyed by their column name.
39 + */
40 + public function get_columns() {
41 + $columns = array(
42 + 'cb' => '<input type="checkbox" />',
43 + 'email' => __( 'Requester' ),
44 + 'status' => __( 'Status' ),
45 + 'created_timestamp' => __( 'Requested' ),
46 + 'next_steps' => __( 'Next steps' ),
47 + );
48 + return $columns;
49 + }
50 +
51 + /**
52 + * Normalizes the admin URL to the current page (by request_type).
53 + *
54 + * @since 5.3.0
55 + *
56 + * @return string URL to the current admin page.
57 + */
58 + protected function get_admin_url() {
59 + $pagenow = str_replace( '_', '-', $this->request_type );
60 +
61 + if ( 'remove-personal-data' === $pagenow ) {
62 + $pagenow = 'erase-personal-data';
63 + }
64 +
65 + return admin_url( $pagenow . '.php' );
66 + }
67 +
68 + /**
69 + * Gets a list of sortable columns.
70 + *
71 + * @since 4.9.6
72 + *
73 + * @return array Default sortable columns.
74 + */
75 + protected function get_sortable_columns() {
76 + /*
77 + * The initial sorting is by 'Requested' (post_date) and descending.
78 + * With initial sorting, the first click on 'Requested' should be ascending.
79 + * With 'Requester' sorting active, the next click on 'Requested' should be descending.
80 + */
81 + $desc_first = isset( $_GET['orderby'] );
82 +
83 + return array(
84 + 'email' => 'requester',
85 + 'created_timestamp' => array( 'requested', $desc_first ),
86 + );
87 + }
88 +
89 + /**
90 + * Returns the default primary column.
91 + *
92 + * @since 4.9.6
93 + *
94 + * @return string Default primary column name.
95 + */
96 + protected function get_default_primary_column_name() {
97 + return 'email';
98 + }
99 +
100 + /**
101 + * Counts the number of requests for each status.
102 + *
103 + * @since 4.9.6
104 + *
105 + * @global wpdb $wpdb WordPress database abstraction object.
106 + *
107 + * @return object Number of posts for each status.
108 + */
109 + protected function get_request_counts() {
110 + global $wpdb;
111 +
112 + $cache_key = $this->post_type . '-' . $this->request_type;
113 + $counts = wp_cache_get( $cache_key, 'counts' );
114 +
115 + if ( false !== $counts ) {
116 + return $counts;
117 + }
118 +
119 + $results = (array) $wpdb->get_results(
120 + $wpdb->prepare(
121 + "SELECT post_status, COUNT( * ) AS num_posts
122 + FROM {$wpdb->posts}
123 + WHERE post_type = %s
124 + AND post_name = %s
125 + GROUP BY post_status",
126 + $this->post_type,
127 + $this->request_type
128 + ),
129 + ARRAY_A
130 + );
131 +
132 + $counts = array_fill_keys( get_post_stati(), 0 );
133 +
134 + foreach ( $results as $row ) {
135 + $counts[ $row['post_status'] ] = $row['num_posts'];
136 + }
137 +
138 + $counts = (object) $counts;
139 + wp_cache_set( $cache_key, $counts, 'counts' );
140 +
141 + return $counts;
142 + }
143 +
144 + /**
145 + * Gets an associative array ( id => link ) with the list of views available on this table.
146 + *
147 + * @since 4.9.6
148 + *
149 + * @return string[] An array of HTML links keyed by their view.
150 + */
151 + protected function get_views() {
152 + $current_status = isset( $_REQUEST['filter-status'] ) ? sanitize_text_field( $_REQUEST['filter-status'] ) : '';
153 + $statuses = _wp_privacy_statuses();
154 + $views = array();
155 + $counts = $this->get_request_counts();
156 + $total_requests = absint( array_sum( (array) $counts ) );
157 +
158 + // Normalized admin URL.
159 + $admin_url = $this->get_admin_url();
160 +
161 + $status_label = sprintf(
162 + /* translators: %s: Number of requests. */
163 + _nx(
164 + 'All <span class="count">(%s)</span>',
165 + 'All <span class="count">(%s)</span>',
166 + $total_requests,
167 + 'requests'
168 + ),
169 + number_format_i18n( $total_requests )
170 + );
171 +
172 + $views['all'] = array(
173 + 'url' => esc_url( $admin_url ),
174 + 'label' => $status_label,
175 + 'current' => empty( $current_status ),
176 + );
177 +
178 + foreach ( $statuses as $status => $label ) {
179 + $post_status = get_post_status_object( $status );
180 + if ( ! $post_status ) {
181 + continue;
182 + }
183 +
184 + $total_status_requests = absint( $counts->{$status} );
185 +
186 + if ( ! $total_status_requests ) {
187 + continue;
188 + }
189 +
190 + $status_label = sprintf(
191 + translate_nooped_plural( $post_status->label_count, $total_status_requests ),
192 + number_format_i18n( $total_status_requests )
193 + );
194 +
195 + $status_link = add_query_arg( 'filter-status', $status, $admin_url );
196 +
197 + $views[ $status ] = array(
198 + 'url' => esc_url( $status_link ),
199 + 'label' => $status_label,
200 + 'current' => $status === $current_status,
201 + );
202 + }
203 +
204 + return $this->get_views_links( $views );
205 + }
206 +
207 + /**
208 + * Gets bulk actions.
209 + *
210 + * @since 4.9.6
211 + *
212 + * @return array Array of bulk action labels keyed by their action.
213 + */
214 + protected function get_bulk_actions() {
215 + return array(
216 + 'resend' => __( 'Resend confirmation requests' ),
217 + 'complete' => __( 'Mark requests as completed' ),
218 + 'delete' => __( 'Delete requests' ),
219 + );
220 + }
221 +
222 + /**
223 + * Process bulk actions.
224 + *
225 + * @since 4.9.6
226 + * @since 5.6.0 Added support for the `complete` action.
227 + */
228 + public function process_bulk_action() {
229 + $action = $this->current_action();
230 + $request_ids = isset( $_REQUEST['request_id'] ) ? wp_parse_id_list( wp_unslash( $_REQUEST['request_id'] ) ) : array();
231 +
232 + if ( empty( $request_ids ) ) {
233 + return;
234 + }
235 +
236 + $count = 0;
237 + $failures = 0;
238 +
239 + check_admin_referer( 'bulk-privacy_requests' );
240 +
241 + switch ( $action ) {
242 + case 'resend':
243 + foreach ( $request_ids as $request_id ) {
244 + $resend = _wp_privacy_resend_request( $request_id );
245 +
246 + if ( $resend && ! is_wp_error( $resend ) ) {
247 + ++$count;
248 + } else {
249 + ++$failures;
250 + }
251 + }
252 +
253 + if ( $failures ) {
254 + add_settings_error(
255 + 'bulk_action',
256 + 'bulk_action',
257 + sprintf(
258 + /* translators: %d: Number of requests. */
259 + _n(
260 + '%d confirmation request failed to resend.',
261 + '%d confirmation requests failed to resend.',
262 + $failures
263 + ),
264 + $failures
265 + ),
266 + 'error'
267 + );
268 + }
269 +
270 + if ( $count ) {
271 + add_settings_error(
272 + 'bulk_action',
273 + 'bulk_action',
274 + sprintf(
275 + /* translators: %d: Number of requests. */
276 + _n(
277 + '%d confirmation request re-sent successfully.',
278 + '%d confirmation requests re-sent successfully.',
279 + $count
280 + ),
281 + $count
282 + ),
283 + 'success'
284 + );
285 + }
286 +
287 + break;
288 +
289 + case 'complete':
290 + foreach ( $request_ids as $request_id ) {
291 + $result = _wp_privacy_completed_request( $request_id );
292 +
293 + if ( $result && ! is_wp_error( $result ) ) {
294 + ++$count;
295 + }
296 + }
297 +
298 + add_settings_error(
299 + 'bulk_action',
300 + 'bulk_action',
301 + sprintf(
302 + /* translators: %d: Number of requests. */
303 + _n(
304 + '%d request marked as complete.',
305 + '%d requests marked as complete.',
306 + $count
307 + ),
308 + $count
309 + ),
310 + 'success'
311 + );
312 + break;
313 +
314 + case 'delete':
315 + foreach ( $request_ids as $request_id ) {
316 + if ( wp_delete_post( $request_id, true ) ) {
317 + ++$count;
318 + } else {
319 + ++$failures;
320 + }
321 + }
322 +
323 + if ( $failures ) {
324 + add_settings_error(
325 + 'bulk_action',
326 + 'bulk_action',
327 + sprintf(
328 + /* translators: %d: Number of requests. */
329 + _n(
330 + '%d request failed to delete.',
331 + '%d requests failed to delete.',
332 + $failures
333 + ),
334 + $failures
335 + ),
336 + 'error'
337 + );
338 + }
339 +
340 + if ( $count ) {
341 + add_settings_error(
342 + 'bulk_action',
343 + 'bulk_action',
344 + sprintf(
345 + /* translators: %d: Number of requests. */
346 + _n(
347 + '%d request deleted successfully.',
348 + '%d requests deleted successfully.',
349 + $count
350 + ),
351 + $count
352 + ),
353 + 'success'
354 + );
355 + }
356 +
357 + break;
358 + }
359 + }
360 +
361 + /**
362 + * Prepares items to output.
363 + *
364 + * @since 4.9.6
365 + * @since 5.1.0 Added support for column sorting.
366 + */
367 + public function prepare_items() {
368 + $this->items = array();
369 + $posts_per_page = $this->get_items_per_page( $this->request_type . '_requests_per_page' );
370 + $args = array(
371 + 'post_type' => $this->post_type,
372 + 'post_name__in' => array( $this->request_type ),
373 + 'posts_per_page' => $posts_per_page,
374 + 'offset' => isset( $_REQUEST['paged'] ) ? max( 0, absint( $_REQUEST['paged'] ) - 1 ) * $posts_per_page : 0,
375 + 'post_status' => 'any',
376 + 's' => isset( $_REQUEST['s'] ) ? sanitize_text_field( $_REQUEST['s'] ) : '',
377 + );
378 +
379 + $orderby_mapping = array(
380 + 'requester' => 'post_title',
381 + 'requested' => 'post_date',
382 + );
383 +
384 + if ( isset( $_REQUEST['orderby'] ) && isset( $orderby_mapping[ $_REQUEST['orderby'] ] ) ) {
385 + $args['orderby'] = $orderby_mapping[ $_REQUEST['orderby'] ];
386 + }
387 +
388 + if ( isset( $_REQUEST['order'] ) && in_array( strtoupper( $_REQUEST['order'] ), array( 'ASC', 'DESC' ), true ) ) {
389 + $args['order'] = strtoupper( $_REQUEST['order'] );
390 + }
391 +
392 + if ( ! empty( $_REQUEST['filter-status'] ) ) {
393 + $filter_status = isset( $_REQUEST['filter-status'] ) ? sanitize_text_field( $_REQUEST['filter-status'] ) : '';
394 + $args['post_status'] = $filter_status;
395 + }
396 +
397 + $requests_query = new WP_Query( $args );
398 + $requests = $requests_query->posts;
399 +
400 + foreach ( $requests as $request ) {
401 + $this->items[] = wp_get_user_request( $request->ID );
402 + }
403 +
404 + $this->items = array_filter( $this->items );
405 +
406 + $this->set_pagination_args(
407 + array(
408 + 'total_items' => $requests_query->found_posts,
409 + 'per_page' => $posts_per_page,
410 + )
411 + );
412 + }
413 +
414 + /**
415 + * Returns the markup for the Checkbox column.
416 + *
417 + * @since 4.9.6
418 + *
419 + * @param WP_User_Request $item Item being shown.
420 + * @return string Checkbox column markup.
421 + */
422 + public function column_cb( $item ) {
423 + return sprintf(
424 + '<input type="checkbox" name="request_id[]" id="requester_%1$s" value="%1$s" />' .
425 + '<label for="requester_%1$s"><span class="screen-reader-text">%2$s</span></label><span class="spinner"></span>',
426 + esc_attr( $item->ID ),
427 + /* translators: Hidden accessibility text. %s: Email address. */
428 + sprintf( __( 'Select %s' ), $item->email )
429 + );
430 + }
431 +
432 + /**
433 + * Status column.
434 + *
435 + * @since 4.9.6
436 + *
437 + * @param WP_User_Request $item Item being shown.
438 + * @return string|void Status column markup. Returns a string if no status is found,
439 + * otherwise it displays the markup.
440 + */
441 + public function column_status( $item ) {
442 + $status = get_post_status( $item->ID );
443 + $status_object = get_post_status_object( $status );
444 +
445 + if ( ! $status_object || empty( $status_object->label ) ) {
446 + return '-';
447 + }
448 +
449 + $timestamp = false;
450 +
451 + switch ( $status ) {
452 + case 'request-confirmed':
453 + $timestamp = $item->confirmed_timestamp;
454 + break;
455 + case 'request-completed':
456 + $timestamp = $item->completed_timestamp;
457 + break;
458 + }
459 +
460 + echo '<span class="status-label status-' . esc_attr( $status ) . '">';
461 + echo esc_html( $status_object->label );
462 +
463 + if ( $timestamp ) {
464 + echo '<span class="status-date">' . $this->get_timestamp_as_date( $timestamp ) . '</span>';
465 + }
466 +
467 + echo '</span>';
468 + }
469 +
470 + /**
471 + * Converts a timestamp for display.
472 + *
473 + * @since 4.9.6
474 + *
475 + * @param int $timestamp Event timestamp.
476 + * @return string Human readable date.
477 + */
478 + protected function get_timestamp_as_date( $timestamp ) {
479 + if ( empty( $timestamp ) ) {
480 + return '';
481 + }
482 +
483 + $time_diff = time() - $timestamp;
484 +
485 + if ( $time_diff >= 0 && $time_diff < DAY_IN_SECONDS ) {
486 + /* translators: %s: Human-readable time difference. */
487 + return sprintf( __( '%s ago' ), human_time_diff( $timestamp ) );
488 + }
489 +
490 + return sprintf(
491 + /* translators: 1: privacy request date format, 2: privacy request time format. */
492 + __( '%1$s at %2$s' ),
493 + /* translators: privacy request date format. See https://www.php.net/manual/en/datetime.format.php */
494 + date_i18n( __( 'Y/m/d' ), $timestamp ),
495 + /* translators: privacy request time format. See https://www.php.net/manual/en/datetime.format.php */
496 + date_i18n( __( 'g:i a' ), $timestamp )
497 + );
498 + }
499 +
500 + /**
501 + * Handles the default column.
502 + *
503 + * @since 4.9.6
504 + * @since 5.7.0 Added `manage_{$this->screen->id}_custom_column` action.
505 + *
506 + * @param WP_User_Request $item Item being shown.
507 + * @param string $column_name Name of column being shown.
508 + */
509 + public function column_default( $item, $column_name ) {
510 + /**
511 + * Fires for each custom column of a specific request type in the Privacy Requests list table.
512 + *
513 + * Custom columns are registered using the {@see 'manage_export-personal-data_columns'}
514 + * and the {@see 'manage_erase-personal-data_columns'} filters.
515 + *
516 + * The dynamic portion of the hook name, `$this->screen->id`, refers to the ID given to the list table
517 + * according to which screen it's displayed on.
518 + *
519 + * Possible hook names include:
520 + *
521 + * - `manage_export-personal-data_custom_column`
522 + * - `manage_erase-personal-data_custom_column`
523 + *
524 + * @since 5.7.0
525 + *
526 + * @param string $column_name The name of the column to display.
527 + * @param WP_User_Request $item The item being shown.
528 + */
529 + do_action( "manage_{$this->screen->id}_custom_column", $column_name, $item );
530 + }
531 +
532 + /**
533 + * Returns the markup for the Created timestamp column. Overridden by children.
534 + *
535 + * @since 5.7.0
536 + *
537 + * @param WP_User_Request $item Item being shown.
538 + * @return string Human readable date.
539 + */
540 + public function column_created_timestamp( $item ) {
541 + return $this->get_timestamp_as_date( $item->created_timestamp );
542 + }
543 +
544 + /**
545 + * Actions column. Overridden by children.
546 + *
547 + * @since 4.9.6
548 + *
549 + * @param WP_User_Request $item Item being shown.
550 + * @return string Email column markup.
551 + */
552 + public function column_email( $item ) {
553 + return sprintf( '<a href="%1$s">%2$s</a> %3$s', esc_url( 'mailto:' . $item->email ), $item->email, $this->row_actions( array() ) );
554 + }
555 +
556 + /**
557 + * Returns the markup for the next steps column. Overridden by children.
558 + *
559 + * @since 4.9.6
560 + *
561 + * @param WP_User_Request $item Item being shown.
562 + */
563 + public function column_next_steps( $item ) {}
564 +
565 + /**
566 + * Generates content for a single row of the table,
567 + *
568 + * @since 4.9.6
569 + *
570 + * @param WP_User_Request $item The current item.
571 + */
572 + public function single_row( $item ) {
573 + $status = $item->status;
574 +
575 + echo '<tr id="request-' . esc_attr( $item->ID ) . '" class="status-' . esc_attr( $status ) . '">';
576 + $this->single_row_columns( $item );
577 + echo '</tr>';
578 + }
579 +
580 + /**
581 + * Embeds scripts used to perform actions. Overridden by children.
582 + *
583 + * @since 4.9.6
584 + */
585 + public function embed_scripts() {}
586 + }
587 +