angelovcom.net

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

class-wp-privacy-requests-table.php (13736B)


      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 	 * Get 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 	 * Normalize 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 	 * Get 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 	 * 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 	 * Count number of requests for each status.
    102 	 *
    103 	 * @since 4.9.6
    104 	 *
    105 	 * @return object Number of posts for each status.
    106 	 */
    107 	protected function get_request_counts() {
    108 		global $wpdb;
    109 
    110 		$cache_key = $this->post_type . '-' . $this->request_type;
    111 		$counts    = wp_cache_get( $cache_key, 'counts' );
    112 
    113 		if ( false !== $counts ) {
    114 			return $counts;
    115 		}
    116 
    117 		$query = "
    118 			SELECT post_status, COUNT( * ) AS num_posts
    119 			FROM {$wpdb->posts}
    120 			WHERE post_type = %s
    121 			AND post_name = %s
    122 			GROUP BY post_status";
    123 
    124 		$results = (array) $wpdb->get_results( $wpdb->prepare( $query, $this->post_type, $this->request_type ), ARRAY_A );
    125 		$counts  = array_fill_keys( get_post_stati(), 0 );
    126 
    127 		foreach ( $results as $row ) {
    128 			$counts[ $row['post_status'] ] = $row['num_posts'];
    129 		}
    130 
    131 		$counts = (object) $counts;
    132 		wp_cache_set( $cache_key, $counts, 'counts' );
    133 
    134 		return $counts;
    135 	}
    136 
    137 	/**
    138 	 * Get an associative array ( id => link ) with the list of views available on this table.
    139 	 *
    140 	 * @since 4.9.6
    141 	 *
    142 	 * @return string[] An array of HTML links keyed by their view.
    143 	 */
    144 	protected function get_views() {
    145 		$current_status = isset( $_REQUEST['filter-status'] ) ? sanitize_text_field( $_REQUEST['filter-status'] ) : '';
    146 		$statuses       = _wp_privacy_statuses();
    147 		$views          = array();
    148 		$counts         = $this->get_request_counts();
    149 		$total_requests = absint( array_sum( (array) $counts ) );
    150 
    151 		// Normalized admin URL.
    152 		$admin_url = $this->get_admin_url();
    153 
    154 		$current_link_attributes = empty( $current_status ) ? ' class="current" aria-current="page"' : '';
    155 		$status_label            = sprintf(
    156 			/* translators: %s: Number of requests. */
    157 			_nx(
    158 				'All <span class="count">(%s)</span>',
    159 				'All <span class="count">(%s)</span>',
    160 				$total_requests,
    161 				'requests'
    162 			),
    163 			number_format_i18n( $total_requests )
    164 		);
    165 
    166 		$views['all'] = sprintf(
    167 			'<a href="%s"%s>%s</a>',
    168 			esc_url( $admin_url ),
    169 			$current_link_attributes,
    170 			$status_label
    171 		);
    172 
    173 		foreach ( $statuses as $status => $label ) {
    174 			$post_status = get_post_status_object( $status );
    175 			if ( ! $post_status ) {
    176 				continue;
    177 			}
    178 
    179 			$current_link_attributes = $status === $current_status ? ' class="current" aria-current="page"' : '';
    180 			$total_status_requests   = absint( $counts->{$status} );
    181 
    182 			if ( ! $total_status_requests ) {
    183 				continue;
    184 			}
    185 
    186 			$status_label = sprintf(
    187 				translate_nooped_plural( $post_status->label_count, $total_status_requests ),
    188 				number_format_i18n( $total_status_requests )
    189 			);
    190 
    191 			$status_link = add_query_arg( 'filter-status', $status, $admin_url );
    192 
    193 			$views[ $status ] = sprintf(
    194 				'<a href="%s"%s>%s</a>',
    195 				esc_url( $status_link ),
    196 				$current_link_attributes,
    197 				$status_label
    198 			);
    199 		}
    200 
    201 		return $views;
    202 	}
    203 
    204 	/**
    205 	 * Get bulk actions.
    206 	 *
    207 	 * @since 4.9.6
    208 	 *
    209 	 * @return array Array of bulk action labels keyed by their action.
    210 	 */
    211 	protected function get_bulk_actions() {
    212 		return array(
    213 			'resend'   => __( 'Resend confirmation requests' ),
    214 			'complete' => __( 'Mark requests as completed' ),
    215 			'delete'   => __( 'Delete requests' ),
    216 		);
    217 	}
    218 
    219 	/**
    220 	 * Process bulk actions.
    221 	 *
    222 	 * @since 4.9.6
    223 	 * @since 5.6.0 Added support for the `complete` action.
    224 	 */
    225 	public function process_bulk_action() {
    226 		$action      = $this->current_action();
    227 		$request_ids = isset( $_REQUEST['request_id'] ) ? wp_parse_id_list( wp_unslash( $_REQUEST['request_id'] ) ) : array();
    228 
    229 		if ( empty( $request_ids ) ) {
    230 			return;
    231 		}
    232 
    233 		$count    = 0;
    234 		$failures = 0;
    235 
    236 		check_admin_referer( 'bulk-privacy_requests' );
    237 
    238 		switch ( $action ) {
    239 			case 'resend':
    240 				foreach ( $request_ids as $request_id ) {
    241 					$resend = _wp_privacy_resend_request( $request_id );
    242 
    243 					if ( $resend && ! is_wp_error( $resend ) ) {
    244 						$count++;
    245 					} else {
    246 						$failures++;
    247 					}
    248 				}
    249 
    250 				if ( $failures ) {
    251 					add_settings_error(
    252 						'bulk_action',
    253 						'bulk_action',
    254 						sprintf(
    255 							/* translators: %d: Number of requests. */
    256 							_n(
    257 								'%d confirmation request failed to resend.',
    258 								'%d confirmation requests failed to resend.',
    259 								$failures
    260 							),
    261 							$failures
    262 						),
    263 						'error'
    264 					);
    265 				}
    266 
    267 				if ( $count ) {
    268 					add_settings_error(
    269 						'bulk_action',
    270 						'bulk_action',
    271 						sprintf(
    272 							/* translators: %d: Number of requests. */
    273 							_n(
    274 								'%d confirmation request re-sent successfully.',
    275 								'%d confirmation requests re-sent successfully.',
    276 								$count
    277 							),
    278 							$count
    279 						),
    280 						'success'
    281 					);
    282 				}
    283 
    284 				break;
    285 
    286 			case 'complete':
    287 				foreach ( $request_ids as $request_id ) {
    288 					$result = _wp_privacy_completed_request( $request_id );
    289 
    290 					if ( $result && ! is_wp_error( $result ) ) {
    291 						$count++;
    292 					}
    293 				}
    294 
    295 				add_settings_error(
    296 					'bulk_action',
    297 					'bulk_action',
    298 					sprintf(
    299 						/* translators: %d: Number of requests. */
    300 						_n(
    301 							'%d request marked as complete.',
    302 							'%d requests marked as complete.',
    303 							$count
    304 						),
    305 						$count
    306 					),
    307 					'success'
    308 				);
    309 				break;
    310 
    311 			case 'delete':
    312 				foreach ( $request_ids as $request_id ) {
    313 					if ( wp_delete_post( $request_id, true ) ) {
    314 						$count++;
    315 					} else {
    316 						$failures++;
    317 					}
    318 				}
    319 
    320 				if ( $failures ) {
    321 					add_settings_error(
    322 						'bulk_action',
    323 						'bulk_action',
    324 						sprintf(
    325 							/* translators: %d: Number of requests. */
    326 							_n(
    327 								'%d request failed to delete.',
    328 								'%d requests failed to delete.',
    329 								$failures
    330 							),
    331 							$failures
    332 						),
    333 						'error'
    334 					);
    335 				}
    336 
    337 				if ( $count ) {
    338 					add_settings_error(
    339 						'bulk_action',
    340 						'bulk_action',
    341 						sprintf(
    342 							/* translators: %d: Number of requests. */
    343 							_n(
    344 								'%d request deleted successfully.',
    345 								'%d requests deleted successfully.',
    346 								$count
    347 							),
    348 							$count
    349 						),
    350 						'success'
    351 					);
    352 				}
    353 
    354 				break;
    355 		}
    356 	}
    357 
    358 	/**
    359 	 * Prepare items to output.
    360 	 *
    361 	 * @since 4.9.6
    362 	 * @since 5.1.0 Added support for column sorting.
    363 	 */
    364 	public function prepare_items() {
    365 		$this->items    = array();
    366 		$posts_per_page = $this->get_items_per_page( $this->request_type . '_requests_per_page' );
    367 		$args           = array(
    368 			'post_type'      => $this->post_type,
    369 			'post_name__in'  => array( $this->request_type ),
    370 			'posts_per_page' => $posts_per_page,
    371 			'offset'         => isset( $_REQUEST['paged'] ) ? max( 0, absint( $_REQUEST['paged'] ) - 1 ) * $posts_per_page : 0,
    372 			'post_status'    => 'any',
    373 			's'              => isset( $_REQUEST['s'] ) ? sanitize_text_field( $_REQUEST['s'] ) : '',
    374 		);
    375 
    376 		$orderby_mapping = array(
    377 			'requester' => 'post_title',
    378 			'requested' => 'post_date',
    379 		);
    380 
    381 		if ( isset( $_REQUEST['orderby'] ) && isset( $orderby_mapping[ $_REQUEST['orderby'] ] ) ) {
    382 			$args['orderby'] = $orderby_mapping[ $_REQUEST['orderby'] ];
    383 		}
    384 
    385 		if ( isset( $_REQUEST['order'] ) && in_array( strtoupper( $_REQUEST['order'] ), array( 'ASC', 'DESC' ), true ) ) {
    386 			$args['order'] = strtoupper( $_REQUEST['order'] );
    387 		}
    388 
    389 		if ( ! empty( $_REQUEST['filter-status'] ) ) {
    390 			$filter_status       = isset( $_REQUEST['filter-status'] ) ? sanitize_text_field( $_REQUEST['filter-status'] ) : '';
    391 			$args['post_status'] = $filter_status;
    392 		}
    393 
    394 		$requests_query = new WP_Query( $args );
    395 		$requests       = $requests_query->posts;
    396 
    397 		foreach ( $requests as $request ) {
    398 			$this->items[] = wp_get_user_request( $request->ID );
    399 		}
    400 
    401 		$this->items = array_filter( $this->items );
    402 
    403 		$this->set_pagination_args(
    404 			array(
    405 				'total_items' => $requests_query->found_posts,
    406 				'per_page'    => $posts_per_page,
    407 			)
    408 		);
    409 	}
    410 
    411 	/**
    412 	 * Checkbox column.
    413 	 *
    414 	 * @since 4.9.6
    415 	 *
    416 	 * @param WP_User_Request $item Item being shown.
    417 	 * @return string Checkbox column markup.
    418 	 */
    419 	public function column_cb( $item ) {
    420 		return sprintf( '<input type="checkbox" name="request_id[]" value="%1$s" /><span class="spinner"></span>', esc_attr( $item->ID ) );
    421 	}
    422 
    423 	/**
    424 	 * Status column.
    425 	 *
    426 	 * @since 4.9.6
    427 	 *
    428 	 * @param WP_User_Request $item Item being shown.
    429 	 * @return string Status column markup.
    430 	 */
    431 	public function column_status( $item ) {
    432 		$status        = get_post_status( $item->ID );
    433 		$status_object = get_post_status_object( $status );
    434 
    435 		if ( ! $status_object || empty( $status_object->label ) ) {
    436 			return '-';
    437 		}
    438 
    439 		$timestamp = false;
    440 
    441 		switch ( $status ) {
    442 			case 'request-confirmed':
    443 				$timestamp = $item->confirmed_timestamp;
    444 				break;
    445 			case 'request-completed':
    446 				$timestamp = $item->completed_timestamp;
    447 				break;
    448 		}
    449 
    450 		echo '<span class="status-label status-' . esc_attr( $status ) . '">';
    451 		echo esc_html( $status_object->label );
    452 
    453 		if ( $timestamp ) {
    454 			echo ' (' . $this->get_timestamp_as_date( $timestamp ) . ')';
    455 		}
    456 
    457 		echo '</span>';
    458 	}
    459 
    460 	/**
    461 	 * Convert timestamp for display.
    462 	 *
    463 	 * @since 4.9.6
    464 	 *
    465 	 * @param int $timestamp Event timestamp.
    466 	 * @return string Human readable date.
    467 	 */
    468 	protected function get_timestamp_as_date( $timestamp ) {
    469 		if ( empty( $timestamp ) ) {
    470 			return '';
    471 		}
    472 
    473 		$time_diff = time() - $timestamp;
    474 
    475 		if ( $time_diff >= 0 && $time_diff < DAY_IN_SECONDS ) {
    476 			/* translators: %s: Human-readable time difference. */
    477 			return sprintf( __( '%s ago' ), human_time_diff( $timestamp ) );
    478 		}
    479 
    480 		return date_i18n( get_option( 'date_format' ), $timestamp );
    481 	}
    482 
    483 	/**
    484 	 * Default column handler.
    485 	 *
    486 	 * @since 4.9.6
    487 	 * @since 5.7.0 Added `manage_{$this->screen->id}_custom_column` action.
    488 	 *
    489 	 * @param WP_User_Request $item        Item being shown.
    490 	 * @param string          $column_name Name of column being shown.
    491 	 */
    492 	public function column_default( $item, $column_name ) {
    493 		/**
    494 		 * Fires for each custom column of a specific request type in the Requests list table.
    495 		 *
    496 		 * Custom columns are registered using the {@see 'manage_export-personal-data_columns'}
    497 		 * and the {@see 'manage_erase-personal-data_columns'} filters.
    498 		 *
    499 		 * @since 5.7.0
    500 		 *
    501 		 * @param string          $column_name The name of the column to display.
    502 		 * @param WP_User_Request $item        The item being shown.
    503 		 */
    504 		do_action( "manage_{$this->screen->id}_custom_column", $column_name, $item );
    505 	}
    506 
    507 	/**
    508 	 * Created timestamp column. Overridden by children.
    509 	 *
    510 	 * @since 5.7.0
    511 	 *
    512 	 * @param WP_User_Request $item Item being shown.
    513 	 * @return string Human readable date.
    514 	 */
    515 	public function column_created_timestamp( $item ) {
    516 		return $this->get_timestamp_as_date( $item->created_timestamp );
    517 	}
    518 
    519 	/**
    520 	 * Actions column. Overridden by children.
    521 	 *
    522 	 * @since 4.9.6
    523 	 *
    524 	 * @param WP_User_Request $item Item being shown.
    525 	 * @return string Email column markup.
    526 	 */
    527 	public function column_email( $item ) {
    528 		return sprintf( '<a href="%1$s">%2$s</a> %3$s', esc_url( 'mailto:' . $item->email ), $item->email, $this->row_actions( array() ) );
    529 	}
    530 
    531 	/**
    532 	 * Next steps column. Overridden by children.
    533 	 *
    534 	 * @since 4.9.6
    535 	 *
    536 	 * @param WP_User_Request $item Item being shown.
    537 	 */
    538 	public function column_next_steps( $item ) {}
    539 
    540 	/**
    541 	 * Generates content for a single row of the table,
    542 	 *
    543 	 * @since 4.9.6
    544 	 *
    545 	 * @param WP_User_Request $item The current item.
    546 	 */
    547 	public function single_row( $item ) {
    548 		$status = $item->status;
    549 
    550 		echo '<tr id="request-' . esc_attr( $item->ID ) . '" class="status-' . esc_attr( $status ) . '">';
    551 		$this->single_row_columns( $item );
    552 		echo '</tr>';
    553 	}
    554 
    555 	/**
    556 	 * Embed scripts used to perform actions. Overridden by children.
    557 	 *
    558 	 * @since 4.9.6
    559 	 */
    560 	public function embed_scripts() {}
    561 }