Ticket #43443: 43443-refactor.diff
File 43443-refactor.diff, 36.7 KB (added by , 6 years ago) |
---|
-
wp-admin/includes/admin-filters.php
46 46 add_action( 'admin_head', '_ipad_meta' ); 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 52 51 // Prerendering. -
wp-admin/includes/user.php
581 581 } 582 582 583 583 /** 584 * Get action description from the name.585 *586 * @since 4.9.6587 * @access private588 *589 * @return string590 */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.6604 * @access private605 *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 584 * Resend an existing request and return the result. 642 585 * 643 586 * @since 4.9.6 644 587 * @access private 645 588 * 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 );592 function _wp_privacy_resend_request( $request_id ) { 593 $request_id = absint( $request_id ); 594 $request = get_post( $request_id ); 652 595 653 if ( ! $ privacy_request || ! in_array( $privacy_request->post_type, _wp_privacy_action_request_types(), true )) {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 ) ) { 665 603 return $result; … … 667 605 return new WP_Error( 'privacy_request_error', __( 'Unable to initiate confirmation request.' ) ); 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 } 679 610 … … 683 614 * @since 4.9.6 684 615 * @access private 685 616 * 686 * @param int $ privacy_request_id Request ID.617 * @param int $request_id Request ID. 687 618 * @return bool|WP_Error 688 619 */ 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 );620 function _wp_privacy_completed_request( $request_id ) { 621 $request_id = absint( $request_id ); 622 $request_data = wp_get_user_request_data( $request_id ); 692 623 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.' ) );624 if ( ! $request_data ) { 625 return; 695 626 } 696 627 628 update_post_meta( $privacy_request_id, '_confirmed_timestamp', time() ); 697 629 wp_update_post( array( 698 'ID' => $ privacy_request_id,699 'post_status' => 'request-co mpleted',630 'ID' => $request_data['request_id'], 631 'post_status' => 'request-confirmed', 700 632 ) ); 701 702 update_post_meta( $privacy_request_id, '_completed_timestamp', time() );703 633 } 704 634 705 635 /** … … 803 733 $email_address = $username_or_email_address; 804 734 } 805 735 806 if ( ! empty( $email_address ) ) { 807 $result = _wp_privacy_create_request( $email_address, $action_type, _wp_privacy_action_description( $action_type ) ); 736 if ( empty( $email_address ) ) { 737 break; 738 } 808 739 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 } 740 $request_id = wp_create_user_request( $email_address, $action_type ); 741 742 if ( is_wp_error( $result ) ) { 743 add_settings_error( 744 'username_or_email_to_export', 745 'username_or_email_to_export', 746 $result->get_error_message(), 747 'error' 748 ); 749 break; 750 } elseif ( ! $result ) { 751 add_settings_error( 752 'username_or_email_to_export', 753 'username_or_email_to_export', 754 __( 'Unable to initiate confirmation request.' ), 755 'error' 756 ); 757 break; 831 758 } 759 760 wp_send_user_request( $request_id ); 761 762 add_settings_error( 763 'username_or_email_to_export', 764 'username_or_email_to_export', 765 __( 'Confirmation request initiated successfully.' ), 766 'updated' 767 ); 832 768 break; 833 769 } 834 770 } … … 871 807 </div> 872 808 <?php wp_nonce_field( 'personal-data-request' ); ?> 873 809 <input type="hidden" name="action" value="add_export_personal_data_request" /> 874 <input type="hidden" name="type_of_action" value=" user_export_request" />810 <input type="hidden" name="type_of_action" value="export_personal_data" /> 875 811 </form> 876 812 <hr /> 877 813 … … 937 873 </div> 938 874 <?php wp_nonce_field( 'personal-data-request' ); ?> 939 875 <input type="hidden" name="action" value="add_remove_personal_data_request" /> 940 <input type="hidden" name="type_of_action" value=" user_remove_request" />876 <input type="hidden" name="type_of_action" value="remove_personal_data" /> 941 877 </form> 942 878 <hr /> 943 879 … … 1011 947 */ 1012 948 public function get_columns() { 1013 949 $columns = array( 1014 'cb' => '<input type="checkbox" />',1015 'email' => __( 'Requester' ),1016 'status' => __( 'Status' ),1017 'requested '=> __( 'Requested' ),1018 'next_steps' => __( 'Next Steps' ),950 'cb' => '<input type="checkbox" />', 951 'email' => __( 'Requester' ), 952 'status' => __( 'Status' ), 953 'requested_timestamp' => __( 'Requested' ), 954 'next_steps' => __( 'Next Steps' ), 1019 955 ); 1020 956 return $columns; 1021 957 } … … 1043 979 } 1044 980 1045 981 /** 982 * Count number of requests for each status. 983 * 984 * @since 4.9.6 985 * 986 * @return object Number of posts for each status. 987 */ 988 protected function get_request_counts() { 989 global $wpdb; 990 991 $cache_key = $this->post_type . '-' . $this->request_type; 992 $counts = wp_cache_get( $cache_key, 'counts' ); 993 994 if ( false !== $counts ) { 995 return $counts; 996 } 997 998 $query = " 999 SELECT post_status, COUNT( * ) AS num_posts 1000 FROM {$wpdb->posts} 1001 WHERE post_type = %s 1002 AND post_title = %s 1003 GROUP BY post_status"; 1004 1005 $results = (array) $wpdb->get_results( $wpdb->prepare( $query, $this->post_type, $this->request_type ), ARRAY_A ); 1006 $counts = array_fill_keys( get_post_stati(), 0 ); 1007 1008 foreach ( $results as $row ) { 1009 $counts[ $row['post_status'] ] = $row['num_posts']; 1010 } 1011 1012 $counts = (object) $counts; 1013 wp_cache_set( $cache_key, $counts, 'counts' ); 1014 1015 return $counts; 1016 } 1017 1018 /** 1046 1019 * Get an associative array ( id => link ) with the list 1047 1020 * of views available on this table. 1048 1021 * … … 1055 1028 $statuses = _wp_privacy_statuses(); 1056 1029 $views = array(); 1057 1030 $admin_url = admin_url( 'tools.php?page=' . $this->request_type ); 1058 $counts = wp_count_posts( $this->post_type);1031 $counts = $this->get_request_counts(); 1059 1032 1060 1033 $current_link_attributes = empty( $current_status ) ? ' class="current" aria-current="page"' : ''; 1061 1034 $views['all'] = '<a href="' . esc_url( $admin_url ) . "\" $current_link_attributes>" . esc_html__( 'All' ) . ' <span class="count">(' . absint( array_sum( (array) $counts ) ) . ')</span></a>'; … … 1151 1124 $posts_per_page = 20; 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, 1156 1130 'post_status' => 'any', … … 1168 1142 array( 1169 1143 'key' => '_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;1150 $requests_query = new WP_Query( $args ); 1151 $requests = $requests_query->posts; 1178 1152 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 ); 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 ) 1196 1162 ); … … 1228 1194 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 } 1237 1203 … … 1279 1245 public function column_default( $item, $column_name ) { 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 } 1285 1251 … … 1352 1318 * 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 /** 1358 1324 * Actions column. … … 1437 1403 * 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 /** 1443 1409 * Actions column. -
wp-content/plugins
-
wp-content
Property changes on: wp-content/plugins ___________________________________________________________________ Added: svn:ignore ## -0,0 +1,6 ## +order-simulator-woocommerce-master +query-monitor +woocommerce +wp-cron-control +woocommerce-product-type-column +wp-mail-smtp
-
wp-includes/post.php
Property changes on: wp-content ___________________________________________________________________ Added: svn:ignore ## -0,0 +1,4 ## +uploads +upgrade +debug.log +db.php
227 227 ); 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, 236 236 '_builtin' => true, /* internal use only. don't use this when registering your own post type. */ … … 242 242 ) 243 243 ); 244 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 261 245 register_post_status( 262 246 'publish', array( 263 247 'label' => _x( 'Published', 'post status' ), -
wp-includes/user.php
2813 2813 /** 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 * 2819 2819 * @return array … … 2820 2820 */ 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 } 2827 2827 … … 2828 2828 /** 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 2834 * @param array $result Result of the request from the user. 2835 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 ); 2836 function _wp_privacy_account_request_confirmed( $request_id, $result ) { 2837 $request_data = wp_get_user_request_data( $request_id ); 2840 2838 2841 if ( ! $privacy_request || ! in_array( $privacy_request->post_type, _wp_privacy_action_request_types(), true )) {2842 2843 2839 if ( ! $request_data ) { 2840 return; 2841 } 2844 2842 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 ) ); 2843 if ( ! in_array( $request_data['status'], array( 'request-pending', 'request-failed' ), true ) ) { 2844 return; 2850 2845 } 2846 2847 update_post_meta( $privacy_request_id, '_confirmed_timestamp', time() ); 2848 wp_update_post( array( 2849 'ID' => $request_data['request_id'], 2850 'post_status' => 'request-confirmed', 2851 ) ); 2851 2852 } 2852 add_action( ' account_action_confirmed', '_wp_privacy_account_request_confirmed' );2853 add_action( 'user_request_action_confirmed', '_wp_privacy_account_request_confirmed' ); 2853 2854 2854 2855 /** 2855 * U pdate log when privacy request fails.2856 * Undocumented function 2856 2857 * 2857 * @since 5.0.0 2858 * @access private 2859 * 2860 * @param array $result Result of the request from the user. 2858 * @param [type] $message 2859 * @param [type] $request_id 2860 * @return void 2861 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 ) ) { 2862 function _wp_privacy_account_request_confirmed_message( $message, $request_id ) { 2863 $request = wp_get_user_request_data( $request_id ); 2865 2864 2866 $privacy_request_id = absint( $result['request_data']['privacy_request_id'] ); 2867 $privacy_request = get_post( $privacy_request_id ); 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 } 2868 2869 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 } 2870 return $message; 2878 2871 } 2872 add_filter( 'user_request_action_confirmed_message', '_wp_privacy_account_request_confirmed_message', 10, 2 ); 2879 2873 2880 2874 /** 2881 * Send a confirmation request email to confirm anaction.2875 * Create and log a user request to perform a specific action. 2882 2876 * 2883 * @since 5.0.0 2877 * Requests are stored inside a post type named `user_request` since they can apply to both 2878 * users on the site, or guests without a user account. 2884 2879 * 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. 2880 * @since 4.9.6 2881 * 2882 * @param string $email_address User email address. This can be the address of a registered or non-registered user. 2883 * @param string $action_name Name of the action that is being confirmed. Required. 2884 * @param array $request_data Misc data you want to send with the verification request and pass to the actions once the request is confirmed. 2885 * @return int|WP_Error Returns the request ID if successful, or a WP_Error object on failure. 2890 2886 */ 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 } 2887 function wp_create_user_request( $email_address = '', $action_name = '', $request_data = array() ) { 2888 $email_address = sanitize_email( $email_address ); 2889 $action_name = sanitize_key( $action_name ); 2895 2890 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'; 2891 if ( ! is_email( $email_address ) ) { 2892 return new WP_Error( 'invalid_email', __( 'Invalid email address' ) ); 2901 2893 } 2902 2894 2903 if ( empty( $action_description )) {2904 $action_description = __( 'Confirm your email address.');2895 if ( ! $action_name ) { 2896 return new WP_Error( 'invalid_action', __( 'Invalid action name' ) ); 2905 2897 } 2906 2898 2907 if ( empty( $email ) ) { 2908 $user = wp_get_current_user(); 2909 $email = $user->ID ? $user->user_email : ''; 2910 } else { 2911 $user = false; 2912 } 2899 $user = get_user_by( 'email', $email_address ); 2900 $user_id = $user && ! is_wp_error( $user ) ? $user->ID: 0; 2913 2901 2914 $email = sanitize_email( $email ); 2902 // Check for duplicates. 2903 $requests_query = new WP_Query( array( 2904 'post_type' => 'user_request', 2905 'title' => $action_name, 2906 'post_status' => 'any', 2907 'fields' => 'ids', 2908 'meta_query' => array( 2909 array( 2910 'key' => '_user_email', 2911 'value' => $email_address, 2912 ), 2913 ), 2914 ) ); 2915 2915 2916 if ( ! is_email( $email )) {2917 return new WP_Error( 'invalid_email', __( ' Invalid email address' ) );2916 if ( $requests_query->found_posts ) { 2917 return new WP_Error( 'invalid_email', __( 'A request for this email address already exists.' ) ); 2918 2918 } 2919 2919 2920 if ( ! $user ) { 2921 $user = get_user_by( 'email', $email ); 2920 $request_id = wp_insert_post( array( 2921 'post_author' => $user_id, 2922 'post_title' => $action_name, 2923 'post_content' => wp_json_encode( $request_data ), 2924 'post_status' => 'request-pending', 2925 'post_type' => 'user_request', 2926 'post_date' => current_time( 'mysql', false ), 2927 'post_date_gmt' => current_time( 'mysql', true ), 2928 ), true ); 2929 2930 if ( is_wp_error( $request_id ) ) { 2931 return $request_id; 2922 2932 } 2923 2933 2924 $confirm_key = wp_get_account_verification_key( $email, $action_name, $request_data ); 2934 update_post_meta( $request_id, '_user_email', $email_address ); 2935 update_post_meta( $request_id, '_confirmed_timestamp', false ); 2925 2936 2926 if ( is_wp_error( $confirm_key ) ) { 2927 return $confirm_key; 2937 return $request_id; 2938 } 2939 2940 /** 2941 * Get action description from the name and return a string. 2942 * 2943 * @since 4.9.6 2944 * 2945 * @param string $action_name Action name for the request. 2946 * @return string 2947 */ 2948 function wp_user_request_action_description( $action_name ) { 2949 switch ( $action_name ) { 2950 case 'export_personal_data': 2951 $description = __( 'Export Personal Data' ); 2952 break; 2953 case 'remove_personal_data': 2954 $description = __( 'Remove Personal Data' ); 2955 break; 2956 default: 2957 /* translators: %s: action name */ 2958 $description = sprintf( __( 'Confirm the "%s" action' ), $action_name ); 2959 break; 2928 2960 } 2961 return apply_filters( 'user_request_action_description', $description, $action_name ); 2962 } 2929 2963 2930 // We could be dealing with a registered user account, or a visitor. 2931 $is_registered_user = $user && ! is_wp_error( $user ); 2964 /** 2965 * Send a confirmation request email to confirm an action. 2966 * 2967 * If the request is not already pending, it will be updated. 2968 * 2969 * @since 4.9.6 2970 * 2971 * @param string $request_id ID of the request created via wp_create_user_request(). 2972 * @return WP_Error|bool Will return true/false based on the success of sending the email, or a WP_Error object. 2973 */ 2974 function wp_send_user_request( $request_id ) { 2975 $request_id = absint( $request_id ); 2976 $request = get_post( $request_id ); 2932 2977 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 ); 2978 if ( ! $request || 'user_request' !== $request->post_type ) { 2979 return new WP_Error( 'user_request_error', __( 'Invalid request.' ) ); 2938 2980 } 2939 2981 2982 if ( 'request-pending' !== $request->post_status ) { 2983 wp_update_post( array( 2984 'ID' => $request_id, 2985 'post_status' => 'request-pending', 2986 'post_date' => current_time( 'mysql', false ), 2987 'post_date_gmt' => current_time( 'mysql', true ), 2988 ) ); 2989 } 2990 2991 $email_data = array( 2992 'action_name' => $request->post_title, 2993 'email' => get_post_meta( $request->ID, '_user_email', true ), 2994 'description' => wp_user_request_action_description( $request->post_title ), 2995 'confirm_url' => add_query_arg( array( 2996 'action' => 'confirmaction', 2997 'request_id' => $request_id, 2998 'confirm_key' => wp_generate_user_request_key( $request_id ), 2999 ), site_url( 'wp-login.php' ) ), 3000 'sitename' => is_multisite() ? get_site_option( 'site_name' ) : get_option( 'blogname' ), 3001 'siteurl' => network_home_url(), 3002 ); 3003 2940 3004 /* translators: Do not translate DESCRIPTION, CONFIRM_URL, EMAIL, SITENAME, SITEURL: those are placeholders. */ 2941 3005 $email_text = __( 2942 3006 'Howdy, … … 2958 3022 ###SITEURL###' 2959 3023 ); 2960 3024 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 );2974 2975 3025 /** 2976 3026 * Filters the text of the email sent when an account action is attempted. 2977 3027 * … … 2983 3033 * ###SITENAME### The name of the site. 2984 3034 * ###SITEURL### The URL to the site. 2985 3035 * 2986 * @since 5.0.03036 * @since 4.9.6 2987 3037 * 2988 3038 * @param string $email_text Text in the email. 2989 3039 * @param array $email_data { … … 2997 3047 * @type string $siteurl The site URL sending the mail. 2998 3048 * } 2999 3049 */ 3000 $content = apply_filters( ' account_verification_email_content', $email_text, $email_data );3050 $content = apply_filters( 'user_request_action_email_content', $email_text, $email_data ); 3001 3051 3002 3052 $content = str_replace( '###DESCRIPTION###', $email_data['description'], $content ); 3003 3053 $content = str_replace( '###CONFIRM_URL###', esc_url_raw( $email_data['confirm_url'] ), $content ); … … 3010 3060 } 3011 3061 3012 3062 /** 3013 * Creates, stores, then returns a confirmation key for an account action.3063 * Returns a confirmation key for a user action and stores the hashed version. 3014 3064 * 3015 * @since 5.0.03065 * @since 4.9.6 3016 3066 * 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. 3067 * @param int $request_id Request ID. 3068 * @return string Confirmation key. 3021 3069 */ 3022 function wp_ge t_account_verification_key( $email, $action_name, $request_data = array()) {3070 function wp_generate_user_request_key( $request_id ) { 3023 3071 global $wp_hasher; 3024 3072 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 3038 3073 // Generate something random for a confirmation key. 3039 3074 $key = wp_generate_password( 20, false ); 3040 3075 3041 // Now insert the key, hashed, into the DB.3076 // Return the key, hashed. 3042 3077 if ( empty( $wp_hasher ) ) { 3043 3078 require_once ABSPATH . WPINC . '/class-phpass.php'; 3044 3079 $wp_hasher = new PasswordHash( 8, true ); 3045 3080 } 3046 3081 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 ); 3082 update_post_meta( $request_id, '_confirm_key', $wp_hasher->HashPassword( $key ) ); 3083 update_post_meta( $request_id, '_confirm_key_timestamp', time() ); 3055 3084 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 }3066 3067 3085 return $key; 3068 3086 } 3069 3087 3070 3088 /** 3071 * Checks if a key is valid and handles the action based on this.3089 * Valdate a user request by comparing the key with the request's key. 3072 3090 * 3073 * @since 5.0.03091 * @since 4.9.6 3074 3092 * 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. 3093 * @param string $request_id ID of the request being confirmed. 3094 * @param string $key Provided key to validate. 3095 * @return bool|WP_Error WP_Error on failure, true on success. 3079 3096 */ 3080 function wp_ check_account_verification_key( $key, $uid, $action_name) {3097 function wp_validate_user_request_key( $request_id, $key ) { 3081 3098 global $wp_hasher; 3099 return true; // @todo 3100 $request_id = absint( $request_id ); 3101 $request = wp_get_user_request_data( $request_id ); 3082 3102 3083 if ( empty( $action_name ) || empty( $key ) || empty( $uid )) {3084 return new WP_Error( ' invalid_key', __( 'Invalid key' ) );3103 if ( ! $request ) { 3104 return new WP_Error( 'user_request_error', __( 'Invalid request.' ) ); 3085 3105 } 3086 3106 3087 $user = false; 3107 if ( ! in_array( $request['status'], array( 'request-pending', 'request-failed' ), true ) ) { 3108 return __( 'This link has expired.' ); 3109 } 3088 3110 3089 if ( is_numeric( $uid) ) {3090 $user = get_user_by( 'id', absint( $uid) );3111 if ( empty( $key ) ) { 3112 return new WP_Error( 'invalid_key', __( 'Invalid key' ) ); 3091 3113 } 3092 3114 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 3099 3115 if ( empty( $wp_hasher ) ) { 3100 3116 require_once ABSPATH . WPINC . '/class-phpass.php'; 3101 3117 $wp_hasher = new PasswordHash( 8, true ); 3102 3118 } 3103 3119 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; 3120 $key_request_time = $request['confirm_key_timestamp']; 3121 $saved_key = $request['confirm_key']; 3108 3122 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();3125 3126 3123 if ( ! $saved_key ) { 3127 3124 return new WP_Error( 'invalid_key', __( 'Invalid key' ) ); 3128 3125 } 3129 3126 3130 if ( ! $key_request_time || ! $email) {3127 if ( ! $key_request_time ) { 3131 3128 return new WP_Error( 'invalid_key', __( 'Invalid action' ) ); 3132 3129 } 3133 3130 … … 3134 3131 /** 3135 3132 * Filters the expiration time of confirm keys. 3136 3133 * 3137 * @since 5.0.03134 * @since 4.9.6 3138 3135 * 3139 3136 * @param int $expiration The expiration time in seconds. 3140 3137 */ 3141 $expiration_duration = apply_filters( ' account_verification_expiration', DAY_IN_SECONDS );3138 $expiration_duration = apply_filters( 'user_request_key_expiration', WEEK_IN_SECONDS ); 3142 3139 $expiration_time = $key_request_time + $expiration_duration; 3143 3140 3144 3141 if ( ! $wp_hasher->CheckPassword( $key, $saved_key ) ) { … … 3145 3142 return new WP_Error( 'invalid_key', __( 'Invalid key' ) ); 3146 3143 } 3147 3144 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 { 3145 if ( ! $expiration_time || time() > $expiration_time ) { 3155 3146 $return = new WP_Error( 'expired_key', __( 'The confirmation email has expired.' ) ); 3156 3147 } 3157 3148 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 ); 3149 return true; 3150 } 3151 3152 /** 3153 * Return data about a user request. 3154 * 3155 * @since 4.9.6 3156 * 3157 * @param int $request_id Request ID to get data about. 3158 * @return array|false 3159 */ 3160 function wp_get_user_request_data( $request_id ) { 3161 $request_id = absint( $request_id ); 3162 $request = get_post( $request_id ); 3163 3164 if ( ! $request || 'user_request' !== $request->post_type ) { 3165 return false; 3163 3166 } 3164 3167 3165 return $return; 3166 } 3168 return array( 3169 'request_id' => $request->ID, 3170 'user_id' => $request->post_author, 3171 'email' => get_post_meta( $request->ID, '_user_email', true ), 3172 'action' => $request->post_title, 3173 'requested_timestamp' => strtotime( $request->post_date_gmt ), 3174 'confirmed_timestamp' => get_post_meta( $request->ID, '_confirmed_timestamp', true ), 3175 'completed_timestamp' => get_post_meta( $request->ID, '_completed_timestamp', true ), 3176 'request_data' => json_decode( $request->post_content, true ), 3177 'status' => $request->post_status, 3178 'confirm_key' => get_post_meta( $request_id, '_confirm_key', true ), 3179 'confirm_key_timestamp' => get_post_meta( $request_id, '_confirm_key_timestamp', true ), 3180 ); 3181 } 3182 No newline at end of file -
wp-login.php
427 427 } 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 } 433 433 … … 858 858 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 = absint( wp_unslash( $_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' ) ); 869 873 } 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 } 883 878 … … 890 885 * After firing this action hook the page will redirect to wp-login a callback 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);890 do_action( 'user_request_action_confirmed', $request_id ); 901 891 902 $message = '<p class="message">' . __( 'Action has been confirmed.' ) . '</p>'; 903 login_header( '', $message ); 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; 906 897 -
.