Changeset 43008
- Timestamp:
- 04/27/2018 10:12:01 AM (7 years ago)
- Location:
- trunk/src
- Files:
-
- 7 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-admin/includes/admin-filters.php
r42980 r43008 47 47 48 48 // Privacy tools 49 add_action( 'account_action_failed', '_wp_privacy_account_request_failed' );50 49 add_action( 'admin_menu', '_wp_privacy_hook_requests_page' ); 51 50 -
trunk/src/wp-admin/includes/ajax-actions.php
r42986 r43008 4465 4465 // Find the request CPT 4466 4466 $request = get_post( $request_id ); 4467 if ( ' user_remove_request' !== $request->post_type ) {4467 if ( 'remove_personal_data' !== $request->post_title ) { 4468 4468 wp_send_json_error( __( 'Error: Invalid request ID.' ) ); 4469 4469 } 4470 4470 4471 $email_address = get_post_meta( $request_id, '_ user_email', true );4471 $email_address = get_post_meta( $request_id, '_wp_user_request_user_email', true ); 4472 4472 4473 4473 if ( ! is_email( $email_address ) ) { -
trunk/src/wp-admin/includes/user.php
r43000 r43008 582 582 583 583 /** 584 * Get action description from the name.584 * Resend an existing request and return the result. 585 585 * 586 586 * @since 4.9.6 587 587 * @access private 588 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 4.9.6 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 4.9.6 644 * @access private 645 * 646 * @param int $privacy_request_id Request ID. 589 * @param int $request_id Request ID. 647 590 * @return bool|WP_Error 648 591 */ 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 )) {592 function _wp_privacy_resend_request( $request_id ) { 593 $request_id = absint( $request_id ); 594 $request = get_post( $request_id ); 595 596 if ( ! $request || 'user_request' !== $request->post_type ) { 654 597 return new WP_Error( 'privacy_request_error', __( 'Invalid request.' ) ); 655 598 } 656 599 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 ) ); 600 $result = wp_send_user_request( $request_id ); 663 601 664 602 if ( is_wp_error( $result ) ) { … … 668 606 } 669 607 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 608 return true; 678 609 } … … 684 615 * @access private 685 616 * 686 * @param int $ privacy_request_id Request ID.687 * @return bool|WP_Error688 */ 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 )) {617 * @param int $request_id Request ID. 618 * @return int|WP_Error Request ID on succes or WP_Error. 619 */ 620 function _wp_privacy_completed_request( $request_id ) { 621 $request_id = absint( $request_id ); 622 $request_data = wp_get_user_request_data( $request_id ); 623 624 if ( ! $request_data ) { 694 625 return new WP_Error( 'privacy_request_error', __( 'Invalid request.' ) ); 695 626 } 696 627 697 wp_update_post( array( 698 'ID' => $privacy_request_id, 699 'post_status' => 'request-completed', 628 update_post_meta( $request_id, '_wp_user_request_confirmed_timestamp', time() ); 629 $request = wp_update_post( array( 630 'ID' => $request_data['request_id'], 631 'post_status' => 'request-confirmed', 700 632 ) ); 701 702 update_post_meta( $privacy_request_id, '_completed_timestamp', time() ); 633 return $request; 703 634 } 704 635 … … 804 735 } 805 736 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 } 737 if ( empty( $email_address ) ) { 738 break; 831 739 } 740 741 $request_id = wp_create_user_request( $email_address, $action_type ); 742 743 if ( is_wp_error( $request_id ) ) { 744 add_settings_error( 745 'username_or_email_to_export', 746 'username_or_email_to_export', 747 $request_id->get_error_message(), 748 'error' 749 ); 750 break; 751 } elseif ( ! $request_id ) { 752 add_settings_error( 753 'username_or_email_to_export', 754 'username_or_email_to_export', 755 __( 'Unable to initiate confirmation request.' ), 756 'error' 757 ); 758 break; 759 } 760 761 wp_send_user_request( $request_id ); 762 763 add_settings_error( 764 'username_or_email_to_export', 765 'username_or_email_to_export', 766 __( 'Confirmation request initiated successfully.' ), 767 'updated' 768 ); 832 769 break; 833 770 } … … 872 809 <?php wp_nonce_field( 'personal-data-request' ); ?> 873 810 <input type="hidden" name="action" value="add_export_personal_data_request" /> 874 <input type="hidden" name="type_of_action" value=" user_export_request" />811 <input type="hidden" name="type_of_action" value="export_personal_data" /> 875 812 </form> 876 813 <hr /> … … 938 875 <?php wp_nonce_field( 'personal-data-request' ); ?> 939 876 <input type="hidden" name="action" value="add_remove_personal_data_request" /> 940 <input type="hidden" name="type_of_action" value=" user_remove_request" />877 <input type="hidden" name="type_of_action" value="remove_personal_data" /> 941 878 </form> 942 879 <hr /> … … 1012 949 public function get_columns() { 1013 950 $columns = array( 1014 'cb' => '<input type="checkbox" />',1015 'email' => __( 'Requester' ),1016 'status' => __( 'Status' ),1017 'requested '=> __( 'Requested' ),1018 'next_steps' => __( 'Next Steps' ),951 'cb' => '<input type="checkbox" />', 952 'email' => __( 'Requester' ), 953 'status' => __( 'Status' ), 954 'requested_timestamp' => __( 'Requested' ), 955 'next_steps' => __( 'Next Steps' ), 1019 956 ); 1020 957 return $columns; … … 1041 978 protected function get_default_primary_column_name() { 1042 979 return 'email'; 980 } 981 982 /** 983 * Count number of requests for each status. 984 * 985 * @since 4.9.6 986 * 987 * @return object Number of posts for each status. 988 */ 989 protected function get_request_counts() { 990 global $wpdb; 991 992 $cache_key = $this->post_type . '-' . $this->request_type; 993 $counts = wp_cache_get( $cache_key, 'counts' ); 994 995 if ( false !== $counts ) { 996 return $counts; 997 } 998 999 $query = " 1000 SELECT post_status, COUNT( * ) AS num_posts 1001 FROM {$wpdb->posts} 1002 WHERE post_type = %s 1003 AND post_title = %s 1004 GROUP BY post_status"; 1005 1006 $results = (array) $wpdb->get_results( $wpdb->prepare( $query, $this->post_type, $this->request_type ), ARRAY_A ); 1007 $counts = array_fill_keys( get_post_stati(), 0 ); 1008 1009 foreach ( $results as $row ) { 1010 $counts[ $row['post_status'] ] = $row['num_posts']; 1011 } 1012 1013 $counts = (object) $counts; 1014 wp_cache_set( $cache_key, $counts, 'counts' ); 1015 1016 return $counts; 1043 1017 } 1044 1018 … … 1056 1030 $views = array(); 1057 1031 $admin_url = admin_url( 'tools.php?page=' . $this->request_type ); 1058 $counts = wp_count_posts( $this->post_type);1032 $counts = $this->get_request_counts(); 1059 1033 1060 1034 $current_link_attributes = empty( $current_status ) ? ' class="current" aria-current="page"' : ''; … … 1091 1065 $action = $this->current_action(); 1092 1066 $request_ids = isset( $_REQUEST['request_id'] ) ? wp_parse_id_list( wp_unslash( $_REQUEST['request_id'] ) ) : array(); // WPCS: input var ok, CSRF ok. 1067 $count = 0; 1093 1068 1094 1069 if ( $request_ids ) { … … 1098 1073 switch ( $action ) { 1099 1074 case 'delete': 1100 $count = 0;1101 1102 1075 foreach ( $request_ids as $request_id ) { 1103 1076 if ( wp_delete_post( $request_id, true ) ) { … … 1114 1087 break; 1115 1088 case 'resend': 1116 $count = 0;1117 1118 1089 foreach ( $request_ids as $request_id ) { 1119 if ( _wp_privacy_resend_request( $request_id ) ) { 1120 $count ++; 1090 $resend = _wp_privacy_resend_request( $request_id ); 1091 1092 if ( $resend && ! is_wp_error( $resend ) ) { 1093 $count++; 1121 1094 } 1122 1095 } … … 1152 1125 $args = array( 1153 1126 'post_type' => $this->post_type, 1127 'title' => $this->request_type, 1154 1128 'posts_per_page' => $posts_per_page, 1155 1129 'offset' => isset( $_REQUEST['paged'] ) ? max( 0, absint( $_REQUEST['paged'] ) - 1 ) * $posts_per_page: 0, … … 1167 1141 'relation' => 'AND', 1168 1142 array( 1169 'key' => '_ user_email',1143 'key' => '_wp_user_request_user_email', 1170 1144 'value' => isset( $_REQUEST['s'] ) ? sanitize_text_field( $_REQUEST['s'] ): '', 1171 'compare' => 'LIKE' 1145 'compare' => 'LIKE', 1172 1146 ), 1173 1147 ); 1174 1148 } 1175 1149 1176 $privacy_requests_query = new WP_Query( $args ); 1177 $privacy_requests = $privacy_requests_query->posts; 1178 1179 foreach ( $privacy_requests as $privacy_request ) { 1180 $this->items[] = array( 1181 'request_id' => $privacy_request->ID, 1182 'user_id' => $privacy_request->post_author, 1183 'email' => get_post_meta( $privacy_request->ID, '_user_email', true ), 1184 'action' => get_post_meta( $privacy_request->ID, '_action_name', true ), 1185 'requested' => strtotime( $privacy_request->post_date_gmt ), 1186 'confirmed' => get_post_meta( $privacy_request->ID, '_confirmed_timestamp', true ), 1187 'completed' => get_post_meta( $privacy_request->ID, '_completed_timestamp', true ), 1188 ); 1150 $requests_query = new WP_Query( $args ); 1151 $requests = $requests_query->posts; 1152 1153 foreach ( $requests as $request ) { 1154 $this->items[] = wp_get_user_request_data( $request->ID ); 1189 1155 } 1190 1156 1191 1157 $this->set_pagination_args( 1192 1158 array( 1193 'total_items' => $ privacy_requests_query->found_posts,1159 'total_items' => $requests_query->found_posts, 1194 1160 'per_page' => $posts_per_page, 1195 1161 ) … … 1229 1195 switch ( $status ) { 1230 1196 case 'request-confirmed': 1231 $timestamp = $item['confirmed '];1197 $timestamp = $item['confirmed_timestamp']; 1232 1198 break; 1233 1199 case 'request-completed': 1234 $timestamp = $item['completed '];1200 $timestamp = $item['completed_timestamp']; 1235 1201 break; 1236 1202 } … … 1280 1246 $cell_value = $item[ $column_name ]; 1281 1247 1282 if ( in_array( $column_name, array( 'requested ' ), true ) ) {1248 if ( in_array( $column_name, array( 'requested_timestamp' ), true ) ) { 1283 1249 return $this->get_timestamp_as_date( $cell_value ); 1284 1250 } … … 1353 1319 * @var string $post_type The post type. 1354 1320 */ 1355 protected $post_type = 'user_ export_request';1321 protected $post_type = 'user_request'; 1356 1322 1357 1323 /** … … 1438 1404 * @var string $post_type The post type. 1439 1405 */ 1440 protected $post_type = 'user_re move_request';1406 protected $post_type = 'user_request'; 1441 1407 1442 1408 /** -
trunk/src/wp-includes/default-filters.php
r42994 r43008 329 329 add_action( 'do_robots', 'do_robots' ); 330 330 add_action( 'set_comment_cookies', 'wp_set_comment_cookies', 10, 3 ); 331 add_filter( 'wp_privacy_personal_data_exporters', 'wp_register_comment_personal_data_exporter', 10 );332 add_filter( 'wp_privacy_personal_data_erasers', 'wp_register_comment_personal_data_eraser', 10 );333 331 add_action( 'sanitize_comment_cookies', 'sanitize_comment_cookies' ); 334 332 add_action( 'admin_print_scripts', 'print_emoji_detection_script' ); … … 350 348 add_action( 'welcome_panel', 'wp_welcome_panel' ); 351 349 350 // Privacy 351 add_action( 'user_request_action_confirmed', '_wp_privacy_account_request_confirmed' ); 352 add_filter( 'user_request_action_confirmed_message', '_wp_privacy_account_request_confirmed_message', 10, 2 ); 353 add_filter( 'wp_privacy_personal_data_exporters', 'wp_register_comment_personal_data_exporter' ); 354 add_filter( 'wp_privacy_personal_data_erasers', 'wp_register_comment_personal_data_eraser' ); 355 352 356 // Cron tasks 353 357 add_action( 'wp_scheduled_delete', 'wp_scheduled_delete' ); -
trunk/src/wp-includes/post.php
r42967 r43008 228 228 229 229 register_post_type( 230 'user_ export_request', array(230 'user_request', array( 231 231 'labels' => array( 232 'name' => __( ' Export Personal DataRequests' ),233 'singular_name' => __( ' Export Personal DataRequest' ),232 'name' => __( 'User Requests' ), 233 'singular_name' => __( 'User Request' ), 234 234 ), 235 235 'public' => false, … … 240 240 'can_export' => false, 241 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, 242 'supports' => array(), 258 243 ) 259 244 ); -
trunk/src/wp-includes/user.php
r42967 r43008 2814 2814 * Get all user privacy request types. 2815 2815 * 2816 * @since 5.0.02816 * @since 4.9.6 2817 2817 * @access private 2818 2818 * … … 2821 2821 function _wp_privacy_action_request_types() { 2822 2822 return array( 2823 ' user_export_request',2824 ' user_remove_request',2823 'export_personal_data', 2824 'remove_personal_data', 2825 2825 ); 2826 2826 } … … 2829 2829 * Update log when privacy request is confirmed. 2830 2830 * 2831 * @since 5.0.02831 * @since 4.9.6 2832 2832 * @access private 2833 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() ); 2834 * @param int $request_id ID of the request. 2835 */ 2836 function _wp_privacy_account_request_confirmed( $request_id ) { 2837 $request_data = wp_get_user_request_data( $request_id ); 2838 2839 if ( ! $request_data ) { 2840 return; 2841 } 2842 2843 if ( ! in_array( $request_data['status'], array( 'request-pending', 'request-failed' ), true ) ) { 2844 return; 2845 } 2846 2847 update_post_meta( $request_id, '_wp_user_request_confirmed_timestamp', time() ); 2848 wp_update_post( array( 2849 'ID' => $request_data['request_id'], 2850 'post_status' => 'request-confirmed', 2851 ) ); 2852 } 2853 2854 /** 2855 * Return request confirmation message HTML. 2856 * 2857 * @since 4.9.6 2858 * @access private 2859 * 2860 * @return string $message The confirmation message. 2861 */ 2862 function _wp_privacy_account_request_confirmed_message( $message, $request_id ) { 2863 $request = wp_get_user_request_data( $request_id ); 2864 2865 if ( $request && in_array( $request['action'], _wp_privacy_action_request_types(), true ) ) { 2866 $message = '<p class="message">' . __( 'Action has been confirmed.' ) . '</p>'; 2867 $message .= __( 'The site administrator has been notified and will fulfill your request as soon as possible.' ); 2868 } 2869 2870 return $message; 2871 } 2872 2873 /** 2874 * Create and log a user request to perform a specific action. 2875 * 2876 * Requests are stored inside a post type named `user_request` since they can apply to both 2877 * users on the site, or guests without a user account. 2878 * 2879 * @since 4.9.6 2880 * 2881 * @param string $email_address User email address. This can be the address of a registered or non-registered user. 2882 * @param string $action_name Name of the action that is being confirmed. Required. 2883 * @param array $request_data Misc data you want to send with the verification request and pass to the actions once the request is confirmed. 2884 * @return int|WP_Error Returns the request ID if successful, or a WP_Error object on failure. 2885 */ 2886 function wp_create_user_request( $email_address = '', $action_name = '', $request_data = array() ) { 2887 $email_address = sanitize_email( $email_address ); 2888 $action_name = sanitize_key( $action_name ); 2889 2890 if ( ! is_email( $email_address ) ) { 2891 return new WP_Error( 'invalid_email', __( 'Invalid email address' ) ); 2892 } 2893 2894 if ( ! $action_name ) { 2895 return new WP_Error( 'invalid_action', __( 'Invalid action name' ) ); 2896 } 2897 2898 $user = get_user_by( 'email', $email_address ); 2899 $user_id = $user && ! is_wp_error( $user ) ? $user->ID: 0; 2900 2901 // Check for duplicates. 2902 $requests_query = new WP_Query( array( 2903 'post_type' => 'user_request', 2904 'title' => $action_name, 2905 'post_status' => 'any', 2906 'fields' => 'ids', 2907 'meta_query' => array( 2908 array( 2909 'key' => '_wp_user_request_user_email', 2910 'value' => $email_address, 2911 ), 2912 ), 2913 ) ); 2914 2915 if ( $requests_query->found_posts ) { 2916 return new WP_Error( 'duplicate_request', __( 'A request for this email address already exists.' ) ); 2917 } 2918 2919 $request_id = wp_insert_post( array( 2920 'post_author' => $user_id, 2921 'post_title' => $action_name, 2922 'post_content' => wp_json_encode( $request_data ), 2923 'post_status' => 'request-pending', 2924 'post_type' => 'user_request', 2925 'post_date' => current_time( 'mysql', false ), 2926 'post_date_gmt' => current_time( 'mysql', true ), 2927 ), true ); 2928 2929 if ( is_wp_error( $request_id ) ) { 2930 return $request_id; 2931 } 2932 2933 update_post_meta( $request_id, '_wp_user_request_user_email', $email_address ); 2934 update_post_meta( $request_id, '_wp_user_request_confirmed_timestamp', false ); 2935 2936 return $request_id; 2937 } 2938 2939 /** 2940 * Get action description from the name and return a string. 2941 * 2942 * @since 4.9.6 2943 * 2944 * @param string $action_name Action name of the request. 2945 * @return string 2946 */ 2947 function wp_user_request_action_description( $action_name ) { 2948 switch ( $action_name ) { 2949 case 'export_personal_data': 2950 $description = __( 'Export Personal Data' ); 2951 break; 2952 case 'remove_personal_data': 2953 $description = __( 'Remove Personal Data' ); 2954 break; 2955 default: 2956 /* translators: %s: action name */ 2957 $description = sprintf( __( 'Confirm the "%s" action' ), $action_name ); 2958 break; 2959 } 2960 2961 /** 2962 * Filters the user action description. 2963 * 2964 * @param string $description The default description. 2965 * @param string $action_name The name of the request. 2966 */ 2967 return apply_filters( 'user_request_action_description', $description, $action_name ); 2968 } 2969 2970 /** 2971 * Send a confirmation request email to confirm an action. 2972 * 2973 * If the request is not already pending, it will be updated. 2974 * 2975 * @since 4.9.6 2976 * 2977 * @param string $request_id ID of the request created via wp_create_user_request(). 2978 * @return WP_Error|bool Will return true/false based on the success of sending the email, or a WP_Error object. 2979 */ 2980 function wp_send_user_request( $request_id ) { 2981 $request_id = absint( $request_id ); 2982 $request = get_post( $request_id ); 2983 2984 if ( ! $request || 'user_request' !== $request->post_type ) { 2985 return new WP_Error( 'user_request_error', __( 'Invalid request.' ) ); 2986 } 2987 2988 if ( 'request-pending' !== $request->post_status ) { 2846 2989 wp_update_post( array( 2847 'ID' => $privacy_request_id, 2848 'post_status' => 'request-confirmed', 2990 'ID' => $request_id, 2991 'post_status' => 'request-pending', 2992 'post_date' => current_time( 'mysql', false ), 2993 'post_date_gmt' => current_time( 'mysql', true ), 2849 2994 ) ); 2850 2995 } 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 /** 2881 * Send a confirmation request email to confirm an action. 2882 * 2883 * @since 5.0.0 2884 * 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. 2886 * @param string $action_name Name of the action that is being confirmed. Defaults to 'confirm_email'. 2887 * @param string $action_description User facing description of the action they will be confirming. Defaults to "confirm your email address". 2888 * @param array $request_data Misc data you want to send with the verification request and pass to the actions once the request is confirmed. 2889 * @return WP_Error|bool Will return true/false based on the success of sending the email, or a WP_Error object. 2890 */ 2891 function wp_send_account_verification_key( $email = '', $action_name = '', $action_description = '', $request_data = array() ) { 2892 if ( ! function_exists( 'wp_get_current_user' ) ) { 2893 return new WP_Error( 'invalid', __( 'This function cannot be used before init.' ) ); 2894 } 2895 2896 $action_name = sanitize_key( $action_name ); 2897 $action_description = wp_kses_post( $action_description ); 2898 2899 if ( empty( $action_name ) ) { 2900 $action_name = 'confirm_email'; 2901 } 2902 2903 if ( empty( $action_description ) ) { 2904 $action_description = __( 'Confirm your email address.' ); 2905 } 2906 2907 if ( empty( $email ) ) { 2908 $user = wp_get_current_user(); 2909 $email = $user->ID ? $user->user_email : ''; 2910 } else { 2911 $user = false; 2912 } 2913 2914 $email = sanitize_email( $email ); 2915 2916 if ( ! is_email( $email ) ) { 2917 return new WP_Error( 'invalid_email', __( 'Invalid email address' ) ); 2918 } 2919 2920 if ( ! $user ) { 2921 $user = get_user_by( 'email', $email ); 2922 } 2923 2924 $confirm_key = wp_get_account_verification_key( $email, $action_name, $request_data ); 2925 2926 if ( is_wp_error( $confirm_key ) ) { 2927 return $confirm_key; 2928 } 2929 2930 // We could be dealing with a registered user account, or a visitor. 2931 $is_registered_user = $user && ! is_wp_error( $user ); 2932 2933 if ( $is_registered_user ) { 2934 $uid = $user->ID; 2935 } else { 2936 // Generate a UID for this email address so we don't send the actual email in the query string. Hash is not supported on all systems. 2937 $uid = function_exists( 'hash' ) ? hash( 'sha256', $email ) : sha1( $email ); 2938 } 2996 2997 $email_data = array( 2998 'action_name' => $request->post_title, 2999 'email' => get_post_meta( $request->ID, '_wp_user_request_user_email', true ), 3000 'description' => wp_user_request_action_description( $request->post_title ), 3001 'confirm_url' => add_query_arg( array( 3002 'action' => 'confirmaction', 3003 'request_id' => $request_id, 3004 'confirm_key' => wp_generate_user_request_key( $request_id ), 3005 ), site_url( 'wp-login.php' ) ), 3006 'sitename' => is_multisite() ? get_site_option( 'site_name' ) : get_option( 'blogname' ), 3007 'siteurl' => network_home_url(), 3008 ); 2939 3009 2940 3010 /* translators: Do not translate DESCRIPTION, CONFIRM_URL, EMAIL, SITENAME, SITEURL: those are placeholders. */ … … 2957 3027 All at ###SITENAME### 2958 3028 ###SITEURL###' 2959 );2960 2961 $email_data = array(2962 'action_name' => $action_name,2963 'email' => $email,2964 'description' => $action_description,2965 'confirm_url' => add_query_arg( array(2966 'action' => 'verifyaccount',2967 'confirm_action' => $action_name,2968 'uid' => $uid,2969 'confirm_key' => $confirm_key,2970 ), site_url( 'wp-login.php' ) ),2971 'sitename' => is_multisite() ? get_site_option( 'site_name' ) : get_option( 'blogname' ),2972 'siteurl' => network_home_url(),2973 3029 ); 2974 3030 … … 2984 3040 * ###SITEURL### The URL to the site. 2985 3041 * 2986 * @since 5.0.03042 * @since 4.9.6 2987 3043 * 2988 3044 * @param string $email_text Text in the email. … … 2998 3054 * } 2999 3055 */ 3000 $content = apply_filters( ' account_verification_email_content', $email_text, $email_data );3056 $content = apply_filters( 'user_request_action_email_content', $email_text, $email_data ); 3001 3057 3002 3058 $content = str_replace( '###DESCRIPTION###', $email_data['description'], $content ); … … 3011 3067 3012 3068 /** 3013 * Creates, stores, then returns a confirmation key for an account action. 3014 * 3015 * @since 5.0.0 3016 * 3017 * @param string $email User email address. This can be the address of a registered or non-registered user. 3018 * @param string $action_name Name of the action this key is being generated for. 3019 * @param array $request_data Misc data you want to send with the verification request and pass to the actions once the request is confirmed. 3020 * @return string|WP_Error Confirmation key on success. WP_Error on error. 3021 */ 3022 function wp_get_account_verification_key( $email, $action_name, $request_data = array() ) { 3069 * Returns a confirmation key for a user action and stores the hashed version. 3070 * 3071 * @since 4.9.6 3072 * 3073 * @param int $request_id Request ID. 3074 * @return string Confirmation key. 3075 */ 3076 function wp_generate_user_request_key( $request_id ) { 3023 3077 global $wp_hasher; 3024 3025 if ( ! is_email( $email ) ) {3026 return new WP_Error( 'invalid_email', __( 'Invalid email address' ) );3027 }3028 3029 if ( empty( $action_name ) ) {3030 return new WP_Error( 'invalid_action', __( 'Invalid action' ) );3031 }3032 3033 $user = get_user_by( 'email', $email );3034 3035 // We could be dealing with a registered user account, or a visitor.3036 $is_registered_user = $user && ! is_wp_error( $user );3037 3078 3038 3079 // Generate something random for a confirmation key. 3039 3080 $key = wp_generate_password( 20, false ); 3040 3081 3041 // Now insert the key, hashed, into the DB.3082 // Return the key, hashed. 3042 3083 if ( empty( $wp_hasher ) ) { 3043 3084 require_once ABSPATH . WPINC . '/class-phpass.php'; … … 3045 3086 } 3046 3087 3047 $hashed_key = $wp_hasher->HashPassword( $key ); 3048 $value = array( 3049 'action' => $action_name, 3050 'time' => time(), 3051 'hash' => $hashed_key, 3052 'email' => $email, 3053 'request_data' => $request_data, 3054 ); 3055 3056 if ( $is_registered_user ) { 3057 $key_saved = (bool) update_user_meta( $user->ID, '_verify_action_' . $action_name, wp_json_encode( $value ) ); 3058 } else { 3059 $uid = function_exists( 'hash' ) ? hash( 'sha256', $email ) : sha1( $email ); 3060 $key_saved = (bool) update_site_option( '_verify_action_' . $action_name . '_' . $uid, wp_json_encode( $value ) ); 3061 } 3062 3063 if ( false === $key_saved ) { 3064 return new WP_Error( 'no_account_verification_key_update', __( 'Could not save confirm account action key to database.' ) ); 3065 } 3088 update_post_meta( $request_id, '_wp_user_request_confirm_key', $wp_hasher->HashPassword( $key ) ); 3089 update_post_meta( $request_id, '_wp_user_request_confirm_key_timestamp', time() ); 3066 3090 3067 3091 return $key; … … 3069 3093 3070 3094 /** 3071 * Checks if a key is valid and handles the action based on this. 3072 * 3073 * @since 5.0.0 3074 * 3075 * @param string $key Key to confirm. 3076 * @param string $uid Email hash or user ID. 3077 * @param string $action_name Name of the action this key is being generated for. 3078 * @return array|WP_Error WP_Error on failure, action name and user email address on success. 3079 */ 3080 function wp_check_account_verification_key( $key, $uid, $action_name ) { 3095 * Valdate a user request by comparing the key with the request's key. 3096 * 3097 * @since 4.9.6 3098 * 3099 * @param string $request_id ID of the request being confirmed. 3100 * @param string $key Provided key to validate. 3101 * @return bool|WP_Error WP_Error on failure, true on success. 3102 */ 3103 function wp_validate_user_request_key( $request_id, $key ) { 3081 3104 global $wp_hasher; 3082 3105 3083 if ( empty( $action_name ) || empty( $key ) || empty( $uid ) ) { 3106 $request_id = absint( $request_id ); 3107 $request = wp_get_user_request_data( $request_id ); 3108 3109 if ( ! $request ) { 3110 return new WP_Error( 'user_request_error', __( 'Invalid request.' ) ); 3111 } 3112 3113 if ( ! in_array( $request['status'], array( 'request-pending', 'request-failed' ), true ) ) { 3114 return __( 'This link has expired.' ); 3115 } 3116 3117 if ( empty( $key ) ) { 3084 3118 return new WP_Error( 'invalid_key', __( 'Invalid key' ) ); 3085 3119 } 3086 3087 $user = false;3088 3089 if ( is_numeric( $uid ) ) {3090 $user = get_user_by( 'id', absint( $uid ) );3091 }3092 3093 // We could be dealing with a registered user account, or a visitor.3094 $is_registered_user = ( $user && ! is_wp_error( $user ) );3095 $key_request_time = '';3096 $saved_key = '';3097 $email = '';3098 3120 3099 3121 if ( empty( $wp_hasher ) ) { … … 3102 3124 } 3103 3125 3104 // Get the saved key from the database. 3105 if ( $is_registered_user ) { 3106 $raw_data = get_user_meta( $user->ID, '_verify_action_' . $action_name, true ); 3107 $email = $user->user_email; 3108 3109 if ( false !== strpos( $raw_data, ':' ) ) { 3110 list( $key_request_time, $saved_key ) = explode( ':', $raw_data, 2 ); 3111 } 3112 } else { 3113 $raw_data = get_site_option( '_verify_action_' . $action_name . '_' . $uid, '' ); 3114 3115 if ( false !== strpos( $raw_data, ':' ) ) { 3116 list( $key_request_time, $saved_key, $email ) = explode( ':', $raw_data, 3 ); 3117 } 3118 } 3119 3120 $data = json_decode( $raw_data, true ); 3121 $key_request_time = (int) isset( $data['time'] ) ? $data['time'] : 0; 3122 $saved_key = isset( $data['hash'] ) ? $data['hash'] : ''; 3123 $email = sanitize_email( isset( $data['email'] ) ? $data['email'] : '' ); 3124 $request_data = isset( $data['request_data'] ) ? $data['request_data'] : array(); 3126 $key_request_time = $request['confirm_key_timestamp']; 3127 $saved_key = $request['confirm_key']; 3125 3128 3126 3129 if ( ! $saved_key ) { … … 3128 3131 } 3129 3132 3130 if ( ! $key_request_time || ! $email) {3133 if ( ! $key_request_time ) { 3131 3134 return new WP_Error( 'invalid_key', __( 'Invalid action' ) ); 3132 3135 } … … 3135 3138 * Filters the expiration time of confirm keys. 3136 3139 * 3137 * @since 5.0.03140 * @since 4.9.6 3138 3141 * 3139 3142 * @param int $expiration The expiration time in seconds. 3140 3143 */ 3141 $expiration_duration = apply_filters( 'account_verification_expiration', DAY_IN_SECONDS );3144 $expiration_duration = (int) apply_filters( 'user_request_key_expiration', DAY_IN_SECONDS ); 3142 3145 $expiration_time = $key_request_time + $expiration_duration; 3143 3146 … … 3146 3149 } 3147 3150 3148 if ( $expiration_time && time() < $expiration_time ) { 3149 $return = array( 3150 'action' => $action_name, 3151 'email' => $email, 3152 'request_data' => $request_data, 3153 ); 3154 } else { 3151 if ( ! $expiration_time || time() > $expiration_time ) { 3155 3152 $return = new WP_Error( 'expired_key', __( 'The confirmation email has expired.' ) ); 3156 3153 } 3157 3154 3158 // Clean up stored keys. 3159 if ( $is_registered_user ) { 3160 delete_user_meta( $user->ID, '_verify_action_' . $action_name ); 3161 } else { 3162 delete_site_option( '_verify_action_' . $action_name . '_' . $uid ); 3163 } 3164 3165 return $return; 3166 } 3155 return true; 3156 } 3157 3158 /** 3159 * Return data about a user request. 3160 * 3161 * @since 4.9.6 3162 * 3163 * @param int $request_id Request ID to get data about. 3164 * @return array|false 3165 */ 3166 function wp_get_user_request_data( $request_id ) { 3167 $request_id = absint( $request_id ); 3168 $request = get_post( $request_id ); 3169 3170 if ( ! $request || 'user_request' !== $request->post_type ) { 3171 return false; 3172 } 3173 3174 return array( 3175 'request_id' => $request->ID, 3176 'user_id' => $request->post_author, 3177 'email' => get_post_meta( $request->ID, '_wp_user_request_user_email', true ), 3178 'action' => $request->post_title, 3179 'requested_timestamp' => strtotime( $request->post_date_gmt ), 3180 'confirmed_timestamp' => get_post_meta( $request->ID, '_wp_user_request_confirmed_timestamp', true ), 3181 'completed_timestamp' => get_post_meta( $request->ID, '_wp_user_request_completed_timestamp', true ), 3182 'request_data' => json_decode( $request->post_content, true ), 3183 'status' => $request->post_status, 3184 'confirm_key' => get_post_meta( $request_id, '_wp_user_request_confirm_key', true ), 3185 'confirm_key_timestamp' => get_post_meta( $request_id, '_wp_user_request_confirm_key_timestamp', true ), 3186 ); 3187 } -
trunk/src/wp-login.php
r42964 r43008 428 428 429 429 // validate action so as to default to the login screen 430 if ( ! in_array( $action, array( 'postpass', 'logout', 'lostpassword', 'retrievepassword', 'resetpass', 'rp', 'register', 'login', ' verifyaccount' ), true ) && false === has_filter( 'login_form_' . $action ) ) {430 if ( ! in_array( $action, array( 'postpass', 'logout', 'lostpassword', 'retrievepassword', 'resetpass', 'rp', 'register', 'login', 'confirmaction' ), true ) && false === has_filter( 'login_form_' . $action ) ) { 431 431 $action = 'login'; 432 432 } … … 859 859 break; 860 860 861 case 'verifyaccount' : 862 if ( isset( $_GET['confirm_action'], $_GET['confirm_key'], $_GET['uid'] ) ) { 863 $key = sanitize_text_field( wp_unslash( $_GET['confirm_key'] ) ); 864 $uid = sanitize_text_field( wp_unslash( $_GET['uid'] ) ); 865 $action_name = sanitize_key( wp_unslash( $_GET['confirm_action'] ) ); 866 $result = wp_check_account_verification_key( $key, $uid, $action_name ); 861 case 'confirmaction' : 862 if ( ! isset( $_GET['request_id'] ) ) { 863 wp_die( __( 'Invalid request' ) ); 864 } 865 866 $request_id = (int) $_GET['request_id']; 867 868 if ( isset( $_GET['confirm_key'] ) ) { 869 $key = sanitize_text_field( wp_unslash( $_GET['confirm_key'] ) ); 870 $result = wp_validate_user_request_key( $request_id, $key ); 867 871 } else { 868 872 $result = new WP_Error( 'invalid_key', __( 'Invalid key' ) ); … … 870 874 871 875 if ( is_wp_error( $result ) ) { 872 /**873 * Fires an action hook when the account action was not confirmed.874 *875 * After running this action hook the page will die.876 *877 * @param WP_Error $result Error object.878 */879 do_action( 'account_action_failed', $result );880 881 876 wp_die( $result ); 882 877 } … … 891 886 * redirects or exits first. 892 887 * 893 * @param array $result { 894 * Data about the action which was confirmed. 895 * 896 * @type string $action Name of the action that was confirmed. 897 * @type string $email Email of the user who confirmed the action. 898 * } 888 * @param int $request_id Request ID. 899 889 */ 900 do_action( 'account_action_confirmed', $result ); 901 902 $message = '<p class="message">' . __( 'Action has been confirmed.' ) . '</p>'; 903 login_header( '', $message ); 890 do_action( 'user_request_action_confirmed', $request_id ); 891 892 $message = apply_filters( 'user_request_action_confirmed_message', '<p class="message">' . __( 'Action has been confirmed.' ) . '</p>', $request_id ); 893 894 login_header( __( 'User action confirmed.' ), $message ); 904 895 login_footer(); 905 896 exit;
Note: See TracChangeset
for help on using the changeset viewer.