A few weeks ago I wrote a tutorial about creating front end registration and login forms. Now it’s time to create a very similar form, but for changing a logged-in user’s password. The process is really pretty simple, and quite similar.
The form is pretty simple. All we need to do is set up a regular HTML form that has two inputs: one for the new password, and one to confirm the new password. Since the form will only be displayed if the user is logged in, there’s not much we have to do in terms of security.
When the form is posted with the new password, we will do a check to make sure the two passwords match, and that we have a user ID. If the passwords match and we have the user ID, then we use the wp_update_user() function to save the information to the user’s account.
The Reset Password Form
We are first going to create a function that will output our Reset Password form.
function pippin_change_password_form() { global $post; if (is_singular()) : $current_url = get_permalink($post->ID); else : $pageURL = 'http'; if ($_SERVER["HTTPS"] == "on") $pageURL .= "s"; $pageURL .= "://"; if ($_SERVER["SERVER_PORT"] != "80") $pageURL .= $_SERVER["SERVER_NAME"].":".$_SERVER["SERVER_PORT"].$_SERVER["REQUEST_URI"]; else $pageURL .= $_SERVER["SERVER_NAME"].$_SERVER["REQUEST_URI"]; $current_url = $pageURL; endif; $redirect = $current_url; ob_start(); // show any error messages after form submission pippin_show_error_messages(); ?> <?php if(isset($_GET['password-reset']) && $_GET['password-reset'] == 'true') { ?> <div class="pippin_message success"> <span><?php _e('Password changed successfully', 'rcp'); ?></span> </div> <?php } ?> <form id="pippin_password_form" method="POST" action="<?php echo $current_url; ?>"> <fieldset> <p> <label for="pippin_user_pass"><?php _e('New Password', 'rcp'); ?></label> <input name="pippin_user_pass" id="pippin_user_pass" class="required" type="password"/> </p> <p> <label for="pippin_user_pass_confirm"><?php _e('Password Confirm', 'rcp'); ?></label> <input name="pippin_user_pass_confirm" id="pippin_user_pass_confirm" class="required" type="password"/> </p> <p> <input type="hidden" name="pippin_action" value="reset-password"/> <input type="hidden" name="pippin_redirect" value="<?php echo $redirect; ?>"/> <input type="hidden" name="pippin_password_nonce" value="<?php echo wp_create_nonce('rcp-password-nonce'); ?>"/> <input id="pippin_password_submit" type="submit" value="<?php _e('Change Password', 'pippin'); ?>"/> </p> </fieldset> </form> <?php return ob_get_clean(); } |
The function looks a lot more complex than it actually is. At the very top we perform a variety of checks needed to obtain the exact URL of our currently page. If we are on a page or post, then getting the URL is simple: just use get_permalink($post->ID). But if we’re on, say, a category index page, or another archive, then we can’t use the get_permalink() function, but instead we use some $_SERVER variables to determine the exact url. There are other ways, probably better ones too, to get the exact url (even if in https: or behind a proxy) but this one works really well. We are going to use this URL in the function that processes the password update, so you can forget about it for a moment.
After we have the page url, we start an output buffer and begin the HTML form. Just before the form, we have a function that will show any error messages generated when submitting the form. If a user tries to change their password, but the new passwords don’t match, then this will display an error message. We will write this function in a little bit.
Next we have a conditional statement that checks to see if the password was reset successfully. If the password was changed, a message is displayed, letting the user know their password has been updated.
Now we have the actual password form. There are two “password” inputs, one for the new password and one for the new password confirmation. there are also several hidden input fields. One is used as an “identifier” that we will use in the processing function. This is just so we know for sure that our processing function is reading data from this form and this form only.
The second hidden field, “pippin_redirect”, has a value of our current page’s URL. This will also be used in our processing function to redirect the user back to this page. We need this so that we can attach a $_GET variable that is used by the conditional above the form to alert the user of a successful password change.
The last hidden field is a nonce. This is a security field that we use to validate the submission of this form. If our nonce doesn’t verify, then we don’t do anything because it probably means that someone is trying to post data to our server from a different location.
The Form Short Code
Now we’re going to write a function that will add a short code that we can use to display our reset password form on any post or page, or in any widget area with a text widget.
// password reset form function pippin_reset_password_form() { if(is_user_logged_in()) { return pippin_change_password_form(); } } add_shortcode('password_form', 'pippin_reset_password_form'); |
This one is really simple. We first check to see if the current user is logged in, and if they are, we return the function we wrote above, which displays the reset password form. The function is then hooked to the add_shortcode() function, which makes our “[password_form]” short code available for use.
Processing the Reset Password Form
This is where it gets fun. The next function we’re going to write is the one that will detect when a password reset is being requested and will then go through the update password process.
function pippin_reset_password() { // reset a users password if(isset($_POST['pippin_action']) && $_POST['pippin_action'] == 'reset-password') { global $user_ID; if(!is_user_logged_in()) return; if(wp_verify_nonce($_POST['pippin_password_nonce'], 'rcp-password-nonce')) { if($_POST['pippin_user_pass'] == '' || $_POST['pippin_user_pass_confirm'] == '') { // password(s) field empty pippin_errors()->add('password_empty', __('Please enter a password, and confirm it', 'pippin')); } if($_POST['pippin_user_pass'] != $_POST['pippin_user_pass_confirm']) { // passwords do not match pippin_errors()->add('password_mismatch', __('Passwords do not match', 'pippin')); } // retrieve all error messages, if any $errors = pippin_errors()->get_error_messages(); if(empty($errors)) { // change the password here $user_data = array( 'ID' => $user_ID, 'user_pass' => $_POST['pippin_user_pass'] ); wp_update_user($user_data); // send password change email here (if WP doesn't) wp_redirect(add_query_arg('password-reset', 'true', $_POST['pippin_redirect'])); exit; } } } } |
The very first thing that happens here is we check to see if the $_POST[‘pippin_action’] variable has been posted, and that its value equals “reset-password”. If both of these checks pass, then we know a user is trying to reset their password and we proceed. Next, we setup the global $user_ID variable. This will hold the ID number of the currently logged in user, if one is logged in. Then we check to make sure that a user is logged in, and stop the function if the user isn’t logged in.
Once we’ve confirmed that the user is logged in and they are trying to change their password, we verify that the nonce field is valid. Remember, this is just for an extra bit of security. We confirm that the nonce field is valid by using the wp_verify_nonce() function.
At this point we have done all of the security checks, so it’s now time to validate the user’s input. We first check to see if either of the input fields are empty by doing this:
if($_POST['pippin_user_pass'] == '' || $_POST['pippin_user_pass_confirm'] == '') |
If either of them are empty, we add an error. This error will be shown when the user is redirected back to the page. We’re going to write the pippin_errors() function in a bit, so don’t worry about it just yet.
Next, we check to see whether the two password fields match. If they don’t match, we record an error:
if($_POST['pippin_user_pass'] != $_POST['pippin_user_pass_confirm']) { // passwords do not match pippin_errors()->add('password_mismatch', __('Passwords do not match', 'pippin')); } |
That’s all of the checks we need to do, so now we setup all of the recorded errors (if any) in an $errors array:
$errors = pippin_errors()->get_error_messages(); |
If there were not any errors, our $errors array will be empty, which we check like this:
if(empty($errors)) { . . . } |
If there were errors, then the page the user was on when trying to reset the password is reloaded and the error messages are displayed through the pippin_show_errpr_messages() function. If there we not any errors, then we setup an array that contains the user’s data:
$user_data = array( 'ID' => $user_ID, 'user_pass' => $_POST['pippin_user_pass'] ); |
This is just the ID number of the user, pulled from the global $user_ID variable, and the plain text password that the user set with the form.
The wp_update_user() function then takes care of saving the new password to the user’s account for us.
Once the user’s password has been updated, we use the wp_redirect() function to send the user back to the same page they were on before, but with a get variable set that we use to alert the user of the successful update. Note that I’m using the add_query_arg() function to attach our “password-reset” $_GET variable to the redirect url.
Our user has now successfully changed their password.
Displaying and Tracking Errors
If you’ve ready my Creating Front End Registration and Login Forms tutorial, then you are probably familiar with the next two functions. If you have not read that tutorial, I highly suggest you do because I explained in detail how the next two functions work, and I’m not going to go into detail again.
The first function we have is the one that displays any error messages that have been recorded. This function was used in our change password form function.
if(!function_exists('pippin_show_error_messages')) { // displays error messages from form submissions function pippin_show_error_messages() { if($codes = pippin_errors()->get_error_codes()) { echo '<div class="pippin_message error">'; // Loop error codes and display errors foreach($codes as $code){ $message = pippin_errors()->get_error_message($code); echo '<span class="pippin_error"><strong>' . __('Error', 'rcp') . '</strong>: ' . $message . '</span><br/>'; } echo '</div>'; } } } |
Basically this function retrieves any error messages that have been logged with the pippin_errors() function, and then loops through them, displayed each inside of a SPAN tag.
Note that I have wrapped the function in an if(!function_exists()) conditional. Since I used this same function in the other tutorial, this is a good way to prevent possible “function already declared” problems.
Next, we have a simple function that does little more than setup a new instance of the WP_Error class, if it’s not already setup.
if(!function_exists('pippin_errors')) { // used for tracking error messages function pippin_errors(){ static $wp_error; // Will hold global variable safely return isset($wp_error) ? $wp_error : ($wp_error = new WP_Error(null, null, null)); } } |
And that’s it. We can now show a change password form with the [ password_form ] short code and allow our registered users to change their own passwords with a nice interface, instead of having to use the WordPress dashboard.
All Together
function pippin_change_password_form() { global $post; if (is_singular()) : $current_url = get_permalink($post->ID); else : $pageURL = 'http'; if ($_SERVER["HTTPS"] == "on") $pageURL .= "s"; $pageURL .= "://"; if ($_SERVER["SERVER_PORT"] != "80") $pageURL .= $_SERVER["SERVER_NAME"].":".$_SERVER["SERVER_PORT"].$_SERVER["REQUEST_URI"]; else $pageURL .= $_SERVER["SERVER_NAME"].$_SERVER["REQUEST_URI"]; $current_url = $pageURL; endif; $redirect = $current_url; ob_start(); // show any error messages after form submission pippin_show_error_messages(); ?> <?php if(isset($_GET['password-reset']) && $_GET['password-reset'] == 'true') { ?> <div class="pippin_message success"> <span><?php _e('Password changed successfully', 'rcp'); ?></span> </div> <?php } ?> <form id="pippin_password_form" method="POST" action="<?php echo $current_url; ?>"> <fieldset> <p> <label for="pippin_user_pass"><?php _e('New Password', 'rcp'); ?></label> <input name="pippin_user_pass" id="pippin_user_pass" class="required" type="password"/> </p> <p> <label for="pippin_user_pass_confirm"><?php _e('Password Confirm', 'rcp'); ?></label> <input name="pippin_user_pass_confirm" id="pippin_user_pass_confirm" class="required" type="password"/> </p> <p> <input type="hidden" name="pippin_action" value="reset-password"/> <input type="hidden" name="pippin_redirect" value="<?php echo $redirect; ?>"/> <input type="hidden" name="pippin_password_nonce" value="<?php echo wp_create_nonce('rcp-password-nonce'); ?>"/> <input id="pippin_password_submit" type="submit" value="<?php _e('Change Password', 'pippin'); ?>"/> </p> </fieldset> </form> <?php return ob_get_clean(); } // password reset form function pippin_reset_password_form() { if(is_user_logged_in()) { return pippin_change_password_form(); } } add_shortcode('password_form', 'pippin_reset_password_form'); function pippin_reset_password() { // reset a users password if(isset($_POST['pippin_action']) && $_POST['pippin_action'] == 'reset-password') { global $user_ID; if(!is_user_logged_in()) return; if(wp_verify_nonce($_POST['pippin_password_nonce'], 'rcp-password-nonce')) { if($_POST['pippin_user_pass'] == '' || $_POST['pippin_user_pass_confirm'] == '') { // password(s) field empty pippin_errors()->add('password_empty', __('Please enter a password, and confirm it', 'pippin')); } if($_POST['pippin_user_pass'] != $_POST['pippin_user_pass_confirm']) { // passwords do not match pippin_errors()->add('password_mismatch', __('Passwords do not match', 'pippin')); } // retrieve all error messages, if any $errors = pippin_errors()->get_error_messages(); if(empty($errors)) { // change the password here $user_data = array( 'ID' => $user_ID, 'user_pass' => $_POST['pippin_user_pass'] ); wp_update_user($user_data); // send password change email here (if WP doesn't) wp_redirect(add_query_arg('password-reset', 'true', $_POST['pippin_redirect'])); exit; } } } } add_action('init', 'pippin_reset_password'); if(!function_exists('pippin_show_error_messages')) { // displays error messages from form submissions function pippin_show_error_messages() { if($codes = pippin_errors()->get_error_codes()) { echo '<div class="pippin_message error">'; // Loop error codes and display errors foreach($codes as $code){ $message = pippin_errors()->get_error_message($code); echo '<span class="pippin_error"><strong>' . __('Error', 'rcp') . '</strong>: ' . $message . '</span><br/>'; } echo '</div>'; } } } if(!function_exists('pippin_errors')) { // used for tracking error messages function pippin_errors(){ static $wp_error; // Will hold global variable safely return isset($wp_error) ? $wp_error : ($wp_error = new WP_Error(null, null, null)); } } |
Merry Christmas to all. Thanks for the tutorial and code Pippin. Best of luck to you in the new year. Looking forward to your subscription idea.
Merry Christmas to you to, Bill! I’m busy putting together the first batch of premium tutorials for the subscription. It should be coming New Year’s day.
I put all the functions in my function.php file and called the short code, but get nothing. I have tried it logged in and not logged in – still nothing. what could i be doing wrong?
Abby, can you put the complete code into a snippi.com and then paste the link here?
All I did was copy and paste from this website and then call the short code on my page. What code do you want me to send you/
Send the code exactly as it is in your file. It’s possible you missed a line (or that I did).
http://snippi.com/s/6sjlaw5
By the way, your response time is AMAZING!!
oh wait!! is this plugin for changing your password if you are already logged in? I thought it was for if you forgot your password. I have a new password form showing up when I am logged in now.
Ah, that explains the problem 🙂 The code is for a “change” password form, not a “reset” password form.
Bummer. Do you know a way to just send an email to a user with their password. not a reset, just a reminder
You can’t do not, at least not easily. Some of the security features of WordPress (which are good) prevent the sending of plain text passwords, except on first signups.
Bummer, again. Thanks for all your help!!
good
hi..
great tutorial, but i don’t understand where i have to put the code in the “all together” paragraph..
can you say it to me..
THANK’s a lot..!!
It will go in either your theme’s functions.php or a custom plugin.
Great tutorial! It gave me exactly what I needed to accomplish — front-end user password change — on a new site without the need for a plugin. It was also easy to customize for my specific site. THANK YOU!
–Rob Emenecker
Spent hours looking for a solution like this, great work. Thanks a million!
Where do I add in the JS script portion?
There is no JS portion to this tutorial. Could you point out the JS you’re referring to?
shortcode is not work for me.
i put the code in function.php
and create page change password put shortcode in the page.
Hi,
I know, this tutorial is some times old, but I hope you can help me with a little thing. I love this tutorial but I need the option for the user to change his bio on this page too. How can I let the user change this too within this snippet?
thanks and regards
Pare
I have the form integrated on the my-account page. But when I submit, I don’t see any errors (say when I leave the fields black or when they’re different from each other). For some reason, it is not capturing the POST. Any suggestions of where to look?
Thank you for this tutorial. But how can I do to redirect to an other page ? Thanks
Hi,
I tried adding your code by just copy and paste it into my function.php file and using plugin as well. But it didn’t work anyway.
Here is snippi url of code., I copied and tried adding in there
http://snippi.com/s/l6z90m8
What a great tutorial and solution. This works perfectly on our membership driven WordPress sites.
Thanks a million!
Thanks for finally talking about >Change Password Form Short Code – Pippins
Plugins <Liked it!