Ticket #34281: 34281.4.diff
File 34281.4.diff, 18.0 KB (added by , 9 years ago) |
---|
-
src/wp-admin/admin-ajax.php
62 62 'send-attachment-to-editor', 'save-attachment-order', 'heartbeat', 'get-revision-diffs', 63 63 'save-user-color-scheme', 'update-widget', 'query-themes', 'parse-embed', 'set-attachment-thumbnail', 64 64 'parse-media-shortcode', 'destroy-sessions', 'install-plugin', 'update-plugin', 'press-this-save-post', 65 'press-this-add-category', 'crop-image', 'generate-password', 'save-wporg-username', 65 'press-this-add-category', 'crop-image', 'generate-password', 'save-wporg-username', 'send-password-reset', 66 66 ); 67 67 68 68 // Deprecated -
src/wp-admin/includes/ajax-actions.php
3316 3316 3317 3317 wp_send_json_success( update_user_meta( get_current_user_id(), 'wporg_favorites', $username ) ); 3318 3318 } 3319 3320 /** 3321 * Ajax handler sends a password reset link. 3322 * 3323 * @since 4.4.0 3324 */ 3325 function wp_ajax_send_password_reset() { 3326 3327 // Validate the nonce for this action. 3328 $user_id = isset( $_POST['user_id'] ) ? (int) $_POST['user_id'] : 0; 3329 check_ajax_referer( 'reset-password-for-' . $user_id, 'nonce' ); 3330 3331 // Verify user capabilities. 3332 if ( ! current_user_can( 'edit_user', $user_id ) ) { 3333 wp_send_json_error( __( 'Cannot send password reset, permission denied.' ) ); 3334 } 3335 3336 // Send the password reset link. 3337 $user = get_userdata( $user_id ); 3338 $results = retrieve_password( $user->user_login ); 3339 3340 if ( true === $results ) { 3341 wp_send_json_success( 3342 /* translators: 1: User's display name. */ 3343 sprintf( __( 'A password reset link was emailed to %s.' ), $user->display_name ) 3344 ); 3345 } else { 3346 wp_send_json_error( $results ); 3347 } 3348 } -
src/wp-admin/includes/class-wp-users-list-table.php
242 242 } else { 243 243 if ( current_user_can( 'delete_users' ) ) 244 244 $actions['delete'] = __( 'Delete' ); 245 245 246 } 246 247 248 // Add a password reset link to the bulk actions dropdown. 249 if ( current_user_can( 'edit_users' ) ) { 250 $actions['resetpassword'] = __( 'Send password reset' ); 251 } 252 247 253 return $actions; 248 254 } 249 255 … … 410 416 $actions['delete'] = "<a class='submitdelete' href='" . wp_nonce_url( "users.php?action=delete&user=$user_object->ID", 'bulk-users' ) . "'>" . __( 'Delete' ) . "</a>"; 411 417 if ( is_multisite() && get_current_user_id() != $user_object->ID && current_user_can( 'remove_user', $user_object->ID ) ) 412 418 $actions['remove'] = "<a class='submitdelete' href='" . wp_nonce_url( $url."action=remove&user=$user_object->ID", 'bulk-users' ) . "'>" . __( 'Remove' ) . "</a>"; 419 // Add a link to send the user a reset password link by email. 420 if ( get_current_user_id() != $user_object->ID && current_user_can( 'edit_user', $user_object->ID ) ) 421 $actions['resetpassword'] = "<a class='resetpassword' href='" . wp_nonce_url( "users.php?action=resetpassword&users=$user_object->ID", 'bulk-users' ) . "'>" . __( 'Send password reset' ) . "</a>"; 413 422 423 414 424 /** 415 425 * Filter the action links displayed under each user in the Users list table. 416 426 * -
src/wp-admin/js/user-profile.js
152 152 }); 153 153 } 154 154 155 /** 156 * Handle the password reset button. Sets up an ajax callback to trigger sending 157 * a password reset email. 158 */ 159 function bindPasswordRestLink() { 160 $( '#generate-reset-link' ).on( 'click', function() { 161 var $this = $(this), 162 data = { 163 'user_id': userProfileL10n.user_id, // The user to send a reset to. 164 'nonce': userProfileL10n.nonce // Nonce to validate the action. 165 }, 166 // Send the reset request. 167 resetAction = wp.ajax.post( 'send-password-reset', data ); 168 169 // Handle reset success. 170 resetAction.done( function( response ) { 171 addInlineNotice( $this, true, response ); 172 } ); 173 174 // Handle reset failure. 175 resetAction.fail( function( response ) { 176 addInlineNotice( $this, false, response ); 177 } ); 178 179 }); 180 181 } 182 183 /** 184 * Helper function to insert an inline notice of success or failure. 185 * 186 * @param {jQuery Object} $this The button element: the message will be inserted 187 * above this button 188 * @param {bool} success Whether the message is a success message. 189 * @param {string} message The message to insert. 190 */ 191 function addInlineNotice( $this, success, message ) { 192 var resultDiv = $( '<div />' ); 193 194 // Set up the notice div. 195 resultDiv.addClass( 'notice inline' ); 196 197 // Add a class indicating success or failure. 198 resultDiv.addClass( 'notice-' + ( success ? 'success' : 'error' ) ); 199 200 // Add the message, using text for security, wrapping in a p tag. 201 resultDiv.text( message ).wrapInner( '<p />' ); 202 203 // Disable the button when the callback has succeeded. 204 $this.prop( 'disabled', success ); 205 206 // Remove any previous notices. 207 $this.siblings( '.notice' ).remove(); 208 209 // Insert the notice. 210 $this.before( resultDiv ); 211 } 212 155 213 function bindPasswordForm() { 156 214 var $passwordWrapper, 157 215 $generateButton, … … 380 438 }); 381 439 382 440 bindPasswordForm(); 441 bindPasswordRestLink(); 383 442 }); 384 443 385 444 $( '#destroy-sessions' ).on( 'click', function( e ) { -
src/wp-admin/user-edit.php
491 491 </td> 492 492 </tr> 493 493 <?php endif; ?> 494 <?php 495 // Allow admins to send reset password link 496 if ( ! IS_PROFILE_PAGE ) : 497 ?> 498 <tr class="user-sessions-wrap hide-if-no-js"> 499 <th><?php _e( 'Password Reset' ); ?></th> 500 <td> 501 <div class="generate-reset-link"> 502 <button type="button" class="button button-secondary" id="generate-reset-link"> 503 <?php _e( 'Send Reset Link' ); ?> 504 </button> 505 </div> 506 <p class="description"> 507 <?php 508 /* translators: 1: User's display name. */ 509 printf( __( 'Send %s a link to reset their password. This will not change their password, nor will it force a change.' ), esc_html( $profileuser->display_name ) ); 510 ?> 511 </p> 512 </td> 513 </tr> 514 <?php endif; ?> 494 515 495 516 <?php 496 517 if ( IS_PROFILE_PAGE && count( $sessions->get_all() ) === 1 ) : ?> -
src/wp-admin/users.php
192 192 wp_redirect($redirect); 193 193 exit(); 194 194 195 case 'resetpassword': 196 check_admin_referer('bulk-users'); 197 if ( ! current_user_can( 'edit_users' ) ) { 198 $errors = new WP_Error( 'edit_users', __( 'You can’t edit users.' ) ); 199 } 200 if ( empty( $_REQUEST['users'] ) ) { 201 wp_redirect( $redirect ); 202 exit(); 203 } 204 $userids = array_map( 'intval', (array) $_REQUEST['users'] ); 205 206 $reset_count = 0; 207 208 foreach ( $userids as $id ) { 209 if ( ! current_user_can( 'edit_user', $id ) ) 210 wp_die(__( 'You can’t edit that user.' ) ); 211 212 if ( $id == $current_user->ID ) { 213 $update = 'err_admin_reset'; 214 continue; 215 } 216 217 // Send the password reset link. 218 $user = get_userdata( $id ); 219 if ( retrieve_password( $user->user_login ) ) { 220 ++$reset_count; 221 } 222 223 } 224 225 $redirect = add_query_arg( 226 array( 227 'reset_count' => $reset_count, 228 'update' => 'resetpassword', 229 ), $redirect 230 ); 231 wp_redirect( $redirect ); 232 exit(); 233 234 195 235 case 'delete': 196 236 if ( is_multisite() ) 197 237 wp_die( __('User deletion is not allowed from this screen.') ); … … 429 469 $messages[] = '<div id="message" class="updated notice is-dismissible"><p>' . __( 'New user created.' ) . '</p></div>'; 430 470 } 431 471 break; 472 case 'resetpassword': 473 $reset_count = isset( $_GET['reset_count'] ) ? (int) $_GET['reset_count'] : 0; 474 if ( 1 === $reset_count ) { 475 $message = __( 'Password reset link sent.' ); 476 } else { 477 $message = sprintf( __( 'Password reset links sent to %s users.' ), $reset_count ); 478 } 479 $messages[] = '<div id="message" class="updated notice is-dismissible"><p>' . $message . '</p></div>'; 480 break; 432 481 case 'promote': 433 482 $messages[] = '<div id="message" class="updated notice is-dismissible"><p>' . __('Changed roles.') . '</p></div>'; 434 483 break; … … 460 509 </ul> 461 510 </div> 462 511 <?php endif; 463 464 512 if ( ! empty($messages) ) { 465 513 foreach ( $messages as $msg ) 466 514 echo $msg; -
src/wp-includes/functions.php
5161 5161 // Strip timezone information 5162 5162 return preg_replace( '/(?:Z|[+-]\d{2}(?::\d{2})?)$/', '', $formatted ); 5163 5163 } 5164 5165 /** 5166 * Handles sending password retrieval email to user. 5167 * 5168 * @global wpdb $wpdb WordPress database abstraction object. 5169 * @global PasswordHash $wp_hasher Portable PHP password hashing framework. 5170 * @param string $user_login Optional user_login, default null. 5171 * Uses $_POST['user_login'] if $user_login not set. 5172 * 5173 * @return bool|WP_Error True: when finish. WP_Error on error 5174 */ 5175 function retrieve_password( $user_login = null ) { 5176 global $wpdb, $wp_hasher; 5177 5178 $errors = new WP_Error(); 5179 5180 // Use the passed $user_login if available, otherwise use $_POST['user_login']. 5181 if ( ! $user_login ) { 5182 $user_login = $_POST['user_login']; 5183 } 5184 5185 if ( empty( $_POST['user_login'] ) ) { 5186 $errors->add('empty_username', __('<strong>ERROR</strong>: Enter a username or email address.')); 5187 } elseif ( strpos( $_POST['user_login'], '@' ) ) { 5188 $user_data = get_user_by( 'email', trim( $_POST['user_login'] ) ); 5189 if ( empty( $user_data ) ) 5190 $errors->add('invalid_email', __('<strong>ERROR</strong>: There is no user registered with that email address.')); 5191 } else { 5192 $login = trim($_POST['user_login']); 5193 $user_data = get_user_by('login', $login); 5194 } 5195 5196 /** 5197 * Fires before errors are returned from a password reset request. 5198 * 5199 * @since 2.1.0 5200 * @since 4.4.0 Added the `$errors` parameter. 5201 * 5202 * @param WP_Error $errors A WP_Error object containing any errors generated 5203 * by using invalid credentials. 5204 */ 5205 do_action( 'lostpassword_post', $errors ); 5206 5207 if ( $errors->get_error_code() ) 5208 return $errors; 5209 5210 if ( !$user_data ) { 5211 $errors->add('invalidcombo', __('<strong>ERROR</strong>: Invalid username or email.')); 5212 return $errors; 5213 } 5214 5215 // Redefining user_login ensures we return the right case in the email. 5216 $user_login = $user_data->user_login; 5217 $user_email = $user_data->user_email; 5218 $key = get_password_reset_key( $user_data ); 5219 5220 if ( is_wp_error( $key ) ) { 5221 return $key; 5222 } 5223 5224 $message = __('Someone requested that the password be reset for the following account:') . "\r\n\r\n"; 5225 $message .= network_home_url( '/' ) . "\r\n\r\n"; 5226 $message .= sprintf(__('Username: %s'), $user_login) . "\r\n\r\n"; 5227 $message .= __('If this was a mistake, just ignore this email and nothing will happen.') . "\r\n\r\n"; 5228 $message .= __('To reset your password, visit the following address:') . "\r\n\r\n"; 5229 $message .= '<' . network_site_url("wp-login.php?action=rp&key=$key&login=" . rawurlencode($user_login), 'login') . ">\r\n"; 5230 5231 if ( is_multisite() ) 5232 $blogname = $GLOBALS['current_site']->site_name; 5233 else 5234 /* 5235 * The blogname option is escaped with esc_html on the way into the database 5236 * in sanitize_option we want to reverse this for the plain text arena of emails. 5237 */ 5238 $blogname = wp_specialchars_decode(get_option('blogname'), ENT_QUOTES); 5239 5240 $title = sprintf( __('[%s] Password Reset'), $blogname ); 5241 5242 /** 5243 * Filter the subject of the password reset email. 5244 * 5245 * @since 2.8.0 5246 * @since 4.4.0 Added the `$user_login` and `$user_data` parameters. 5247 * 5248 * @param string $title Default email title. 5249 * @param string $user_login The username for the user. 5250 * @param WP_User $user_data WP_User object. 5251 */ 5252 $title = apply_filters( 'retrieve_password_title', $title, $user_login, $user_data ); 5253 5254 /** 5255 * Filter the message body of the password reset mail. 5256 * 5257 * @since 2.8.0 5258 * @since 4.1.0 Added `$user_login` and `$user_data` parameters. 5259 * 5260 * @param string $message Default mail message. 5261 * @param string $key The activation key. 5262 * @param string $user_login The username for the user. 5263 * @param WP_User $user_data WP_User object. 5264 */ 5265 $message = apply_filters( 'retrieve_password_message', $message, $key, $user_login, $user_data ); 5266 5267 if ( $message && !wp_mail( $user_email, wp_specialchars_decode( $title ), $message ) ) 5268 wp_die( __('The email could not be sent.') . "<br />\n" . __('Possible reason: your host may have disabled the mail() function.') ); 5269 5270 return true; 5271 } -
src/wp-includes/script-loader.php
382 382 'mismatch' => _x( 'Mismatch', 'password mismatch' ), 383 383 ) ); 384 384 385 $user_id = isset( $_GET['user_id'] ) ? (int) $_GET['user_id'] : 0; 385 386 $scripts->add( 'user-profile', "/wp-admin/js/user-profile$suffix.js", array( 'jquery', 'password-strength-meter', 'wp-util' ), false, 1 ); 386 387 did_action( 'init' ) && $scripts->localize( 'user-profile', 'userProfileL10n', array( 387 388 'warn' => __( 'Your new password has not been saved.' ), … … 390 391 'cancel' => __( 'Cancel' ), 391 392 'ariaShow' => esc_attr__( 'Show password' ), 392 393 'ariaHide' => esc_attr__( 'Hide password' ), 394 'user_id' => $user_id, 395 'nonce' => wp_create_nonce( 'reset-password-for-' . $user_id ), 393 396 ) ); 394 397 395 398 $scripts->add( 'language-chooser', "/wp-admin/js/language-chooser$suffix.js", array( 'jquery' ), false, 1 ); -
src/wp-login.php
267 267 <?php 268 268 } 269 269 270 /**271 * Handles sending password retrieval email to user.272 *273 * @global wpdb $wpdb WordPress database abstraction object.274 * @global PasswordHash $wp_hasher Portable PHP password hashing framework.275 *276 * @return bool|WP_Error True: when finish. WP_Error on error277 */278 function retrieve_password() {279 global $wpdb, $wp_hasher;280 281 $errors = new WP_Error();282 283 if ( empty( $_POST['user_login'] ) ) {284 $errors->add('empty_username', __('<strong>ERROR</strong>: Enter a username or email address.'));285 } elseif ( strpos( $_POST['user_login'], '@' ) ) {286 $user_data = get_user_by( 'email', trim( $_POST['user_login'] ) );287 if ( empty( $user_data ) )288 $errors->add('invalid_email', __('<strong>ERROR</strong>: There is no user registered with that email address.'));289 } else {290 $login = trim($_POST['user_login']);291 $user_data = get_user_by('login', $login);292 }293 294 /**295 * Fires before errors are returned from a password reset request.296 *297 * @since 2.1.0298 * @since 4.4.0 Added the `$errors` parameter.299 *300 * @param WP_Error $errors A WP_Error object containing any errors generated301 * by using invalid credentials.302 */303 do_action( 'lostpassword_post', $errors );304 305 if ( $errors->get_error_code() )306 return $errors;307 308 if ( !$user_data ) {309 $errors->add('invalidcombo', __('<strong>ERROR</strong>: Invalid username or email.'));310 return $errors;311 }312 313 // Redefining user_login ensures we return the right case in the email.314 $user_login = $user_data->user_login;315 $user_email = $user_data->user_email;316 $key = get_password_reset_key( $user_data );317 318 if ( is_wp_error( $key ) ) {319 return $key;320 }321 322 $message = __('Someone has requested a password reset for the following account:') . "\r\n\r\n";323 $message .= network_home_url( '/' ) . "\r\n\r\n";324 $message .= sprintf(__('Username: %s'), $user_login) . "\r\n\r\n";325 $message .= __('If this was a mistake, just ignore this email and nothing will happen.') . "\r\n\r\n";326 $message .= __('To reset your password, visit the following address:') . "\r\n\r\n";327 $message .= '<' . network_site_url("wp-login.php?action=rp&key=$key&login=" . rawurlencode($user_login), 'login') . ">\r\n";328 329 if ( is_multisite() )330 $blogname = $GLOBALS['current_site']->site_name;331 else332 /*333 * The blogname option is escaped with esc_html on the way into the database334 * in sanitize_option we want to reverse this for the plain text arena of emails.335 */336 $blogname = wp_specialchars_decode(get_option('blogname'), ENT_QUOTES);337 338 $title = sprintf( __('[%s] Password Reset'), $blogname );339 340 /**341 * Filter the subject of the password reset email.342 *343 * @since 2.8.0344 * @since 4.4.0 Added the `$user_login` and `$user_data` parameters.345 *346 * @param string $title Default email title.347 * @param string $user_login The username for the user.348 * @param WP_User $user_data WP_User object.349 */350 $title = apply_filters( 'retrieve_password_title', $title, $user_login, $user_data );351 352 /**353 * Filter the message body of the password reset mail.354 *355 * @since 2.8.0356 * @since 4.1.0 Added `$user_login` and `$user_data` parameters.357 *358 * @param string $message Default mail message.359 * @param string $key The activation key.360 * @param string $user_login The username for the user.361 * @param WP_User $user_data WP_User object.362 */363 $message = apply_filters( 'retrieve_password_message', $message, $key, $user_login, $user_data );364 365 if ( $message && !wp_mail( $user_email, wp_specialchars_decode( $title ), $message ) )366 wp_die( __('The email could not be sent.') . "<br />\n" . __('Possible reason: your host may have disabled the mail() function.') );367 368 return true;369 }370 371 270 // 372 271 // Main 373 272 //