PageRenderTime 44ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/wp-admin/includes/class-wp-privacy-requests-table.php

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