1

I set up a front end form for logged in users to update their email address. So far it is working however I want the user, after confirming the email change via link sent to the new address, to be redirected to a custom front-end page.(after the change done in /wp-admin/edit-profile.php).

Thus the user never seeing the backend user profile page.

Here is the code for the form

<form action="<?php the_permalink(); ?>" method="post">
<div class="form-group">
<label for="e-mail">E-mail</label>
<input type="email" class="form-control" value="<?php echo esc_html($current_user->user_email); ?>" name="email" id="email">
<small class="form-text text-muted">If you change this we will send you an email at your new address to confirm it. <strong>The new address will not become active until confirmed.</strong></small>
</div>
<button class="btn btn-primary" type="submit">Change E-mail</button>
</form>

and the form processing

    if (isset( $_POST['email'])) {

// check if user is really updating the value
if ($user_email != $_POST['email']) {       
    // check if email is free to use
    if (email_exists( $_POST['email'] )){
        // email already taken
        echo 'That e-mail address is not available.';
        exit();
    } else {
        $_POST['user_id'] = $current_user->ID;
        send_confirmation_on_profile_email();
        echo 'User update email ink sent to new email for verification.';
    }   
}else{
//same email
echo 'The email you entered is the same as your current email.';
}

}

This is the default code in edit-profile.php that handles the email change. Could I add a redirect here somewhere after email change. Is that a good idea?

    // Execute confirmed email change. See send_confirmation_on_profile_email().
if ( IS_PROFILE_PAGE && isset( $_GET['newuseremail'] ) && $current_user->ID ) {
    $new_email = get_user_meta( $current_user->ID, '_new_email', true );
    if ( $new_email && hash_equals( $new_email['hash'], $_GET['newuseremail'] ) ) {
        $user             = new stdClass;
        $user->ID         = $current_user->ID;
        $user->user_email = esc_html( trim( $new_email['newemail'] ) );
        if ( is_multisite() && $wpdb->get_var( $wpdb->prepare( "SELECT user_login FROM {$wpdb->signups} WHERE user_login = %s", $current_user->user_login ) ) ) {
            $wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->signups} SET user_email = %s WHERE user_login = %s", $user->user_email, $current_user->user_login ) );
        }
        wp_update_user( $user );
        delete_user_meta( $current_user->ID, '_new_email' );
        wp_redirect( add_query_arg( array( 'updated' => 'true' ), self_admin_url( 'profile.php' ) ) );
        die();
    } else {
        wp_redirect( add_query_arg( array( 'error' => 'new-email' ), self_admin_url( 'profile.php' ) ) );
    }
} elseif ( IS_PROFILE_PAGE && ! empty( $_GET['dismiss'] ) && $current_user->ID . '_new_email' === $_GET['dismiss'] ) {
    check_admin_referer( 'dismiss-' . $current_user->ID . '_new_email' );
    delete_user_meta( $current_user->ID, '_new_email' );
    wp_redirect( add_query_arg( array( 'updated' => 'true' ), self_admin_url( 'profile.php' ) ) );
    die();
}

  • eg. if I add something like this wp_redirect(esc_url(site_url('/profile'))) in wp-adin/edit-user.php is that safe and will it be deleted with future updates of wordpress core files? – fmarkovic Dec 14 '19 at 17:08
0

You need to do the processing of the form before headers are sent downstream. Otherwise, if you do it while the body is loading (the way you have it now), then redirecting after a successful email change will result in an error.

A lot of people would use the init action for this; but personally, I like to use template_redirect to hook form processing to. It comes a little later and then the $post object is loaded if you need any data from that.

Here's how I would approach it:

add_action( 'template_redirect', 'my_user_email_update' );
function my_user_email_update() {

    global $error;

    $error = false;

    if ( is_user_logged_in() && isset( $_POST['email'] ) ) {

        // ALWAYS sanitize untrusted input!
        $new_email = sanitize_email( $_POST['email'] );

        // Get the user info for validating that the email is changing
        $user = wp_get_current_user();

        // check if user is really updating the value
        if ( $user->user_email != $new_email ) {

            // Is the new email actually an email address?
            if ( ! is_email( $new_email ) ) {
                $error = 'Please input a valid email address.';
            }

            // check if email is free to use
            if ( email_exists( $new_email ) ) {
                // email already taken
                $error = 'That e-mail address is not available.';

            } else {

                $_POST['user_id'] = $current_user->ID; // This doesn't make sense.

                $success = wp_update_user( array( 'ID'=>$user->ID, 'user_email'=>$new_email ) );

                send_confirmation_on_profile_email();

                // You could redirect here and pass a query string to indicate successful update
            }   
        } else {
            //same email
            $error = 'The email you entered is the same as your current email.';
        }
    }
}

Some things to note:

  1. ALWAYS sanitize input. Instead of retrieving $_POST['email'] each time, put it in a variable and sanitize it. Then use the sanitized result in the variable throughout.
  2. You validate with email_exists() which is good. You should also validate that the posted value is actually a valid email with is_email().
  3. I'm not sure what the $_POST['user_id'] = $current_user->ID is for. Neither of these make sense, so I'm not sure what was intended in that line.
  4. As I mentioned, if you want to redirect, you need to do this before headers are sent, as I have in this example. I operated under the assumption that if there is any type of error, you won't redirect but rather display the error message on the existing form. So there is a global $error and any error result loads into that variable. I initialized it as false so you can check it in the area of your form and if it's not false, display the result.
  • In your example you are modifying the user's email and then sending the email confirmation. I want just the confirmation email to be sent and then modifying the email once the link in the mailbox has been clicked. This takes the user to the backend, to update-user.php. I want the user to be redirected after the change is done from update-user.php to another page eg domain.com/user that I created. How to do this without adding code to edit-user.php? All other comments stand regarding security, I am aware of that, this is in production thou. – fmarkovic Dec 15 '19 at 7:21
  • Ok, I managed to find a hook that does the job (profile_update). Which fires when the email change is done, then I redirect the user to a front-end page. Thus not editing any code in the core file. – fmarkovic Dec 15 '19 at 9:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.