- Timestamp:
- 04/10/2018 06:01:20 PM (7 years ago)
- Location:
- trunk/src
- Files:
-
- 5 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-admin/css/forms.css
r42864 r42967 1404 1404 } 1405 1405 } 1406 1407 1408 /* Privacy */ 1409 1410 .privacy_requests .column-email { 1411 width: 40%; 1412 } 1413 .privacy_requests .column-type { 1414 text-align: center; 1415 } 1416 .privacy_requests thead td:first-child, 1417 .privacy_requests tfoot td:first-child { 1418 border-left: 4px solid #fff; 1419 } 1420 .privacy_requests tbody th { 1421 border-left: 4px solid #fff; 1422 background: #fff; 1423 box-shadow: inset 0 -1px 0 rgba(0,0,0,0.1); 1424 } 1425 .privacy_requests tbody td { 1426 background: #fff; 1427 box-shadow: inset 0 -1px 0 rgba(0,0,0,0.1); 1428 } 1429 .privacy_requests .status-request-confirmed th, 1430 .privacy_requests .status-request-confirmed td { 1431 background-color: #f7fcfe; 1432 border-left-color: #00a0d2; 1433 } 1434 .privacy_requests .status-request-failed th, 1435 .privacy_requests .status-request-failed td { 1436 background-color: #fef7f1; 1437 border-left-color: #d64d21; 1438 } 1439 .status-label { 1440 font-weight: bold; 1441 } 1442 .status-label.status-request-pending { 1443 font-weight: normal; 1444 font-style: italic; 1445 color: #6c7781; 1446 } 1447 .status-label.status-request-failed { 1448 color: #aa0000; 1449 font-weight: bold; 1450 } 1451 .wp-privacy-request-form { 1452 clear: both; 1453 } 1454 .wp-privacy-request-form-field { 1455 margin: 1.5em 0; 1456 } 1457 .wp-privacy-request-form label { 1458 font-weight: bold; 1459 line-height: 1.5; 1460 padding-bottom: .5em; 1461 display: block; 1462 } 1463 .wp-privacy-request-form input { 1464 line-height: 1.5; 1465 margin: 0; 1466 } 1467 .email-personal-data::before { 1468 display: inline-block; 1469 font: normal 20px/1 dashicons; 1470 margin: 3px 5px 0 -2px; 1471 speak: none; 1472 -webkit-font-smoothing: antialiased; 1473 -moz-osx-font-smoothing: grayscale; 1474 vertical-align: top; 1475 } 1476 .email-personal-data--sending::before { 1477 color: #f56e28; 1478 content: "\f463"; 1479 -webkit-animation: rotation 2s infinite linear; 1480 animation: rotation 2s infinite linear; 1481 } 1482 .email-personal-data--sent::before { 1483 color: #79ba49; 1484 content: "\f147"; 1485 } 1486 @-webkit-keyframes rotation { 1487 0% { 1488 -webkit-transform: rotate(0deg); 1489 transform: rotate(0deg); 1490 } 1491 100% { 1492 -webkit-transform: rotate(359deg); 1493 transform: rotate(359deg); 1494 } 1495 } 1496 @keyframes rotation { 1497 0% { 1498 -webkit-transform: rotate(0deg); 1499 transform: rotate(0deg); 1500 } 1501 100% { 1502 -webkit-transform: rotate(359deg); 1503 transform: rotate(359deg); 1504 } 1505 } -
trunk/src/wp-admin/includes/admin-filters.php
r42343 r42967 45 45 add_action( 'admin_head', 'wp_site_icon' ); 46 46 add_action( 'admin_head', '_ipad_meta' ); 47 48 // Privacy tools 49 add_action( 'account_action_failed', '_wp_privacy_account_request_failed' ); 50 add_action( 'admin_menu', '_wp_privacy_hook_requests_page' ); 47 51 48 52 // Prerendering. -
trunk/src/wp-admin/includes/user.php
r42875 r42967 580 580 ); 581 581 } 582 583 /** 584 * Get action description from the name. 585 * 586 * @since 5.0.0 587 * @access private 588 * 589 * @return string 590 */ 591 function _wp_privacy_action_description( $request_type ) { 592 switch ( $request_type ) { 593 case 'user_export_request': 594 return __( 'Export Personal Data' ); 595 case 'user_remove_request': 596 return __( 'Remove Personal Data' ); 597 } 598 } 599 600 /** 601 * Log a request and send to the user. 602 * 603 * @since 5.0.0 604 * @access private 605 * 606 * @param string $email_address Email address sending the request to. 607 * @param string $action Action being requested. 608 * @param string $description Description of request. 609 * @return bool|WP_Error depending on success. 610 */ 611 function _wp_privacy_create_request( $email_address, $action, $description ) { 612 $user_id = 0; 613 $user = get_user_by( 'email', $email_address ); 614 615 if ( $user ) { 616 $user_id = $user->ID; 617 } 618 619 $privacy_request_id = wp_insert_post( array( 620 'post_author' => $user_id, 621 'post_status' => 'request-pending', 622 'post_type' => $action, 623 'post_date' => current_time( 'mysql', false ), 624 'post_date_gmt' => current_time( 'mysql', true ), 625 ), true ); 626 627 if ( is_wp_error( $privacy_request_id ) ) { 628 return $privacy_request_id; 629 } 630 631 update_post_meta( $privacy_request_id, '_user_email', $email_address ); 632 update_post_meta( $privacy_request_id, '_action_name', $action ); 633 update_post_meta( $privacy_request_id, '_confirmed_timestamp', false ); 634 635 return wp_send_account_verification_key( $email_address, $action, $description, array( 636 'privacy_request_id' => $privacy_request_id, 637 ) ); 638 } 639 640 /** 641 * Resend an existing request and return the result. 642 * 643 * @since 5.0.0 644 * @access private 645 * 646 * @param int $privacy_request_id Request ID. 647 * @return bool|WP_Error 648 */ 649 function _wp_privacy_resend_request( $privacy_request_id ) { 650 $privacy_request_id = absint( $privacy_request_id ); 651 $privacy_request = get_post( $privacy_request_id ); 652 653 if ( ! $privacy_request || ! in_array( $privacy_request->post_type, _wp_privacy_action_request_types(), true ) ) { 654 return new WP_Error( 'privacy_request_error', __( 'Invalid request.' ) ); 655 } 656 657 $email_address = get_post_meta( $privacy_request_id, '_user_email', true ); 658 $action = get_post_meta( $privacy_request_id, '_action_name', true ); 659 $description = _wp_privacy_action_description( $action ); 660 $result = wp_send_account_verification_key( $email_address, $action, $description, array( 661 'privacy_request_id' => $privacy_request_id, 662 ) ); 663 664 if ( is_wp_error( $result ) ) { 665 return $result; 666 } elseif ( ! $result ) { 667 return new WP_Error( 'privacy_request_error', __( 'Unable to initiate confirmation request.' ) ); 668 } 669 670 wp_update_post( array( 671 'ID' => $privacy_request_id, 672 'post_status' => 'request-pending', 673 'post_date' => current_time( 'mysql', false ), 674 'post_date_gmt' => current_time( 'mysql', true ), 675 ) ); 676 677 return true; 678 } 679 680 /** 681 * Marks a request as completed by the admin and logs the datetime. 682 * 683 * @since 5.0.0 684 * @access private 685 * 686 * @param int $privacy_request_id Request ID. 687 * @return bool|WP_Error 688 */ 689 function _wp_privacy_completed_request( $privacy_request_id ) { 690 $privacy_request_id = absint( $privacy_request_id ); 691 $privacy_request = get_post( $privacy_request_id ); 692 693 if ( ! $privacy_request || ! in_array( $privacy_request->post_type, _wp_privacy_action_request_types(), true ) ) { 694 return new WP_Error( 'privacy_request_error', __( 'Invalid request.' ) ); 695 } 696 697 wp_update_post( array( 698 'ID' => $privacy_request_id, 699 'post_status' => 'request-completed', 700 ) ); 701 702 update_post_meta( $privacy_request_id, '_completed_timestamp', time() ); 703 } 704 705 /** 706 * Handle list table actions. 707 * 708 * @since 5.0.0 709 * @access private 710 */ 711 function _wp_personal_data_handle_actions() { 712 if ( isset( $_POST['export_personal_data_email_retry'] ) ) { // WPCS: input var ok. 713 check_admin_referer( 'bulk-privacy_requests' ); 714 715 $request_id = absint( current( array_keys( (array) wp_unslash( $_POST['export_personal_data_email_retry'] ) ) ) ); // WPCS: input var ok, sanitization ok. 716 $result = _wp_privacy_resend_request( $request_id ); 717 718 if ( is_wp_error( $result ) ) { 719 add_settings_error( 720 'export_personal_data_email_retry', 721 'export_personal_data_email_retry', 722 $result->get_error_message(), 723 'error' 724 ); 725 } else { 726 add_settings_error( 727 'export_personal_data_email_retry', 728 'export_personal_data_email_retry', 729 __( 'Confirmation request re-resent successfully.' ), 730 'updated' 731 ); 732 } 733 734 } elseif ( isset( $_POST['export_personal_data_email_send'] ) ) { // WPCS: input var ok. 735 check_admin_referer( 'bulk-privacy_requests' ); 736 737 $request_id = absint( current( array_keys( (array) wp_unslash( $_POST['export_personal_data_email_send'] ) ) ) ); // WPCS: input var ok, sanitization ok. 738 $result = false; 739 740 /** 741 * TODO: Email the data to the user here. 742 */ 743 744 if ( is_wp_error( $result ) ) { 745 add_settings_error( 746 'export_personal_data_email_send', 747 'export_personal_data_email_send', 748 $result->get_error_message(), 749 'error' 750 ); 751 } else { 752 _wp_privacy_completed_request( $request_id ); 753 add_settings_error( 754 'export_personal_data_email_send', 755 'export_personal_data_email_send', 756 __( 'Personal data was sent to the user successfully.' ), 757 'updated' 758 ); 759 } 760 761 } elseif ( isset( $_POST['action'] ) ) { 762 $action = isset( $_POST['action'] ) ? sanitize_key( wp_unslash( $_POST['action'] ) ) : ''; // WPCS: input var ok, CSRF ok. 763 764 switch ( $action ) { 765 case 'add_export_personal_data_request': 766 case 'add_remove_personal_data_request': 767 check_admin_referer( 'personal-data-request' ); 768 769 if ( ! isset( $_POST['type_of_action'], $_POST['username_or_email_to_export'] ) ) { // WPCS: input var ok. 770 add_settings_error( 771 'action_type', 772 'action_type', 773 __( 'Invalid action.' ), 774 'error' 775 ); 776 } 777 $action_type = sanitize_text_field( wp_unslash( $_POST['type_of_action'] ) ); // WPCS: input var ok. 778 $username_or_email_address = sanitize_text_field( wp_unslash( $_POST['username_or_email_to_export'] ) ); // WPCS: input var ok. 779 $email_address = ''; 780 781 if ( ! in_array( $action_type, _wp_privacy_action_request_types(), true ) ) { 782 add_settings_error( 783 'action_type', 784 'action_type', 785 __( 'Invalid action.' ), 786 'error' 787 ); 788 } 789 790 if ( ! is_email( $username_or_email_address ) ) { 791 $user = get_user_by( 'login', $username_or_email_address ); 792 if ( ! $user instanceof WP_User ) { 793 add_settings_error( 794 'username_or_email_to_export', 795 'username_or_email_to_export', 796 __( 'Unable to add export request. A valid email address or username must be supplied.' ), 797 'error' 798 ); 799 } else { 800 $email_address = $user->user_email; 801 } 802 } else { 803 $email_address = $username_or_email_address; 804 } 805 806 if ( ! empty( $email_address ) ) { 807 $result = _wp_privacy_create_request( $email_address, $action_type, _wp_privacy_action_description( $action_type ) ); 808 809 if ( is_wp_error( $result ) ) { 810 add_settings_error( 811 'username_or_email_to_export', 812 'username_or_email_to_export', 813 $result->get_error_message(), 814 'error' 815 ); 816 } elseif ( ! $result ) { 817 add_settings_error( 818 'username_or_email_to_export', 819 'username_or_email_to_export', 820 __( 'Unable to initiate confirmation request.' ), 821 'error' 822 ); 823 } else { 824 add_settings_error( 825 'username_or_email_to_export', 826 'username_or_email_to_export', 827 __( 'Confirmation request initiated successfully.' ), 828 'updated' 829 ); 830 } 831 } 832 break; 833 } 834 } 835 } 836 837 /** 838 * Personal data export. 839 * 840 * @since 5.0.0 841 * @access private 842 */ 843 function _wp_personal_data_export_page() { 844 if ( ! current_user_can( 'manage_options' ) ) { 845 wp_die( esc_html__( 'Sorry, you are not allowed to manage privacy on this site.' ) ); 846 } 847 848 _wp_personal_data_handle_actions(); 849 850 $requests_table = new WP_Privacy_Data_Export_Requests_Table( array( 851 'plural' => 'privacy_requests', 852 'singular' => 'privacy_request', 853 ) ); 854 $requests_table->process_bulk_action(); 855 $requests_table->prepare_items(); 856 ?> 857 <div class="wrap nosubsub"> 858 <h1><?php esc_html_e( 'Export Personal Data' ); ?></h1> 859 <hr class="wp-header-end" /> 860 861 <?php settings_errors(); ?> 862 863 <form method="post" class="wp-privacy-request-form"> 864 <h2><?php esc_html_e( 'Add Data Export Request' ); ?></h2> 865 <p><?php esc_html_e( 'An email will be sent to the user at this email address asking them to verify the request.' ); ?></p> 866 867 <div class="wp-privacy-request-form-field"> 868 <label for="username_or_email_to_export"><?php esc_html_e( 'Username or email address' ); ?></label> 869 <input type="text" required class="regular-text" id="username_or_email_to_export" name="username_or_email_to_export" /> 870 <?php submit_button( __( 'Send Request' ), 'secondary', 'submit', false ); ?> 871 </div> 872 <?php wp_nonce_field( 'personal-data-request' ); ?> 873 <input type="hidden" name="action" value="add_export_personal_data_request" /> 874 <input type="hidden" name="type_of_action" value="user_export_request" /> 875 </form> 876 <hr /> 877 878 <?php $requests_table->views(); ?> 879 880 <form class="search-form wp-clearfix"> 881 <?php $requests_table->search_box( __( 'Search Requests' ), 'requests' ); ?> 882 <input type="hidden" name="page" value="export_personal_data" /> 883 <input type="hidden" name="filter-status" value="<?php echo isset( $_REQUEST['filter-status'] ) ? esc_attr( sanitize_text_field( $_REQUEST['filter-status'] ) ) : ''; ?>" /> 884 <input type="hidden" name="orderby" value="<?php echo isset( $_REQUEST['orderby'] ) ? esc_attr( sanitize_text_field( $_REQUEST['orderby'] ) ) : ''; ?>" /> 885 <input type="hidden" name="order" value="<?php echo isset( $_REQUEST['order'] ) ? esc_attr( sanitize_text_field( $_REQUEST['order'] ) ) : ''; ?>" /> 886 </form> 887 888 <form method="post"> 889 <?php 890 $requests_table->display(); 891 $requests_table->embed_scripts(); 892 ?> 893 </form> 894 </div> 895 <?php 896 } 897 898 /** 899 * Personal data anonymization. 900 * 901 * @since 5.0.0 902 * @access private 903 */ 904 function _wp_personal_data_removal_page() { 905 if ( ! current_user_can( 'manage_options' ) ) { 906 wp_die( esc_html__( 'Sorry, you are not allowed to manage privacy on this site.' ) ); 907 } 908 909 _wp_personal_data_handle_actions(); 910 911 $requests_table = new WP_Privacy_Data_Removal_Requests_Table( array( 912 'plural' => 'privacy_requests', 913 'singular' => 'privacy_request', 914 ) ); 915 $requests_table->process_bulk_action(); 916 $requests_table->prepare_items(); 917 ?> 918 <div class="wrap nosubsub"> 919 <h1><?php esc_html_e( 'Remove Personal Data' ); ?></h1> 920 <hr class="wp-header-end" /> 921 922 <?php settings_errors(); ?> 923 924 <form method="post" class="wp-privacy-request-form"> 925 <h2><?php esc_html_e( 'Add Data Removal Request' ); ?></h2> 926 <p><?php esc_html_e( 'An email will be sent to the user at this email address asking them to verify the request.' ); ?></p> 927 928 <div class="wp-privacy-request-form-field"> 929 <label for="username_or_email_to_export"><?php esc_html_e( 'Username or email address' ); ?></label> 930 <input type="text" required class="regular-text" id="username_or_email_to_export" name="username_or_email_to_export" /> 931 <?php submit_button( __( 'Send Request' ), 'secondary', 'submit', false ); ?> 932 </div> 933 <?php wp_nonce_field( 'personal-data-request' ); ?> 934 <input type="hidden" name="action" value="add_remove_personal_data_request" /> 935 <input type="hidden" name="type_of_action" value="user_remove_request" /> 936 </form> 937 <hr /> 938 939 <?php $requests_table->views(); ?> 940 941 <form class="search-form wp-clearfix"> 942 <?php $requests_table->search_box( __( 'Search Requests' ), 'requests' ); ?> 943 <input type="hidden" name="page" value="export_personal_data" /> 944 <input type="hidden" name="filter-status" value="<?php echo isset( $_REQUEST['filter-status'] ) ? esc_attr( sanitize_text_field( $_REQUEST['filter-status'] ) ) : ''; ?>" /> 945 <input type="hidden" name="orderby" value="<?php echo isset( $_REQUEST['orderby'] ) ? esc_attr( sanitize_text_field( $_REQUEST['orderby'] ) ) : ''; ?>" /> 946 <input type="hidden" name="order" value="<?php echo isset( $_REQUEST['order'] ) ? esc_attr( sanitize_text_field( $_REQUEST['order'] ) ) : ''; ?>" /> 947 </form> 948 949 <form method="post"> 950 <?php 951 $requests_table->display(); 952 $requests_table->embed_scripts(); 953 ?> 954 </form> 955 </div> 956 <?php 957 } 958 959 /** 960 * Add requests pages. 961 * 962 * @since 5.0.0 963 * @access private 964 */ 965 function _wp_privacy_hook_requests_page() { 966 add_submenu_page( 'tools.php', __( 'Export Personal Data' ), __( 'Export Personal Data' ), 'manage_options', 'export_personal_data', '_wp_personal_data_export_page' ); 967 add_submenu_page( 'tools.php', __( 'Remove Personal Data' ), __( 'Remove Personal Data' ), 'manage_options', 'remove_personal_data', '_wp_personal_data_removal_page' ); 968 } 969 970 // TODO: move the following classes in new files. 971 if ( ! class_exists( 'WP_List_Table' ) ) { 972 require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' ); 973 } 974 975 /** 976 * WP_Privacy_Requests_Table class. 977 */ 978 abstract class WP_Privacy_Requests_Table extends WP_List_Table { 979 980 /** 981 * Action name for the requests this table will work with. Classes 982 * which inherit from WP_Privacy_Requests_Table should define this. 983 * e.g. 'user_export_request' 984 * 985 * @since 5.0.0 986 * 987 * @var string $request_type Name of action. 988 */ 989 protected $request_type = 'INVALID'; 990 991 /** 992 * Get columns to show in the list table. 993 * 994 * @since 5.0.0 995 * 996 * @param array Array of columns. 997 */ 998 public function get_columns() { 999 $columns = array( 1000 'cb' => '<input type="checkbox" />', 1001 'email' => __( 'Requester' ), 1002 'status' => __( 'Status' ), 1003 'requested' => __( 'Requested' ), 1004 'next_steps' => __( 'Next Steps' ), 1005 ); 1006 return $columns; 1007 } 1008 1009 /** 1010 * Get a list of sortable columns. 1011 * 1012 * @since 5.0.0 1013 * 1014 * @return array 1015 */ 1016 protected function get_sortable_columns() { 1017 return array(); 1018 } 1019 1020 /** 1021 * Default primary column. 1022 * 1023 * @since 5.0.0 1024 * 1025 * @return string 1026 */ 1027 protected function get_default_primary_column_name() { 1028 return 'email'; 1029 } 1030 1031 /** 1032 * Get an associative array ( id => link ) with the list 1033 * of views available on this table. 1034 * 1035 * @since 5.0.0 1036 * 1037 * @return array 1038 */ 1039 protected function get_views() { 1040 $current_status = isset( $_REQUEST['filter-status'] ) ? sanitize_text_field( $_REQUEST['filter-status'] ): ''; 1041 $statuses = _wp_privacy_statuses(); 1042 $views = array(); 1043 $admin_url = admin_url( 'tools.php?page=' . $this->request_type ); 1044 $counts = wp_count_posts( $this->request_type ); 1045 1046 $current_link_attributes = empty( $current_status ) ? ' class="current" aria-current="page"' : ''; 1047 $views['all'] = '<a href="' . esc_url( $admin_url ) . "\" $current_link_attributes>" . esc_html__( 'All' ) . ' (' . absint( array_sum( (array) $counts ) ) . ')</a>'; 1048 1049 foreach ( $statuses as $status => $label ) { 1050 $current_link_attributes = $status === $current_status ? ' class="current" aria-current="page"' : ''; 1051 $views[ $status ] = '<a href="' . esc_url( add_query_arg( 'filter-status', $status, $admin_url ) ) . "\" $current_link_attributes>" . esc_html( $label ) . ' (' . absint( $counts->$status ) . ')</a>'; 1052 } 1053 1054 return $views; 1055 } 1056 1057 /** 1058 * Get bulk actions. 1059 * 1060 * @since 5.0.0 1061 * 1062 * @return array 1063 */ 1064 protected function get_bulk_actions() { 1065 return array( 1066 'delete' => __( 'Remove' ), 1067 'resend' => __( 'Resend email' ), 1068 ); 1069 } 1070 1071 /** 1072 * Process bulk actions. 1073 * 1074 * @since 5.0.0 1075 */ 1076 public function process_bulk_action() { 1077 $action = $this->current_action(); 1078 $request_ids = isset( $_REQUEST['request_id'] ) ? wp_parse_id_list( wp_unslash( $_REQUEST['request_id'] ) ) : array(); // WPCS: input var ok, CSRF ok. 1079 1080 if ( $request_ids ) { 1081 check_admin_referer( 'bulk-privacy_requests' ); 1082 } 1083 1084 switch ( $action ) { 1085 case 'delete': 1086 $count = 0; 1087 1088 foreach ( $request_ids as $request_id ) { 1089 if ( wp_delete_post( $request_id, true ) ) { 1090 $count ++; 1091 } 1092 } 1093 1094 add_settings_error( 1095 'bulk_action', 1096 'bulk_action', 1097 sprintf( _n( 'Deleted %d request', 'Deleted %d requests', $count ), $count ), 1098 'updated' 1099 ); 1100 break; 1101 case 'resend': 1102 $count = 0; 1103 1104 foreach ( $request_ids as $request_id ) { 1105 if ( _wp_privacy_resend_request( $request_id ) ) { 1106 $count ++; 1107 } 1108 } 1109 1110 add_settings_error( 1111 'bulk_action', 1112 'bulk_action', 1113 sprintf( _n( 'Re-sent %d request', 'Re-sent %d requests', $count ), $count ), 1114 'updated' 1115 ); 1116 break; 1117 } 1118 } 1119 1120 /** 1121 * Prepare items to output. 1122 * 1123 * @since 5.0.0 1124 */ 1125 public function prepare_items() { 1126 global $wpdb; 1127 1128 $primary = $this->get_primary_column_name(); 1129 $this->_column_headers = array( 1130 $this->get_columns(), 1131 array(), 1132 $this->get_sortable_columns(), 1133 $primary, 1134 ); 1135 1136 $this->items = array(); 1137 $posts_per_page = 20; 1138 $args = array( 1139 'post_type' => $this->request_type, 1140 'posts_per_page' => $posts_per_page, 1141 'offset' => isset( $_REQUEST['paged'] ) ? max( 0, absint( $_REQUEST['paged'] ) - 1 ) * $posts_per_page: 0, 1142 'post_status' => 'any', 1143 ); 1144 1145 if ( ! empty( $_REQUEST['filter-status'] ) ) { 1146 $filter_status = isset( $_REQUEST['filter-status'] ) ? sanitize_text_field( $_REQUEST['filter-status'] ) : ''; 1147 $args['post_status'] = $filter_status; 1148 } 1149 1150 if ( ! empty( $_REQUEST['s'] ) ) { 1151 $args['meta_query'] = array( 1152 $name_query, 1153 'relation' => 'AND', 1154 array( 1155 'key' => '_user_email', 1156 'value' => isset( $_REQUEST['s'] ) ? sanitize_text_field( $_REQUEST['s'] ): '', 1157 'compare' => 'LIKE' 1158 ), 1159 ); 1160 } 1161 1162 $privacy_requests_query = new WP_Query( $args ); 1163 $privacy_requests = $privacy_requests_query->posts; 1164 1165 foreach ( $privacy_requests as $privacy_request ) { 1166 $this->items[] = array( 1167 'request_id' => $privacy_request->ID, 1168 'user_id' => $privacy_request->post_author, 1169 'email' => get_post_meta( $privacy_request->ID, '_user_email', true ), 1170 'action' => get_post_meta( $privacy_request->ID, '_action_name', true ), 1171 'requested' => strtotime( $privacy_request->post_date_gmt ), 1172 'confirmed' => get_post_meta( $privacy_request->ID, '_confirmed_timestamp', true ), 1173 'completed' => get_post_meta( $privacy_request->ID, '_completed_timestamp', true ), 1174 ); 1175 } 1176 1177 $this->set_pagination_args( 1178 array( 1179 'total_items' => $privacy_requests_query->found_posts, 1180 'per_page' => $posts_per_page, 1181 ) 1182 ); 1183 } 1184 1185 /** 1186 * Checkbox column. 1187 * 1188 * @since 5.0.0 1189 * 1190 * @param array $item Item being shown. 1191 * @return string 1192 */ 1193 public function column_cb( $item ) { 1194 return sprintf( '<input type="checkbox" name="request_id[]" value="%1$s" /><span class="spinner"></span>', esc_attr( $item['request_id'] ) ); 1195 } 1196 1197 /** 1198 * Status column. 1199 * 1200 * @since 5.0.0 1201 * 1202 * @param array $item Item being shown. 1203 * @return string 1204 */ 1205 public function column_status( $item ) { 1206 $status = get_post_status( $item['request_id'] ); 1207 $status_object = get_post_status_object( $status ); 1208 1209 if ( ! $status_object || empty( $status_object->label ) ) { 1210 return '-'; 1211 } 1212 1213 $timestamp = false; 1214 1215 switch ( $status ) { 1216 case 'request-confirmed': 1217 $timestamp = $item['confirmed']; 1218 break; 1219 case 'request-completed': 1220 $timestamp = $item['completed']; 1221 break; 1222 } 1223 1224 echo '<span class="status-label status-' . esc_attr( $status ) . '">'; 1225 echo esc_html( $status_object->label ); 1226 1227 if ( $timestamp ) { 1228 echo ' (' . $this->get_timestamp_as_date( $timestamp ) . ')'; 1229 } 1230 1231 echo '</span>'; 1232 } 1233 1234 /** 1235 * Convert timestamp for display. 1236 * 1237 * @since 5.0.0 1238 * 1239 * @param int $timestamp Event timestamp. 1240 * @return string 1241 */ 1242 protected function get_timestamp_as_date( $timestamp ) { 1243 if ( empty( $timestamp ) ) { 1244 return ''; 1245 } 1246 1247 $time_diff = current_time( 'timestamp', true ) - $timestamp; 1248 1249 if ( $time_diff >= 0 && $time_diff < DAY_IN_SECONDS ) { 1250 return sprintf( __( '%s ago' ), human_time_diff( $timestamp ) ); 1251 } 1252 1253 return date_i18n( get_option( 'date_format' ), $timestamp ); 1254 } 1255 1256 /** 1257 * Default column handler. 1258 * 1259 * @since 5.0.0 1260 * 1261 * @param array $item Item being shown. 1262 * @param string $column_name Name of column being shown. 1263 * @return string 1264 */ 1265 public function column_default( $item, $column_name ) { 1266 $cell_value = $item[ $column_name ]; 1267 1268 if ( in_array( $column_name, array( 'requested' ), true ) ) { 1269 return $this->get_timestamp_as_date( $cell_value ); 1270 } 1271 1272 return $cell_value; 1273 } 1274 1275 /** 1276 * Actions column. Overriden by children. 1277 * 1278 * @since 5.0.0 1279 * 1280 * @param array $item Item being shown. 1281 * @return string 1282 */ 1283 public function column_email( $item ) { 1284 return sprintf( '%1$s %2$s', $item['email'], $this->row_actions( array() ) ); 1285 } 1286 1287 /** 1288 * Next steps column. Overriden by children. 1289 * 1290 * @since 5.0.0 1291 * 1292 * @param array $item Item being shown. 1293 */ 1294 public function column_next_steps( $item ) {} 1295 1296 /** 1297 * Generates content for a single row of the table 1298 * 1299 * @since 5.0.0 1300 * 1301 * @param object $item The current item 1302 */ 1303 public function single_row( $item ) { 1304 $status = get_post_status( $item['request_id'] ); 1305 1306 echo '<tr class="status-' . esc_attr( $status ) . '">'; 1307 $this->single_row_columns( $item ); 1308 echo '</tr>'; 1309 } 1310 1311 /** 1312 * Embed scripts used to perform actions. Overriden by children. 1313 * 1314 * @since 5.0.0 1315 */ 1316 public function embed_scripts() {} 1317 } 1318 1319 /** 1320 * WP_Privacy_Data_Export_Requests_Table class. 1321 * 1322 * @since 5.0.0 1323 */ 1324 class WP_Privacy_Data_Export_Requests_Table extends WP_Privacy_Requests_Table { 1325 /** 1326 * Action name for the requests this table will work with. Classes 1327 * which inherit from WP_Privacy_Requests_Table should define this. 1328 * e.g. 'user_export_request' 1329 * 1330 * @since 5.0.0 1331 * 1332 * @var string $request_type Name of action. 1333 */ 1334 protected $request_type = 'user_export_request'; 1335 1336 /** 1337 * Actions column. 1338 * 1339 * @since 5.0.0 1340 * 1341 * @param array $item Item being shown. 1342 * @return string 1343 */ 1344 public function column_email( $item ) { 1345 $row_actions = array( 1346 'download_data' => __( 'Download Personal Data' ), 1347 ); 1348 1349 return sprintf( '%1$s %2$s', $item['email'], $this->row_actions( $row_actions ) ); 1350 } 1351 1352 /** 1353 * Next steps column. 1354 * 1355 * @since 5.0.0 1356 * 1357 * @param array $item Item being shown. 1358 */ 1359 public function column_next_steps( $item ) { 1360 $status = get_post_status( $item['request_id'] ); 1361 1362 switch ( $status ) { 1363 case 'request-pending': 1364 esc_html_e( 'Waiting for confirmation' ); 1365 break; 1366 case 'request-confirmed': 1367 // TODO Complete in follow on patch. 1368 break; 1369 case 'request-failed': 1370 submit_button( __( 'Retry' ), 'secondary', 'export_personal_data_email_retry[' . $item['request_id'] . ']', false ); 1371 break; 1372 case 'request-completed': 1373 echo '<a href="' . esc_url( wp_nonce_url( add_query_arg( array( 1374 'action' => 'delete', 1375 'request_id' => array( $item['request_id'] ) 1376 ), admin_url( 'tools.php?page=export_personal_data' ) ), 'bulk-privacy_requests' ) ) . '">' . esc_html__( 'Remove request' ) . '</a>'; 1377 break; 1378 } 1379 } 1380 } 1381 1382 /** 1383 * WP_Privacy_Data_Removal_Requests_Table class. 1384 * 1385 * @since 5.0.0 1386 */ 1387 class WP_Privacy_Data_Removal_Requests_Table extends WP_Privacy_Requests_Table { 1388 /** 1389 * Action name for the requests this table will work with. Classes 1390 * which inherit from WP_Privacy_Requests_Table should define this. 1391 * e.g. 'user_remove_request' 1392 * 1393 * @since 5.0.0 1394 * 1395 * @var string $request_type Name of action. 1396 */ 1397 protected $request_type = 'user_remove_request'; 1398 1399 /** 1400 * Actions column. 1401 * 1402 * @since 5.0.0 1403 * 1404 * @param array $item Item being shown. 1405 * @return string 1406 */ 1407 public function column_email( $item ) { 1408 $row_actions = array( 1409 // TODO Complete in follow on patch. 1410 'remove_data' => __( 'Remove Personal Data' ), 1411 ); 1412 1413 // If we have a user ID, include a delete user action. 1414 if ( ! empty( $item['user_id'] ) ) { 1415 // TODO Complete in follow on patch. 1416 $row_actions['delete_user'] = __( 'Delete User' ); 1417 } 1418 1419 return sprintf( '%1$s %2$s', $item['email'], $this->row_actions( $row_actions ) ); 1420 } 1421 1422 /** 1423 * Next steps column. 1424 * 1425 * @since 5.0.0 1426 * 1427 * @param array $item Item being shown. 1428 */ 1429 public function column_next_steps( $item ) { 1430 } 1431 1432 } -
trunk/src/wp-includes/post.php
r42876 r42967 227 227 ); 228 228 229 register_post_type( 230 'user_export_request', array( 231 'labels' => array( 232 'name' => __( 'Export Personal Data Requests' ), 233 'singular_name' => __( 'Export Personal Data Request' ), 234 ), 235 'public' => false, 236 '_builtin' => true, /* internal use only. don't use this when registering your own post type. */ 237 'hierarchical' => false, 238 'rewrite' => false, 239 'query_var' => false, 240 'can_export' => false, 241 'delete_with_user' => false, 242 ) 243 ); 244 245 register_post_type( 246 'user_remove_request', array( 247 'labels' => array( 248 'name' => __( 'Remove Personal Data Requests' ), 249 'singular_name' => __( 'Remove Personal Data Request' ), 250 ), 251 'public' => false, 252 '_builtin' => true, /* internal use only. don't use this when registering your own post type. */ 253 'hierarchical' => false, 254 'rewrite' => false, 255 'query_var' => false, 256 'can_export' => false, 257 'delete_with_user' => false, 258 ) 259 ); 260 229 261 register_post_status( 230 262 'publish', array( … … 293 325 'inherit', array( 294 326 'label' => 'inherit', 327 'internal' => true, 328 '_builtin' => true, /* internal use only. */ 329 'exclude_from_search' => false, 330 ) 331 ); 332 333 register_post_status( 334 'request-pending', array( 335 'label' => _x( 'Pending', 'request status' ), 336 'internal' => true, 337 '_builtin' => true, /* internal use only. */ 338 'exclude_from_search' => false, 339 ) 340 ); 341 342 register_post_status( 343 'request-confirmed', array( 344 'label' => _x( 'Confirmed', 'request status' ), 345 'internal' => true, 346 '_builtin' => true, /* internal use only. */ 347 'exclude_from_search' => false, 348 ) 349 ); 350 351 register_post_status( 352 'request-failed', array( 353 'label' => _x( 'Failed', 'request status' ), 354 'internal' => true, 355 '_builtin' => true, /* internal use only. */ 356 'exclude_from_search' => false, 357 ) 358 ); 359 360 register_post_status( 361 'request-completed', array( 362 'label' => _x( 'Completed', 'request status' ), 295 363 'internal' => true, 296 364 '_builtin' => true, /* internal use only. */ … … 781 849 782 850 return $status; 851 } 852 853 /** 854 * Return statuses for privacy requests. 855 * 856 * @since 5.0.0 857 * 858 * @return array 859 */ 860 function _wp_privacy_statuses() { 861 return array( 862 'request-pending' => __( 'Pending' ), // Pending confirmation from user. 863 'request-confirmed' => __( 'Confirmed' ), // User has confirmed the action. 864 'request-failed' => __( 'Failed' ), // User failed to confirm the action. 865 'request-completed' => __( 'Completed' ), // Admin has handled the request. 866 ); 783 867 } 784 868 -
trunk/src/wp-includes/user.php
r42964 r42967 2812 2812 2813 2813 /** 2814 * Get all user privacy request types. 2815 * 2816 * @since 5.0.0 2817 * @access private 2818 * 2819 * @return array 2820 */ 2821 function _wp_privacy_action_request_types() { 2822 return array( 2823 'user_export_request', 2824 'user_remove_request', 2825 ); 2826 } 2827 2828 /** 2829 * Update log when privacy request is confirmed. 2830 * 2831 * @since 5.0.0 2832 * @access private 2833 * 2834 * @param array $result Result of the request from the user. 2835 */ 2836 function _wp_privacy_account_request_confirmed( $result ) { 2837 if ( isset( $result['action'], $result['request_data'], $result['request_data']['privacy_request_id'] ) && in_array( $result['action'], _wp_privacy_action_request_types(), true ) ) { 2838 $privacy_request_id = absint( $result['request_data']['privacy_request_id'] ); 2839 $privacy_request = get_post( $privacy_request_id ); 2840 2841 if ( ! $privacy_request || ! in_array( $privacy_request->post_type, _wp_privacy_action_request_types(), true ) ) { 2842 return; 2843 } 2844 2845 update_post_meta( $privacy_request_id, '_confirmed_timestamp', time() ); 2846 wp_update_post( array( 2847 'ID' => $privacy_request_id, 2848 'post_status' => 'request-confirmed', 2849 ) ); 2850 } 2851 } 2852 add_action( 'account_action_confirmed', '_wp_privacy_account_request_confirmed' ); 2853 2854 /** 2855 * Update log when privacy request fails. 2856 * 2857 * @since 5.0.0 2858 * @access private 2859 * 2860 * @param array $result Result of the request from the user. 2861 */ 2862 function _wp_privacy_account_request_failed( $result ) { 2863 if ( isset( $result['action'], $result['request_data'], $result['request_data']['privacy_request_id'] ) && 2864 in_array( $result['action'], _wp_privacy_action_request_types(), true ) ) { 2865 2866 $privacy_request_id = absint( $result['request_data']['privacy_request_id'] ); 2867 $privacy_request = get_post( $privacy_request_id ); 2868 2869 if ( ! $privacy_request || ! in_array( $privacy_request->post_type, _wp_privacy_action_request_types(), true ) ) { 2870 return; 2871 } 2872 2873 wp_update_post( array( 2874 'ID' => $privacy_request_id, 2875 'post_status' => 'request-failed', 2876 ) ); 2877 } 2878 } 2879 2880 /** 2814 2881 * Send a confirmation request email to confirm an action. 2815 2882 * 2816 2883 * @since 5.0.0 2817 2884 * 2818 * @param string $email User email address. This can be the address of a registered or non-registered user. Defaults to logged in user email address. 2885 * @param string $email User email address. This can be the address of a registered or non-registered user. Defaults to logged in user email address. 2819 2886 * @param string $action_name Name of the action that is being confirmed. Defaults to 'confirm_email'. 2820 2887 * @param string $action_description User facing description of the action they will be confirming. Defaults to "confirm your email address". … … 2918 2985 * 2919 2986 * @since 5.0.0 2920 * 2987 * 2921 2988 * @param string $email_text Text in the email. 2922 2989 * @param array $email_data { … … 3040 3107 $email = $user->user_email; 3041 3108 3042 if ( false !== strpos( $ confirm_action_data, ':' ) ) {3043 list( $key_request_time, $saved_key ) = explode( ':', $ confirm_action_data, 2 );3109 if ( false !== strpos( $raw_data, ':' ) ) { 3110 list( $key_request_time, $saved_key ) = explode( ':', $raw_data, 2 ); 3044 3111 } 3045 3112 } else { 3046 3113 $raw_data = get_site_option( '_verify_action_' . $action_name . '_' . $uid, '' ); 3047 3114 3048 if ( false !== strpos( $ confirm_action_data, ':' ) ) {3049 list( $key_request_time, $saved_key, $email ) = explode( ':', $ confirm_action_data, 3 );3115 if ( false !== strpos( $raw_data, ':' ) ) { 3116 list( $key_request_time, $saved_key, $email ) = explode( ':', $raw_data, 3 ); 3050 3117 } 3051 3118 } … … 3069 3136 * 3070 3137 * @since 5.0.0 3071 * 3138 * 3072 3139 * @param int $expiration The expiration time in seconds. 3073 3140 */
Note: See TracChangeset
for help on using the changeset viewer.