Develop

WordPress: Send Admin Notification when User Updates Specific Profile Fields

Background

I’ve had some requests in the past from clients who want to be notified when WordPress users change information in their user profile, easy enough for the most part. However, a recent client asked to be notified when specific information was changed, mainly, contact related info. This would include their email address, their phone number and their mailing address. No big deal, or so I thought…

Rename Post

fig 1.0 send mail

The Issue

This simple idea turned out to be not-so-straightforward. Setting up the conditions for notification on email change were easy: compare $old_user_data->user_email to $user->user_email*, if the values are not equal, send an email. Great!

*more about profile updates in the codex.

However, when using this method with the phone field and any of the address fields $old_user_data returns null. Why?! After some exhaustive searching, I found this little bit of information that explained everything:

$old_user_data is an object containing only the basic WP_User fields, but none of the additional user keys (first_name, last_name, etc. are not part of this object).

Ahhhh, ok. Well at least that explains it. So, what to do now? I thought about if for a bit, but no easy solution came to mind. I also wanted to implement a solution that was native to WordPress, without using jQuery etc. I posted the situation to the Advanced WordPress Facebook Group, which if you’re not involved in, you should be!

Within a matter of minutes I had multiple ideas coming in from awesome WordPress folks. One solution in particular stood out to me, it was a suggestion from Nick Ciske, to store the user meta in a transient, and then compare the data. A transient? I had never heard of this before so I looked it up in the codex, behold, awesome: Transient API.

I was super excited, this is what I needed! I went to bed happy knowing that I had a solution to integrate the next day. When I awoke the next morning I had a message from Nick, he had personally taken the code sample I had posted on GitHub and coded the transient portions for me! Wow…what and awesome and selfless thing to do :)

The Solution

Since this is nowhere to be found on the interwebs, below is the solution I came up with including Nick’s awesome code for setting up the transient metadata. There is an email to be sent for each of the following conditions, when they are true:

1. A user’s email is updated
2. A user’s phone number is updated
3. Any of the address related fields are updated

/* ===========================================
    Send Emails when User Profile Changes
   =============================================*/

// IF EMAIL CHANGES
function sr_user_profile_update_email( $user_id, $old_user_data ) {

  $user = get_userdata( $user_id );
  if($old_user_data->user_email != $user->user_email) {
    $admin_email = "you@yourdomain.com";
    $message = sprintf( __( 'This user has updated their profile on the SchoolRise USA Staff Member site.' ) ) . "\r\n\r\n";
    $message .= sprintf( __( 'Display Name: %s' ), $user->display_name ). "\r\n\r\n";
    $message .= sprintf( __( 'Old Email: %s' ), $old_user_data->user_email ). "\r\n\r\n";
    $message .= sprintf( __( 'New Email: %s' ), $user->user_email ). "\r\n\r\n";
    wp_mail( $admin_email, sprintf( __( '[Staff Member Site] User Profile Update' ), get_option('blogname') ), $message );
  }

}

add_action( 'profile_update', 'sr_user_profile_update_email', 10, 2 );

// IF PHONE NUMBER CHANGES
function sr_user_profile_update_phone( $user_id, $old_user_data ) {

  $old_user_data = get_transient( 'sr_old_user_data_' . $user_id );
  $user = get_userdata( $user_id );

  if($old_user_data->phone != $user->phone) {
    $admin_email = "you@yourdomain.com";
    $message = sprintf( __( 'This user has updated their profile on the SchoolRise USA Staff Member site.' ) ) . "\r\n\r\n";
    $message .= sprintf( __( 'Display Name: %s' ), $user->display_name ). "\r\n\r\n";
    $message .= sprintf( __( 'Old Phone: %s' ), $old_user_data->phone ). "\r\n\r\n";
    $message .= sprintf( __( 'New Phone: %s' ), $user->phone ). "\r\n\r\n";
    wp_mail( $admin_email, sprintf( __( '[Staff Member Site] User Profile Update' ), get_option('blogname') ), $message );
  }

}

add_action( 'profile_update', 'sr_user_profile_update_phone', 10, 2 );

// IF ADDRESS CHANGES
function sr_user_profile_update_address( $user_id, $old_user_data ) {

  $old_user_data = get_transient( 'sr_old_user_data_' . $user_id );
  $user = get_userdata( $user_id );

  if($old_user_data->street != $user->street or $old_user_data->city != $user->city or $old_user_data->state != $user->state or $old_user_data->zip != $user->zip) {

    $admin_email = "you@yourdomain.com";
    $message = sprintf( __( 'This user has updated their profile on the SchoolRise USA Staff Member site.' ) ) . "\r\n\r\n";
    $message .= sprintf( __( 'Display Name: %s' ), $user->display_name ). "\r\n\r\n";
    $message .= sprintf( __( 'Old Address: %s, %s, %s %s' ), $old_user_data->street, $old_user_data->city, $old_user_data->state, $old_user_data->zip ). "\r\n\r\n";
    $message .= sprintf( __( 'New Address: %s, %s, %s %s' ), $user->street, $user->city, $user->state, $user->zip ). "\r\n\r\n";
    wp_mail( $admin_email, sprintf( __( '[Staff Member Site] User Profile Update' ), get_option('blogname') ), $message );
  }

}

add_action( 'profile_update', 'sr_user_profile_update_address', 10, 2 );

// Save old user data and meta for later comparison for non-standard fields (phone, address etc.)
function sr_old_user_data_transient(){

  $user_id = get_current_user_id();
  $user_data = get_userdata( $user_id );
  $user_meta = get_user_meta( $user_id );

  foreach( $user_meta as $key=>$val ){
    $user_data->data->$key = current($val);
  }

  // 1 hour should be sufficient
  set_transient( 'sr_old_user_data_' . $user_id, $user_data->data, 60 * 60 );

}
add_action('show_user_profile', 'sr_old_user_data_transient');

// Cleanup when done
function sr_old_user_data_cleanup( $user_id, $old_user_data ){
  delete_transient( 'sr_old_user_data_' . $user_id );
}
add_action( 'profile_update', 'sr_old_user_data_cleanup', 1000, 2 );

Conclusion

In conclusion, code is awesome. Code that works is more awesome. And people in the WordPress community are the most awesome of them all. Thanks again, Nick!

{ 68 Comments }

  1. Keely

    Thought this would be ideal for me but I didn’t get an email – nothing in spam, and Yes, I have changed the 3 email addresses to mine.

    Any ideas?

    Thanks

  2. Bernard Karsten

    Works fine with email changes but not for custom fields. For custom fields, old data comes out blank and new data shows old data.

    The code with custom fields i use:

    // IF PHONE NUMBER CHANGES
    function sr_user_profile_update_phone( $user_id, $old_user_data ) {

    $old_user_data = get_transient( ‘sr_old_user_data_’ . $user_id );
    $user = get_userdata( $user_id );

    if($old_user_data->telefoonnr != $user->telefoonnr) {
    $admin_email = “info@karstengroep.nl”;
    $message = sprintf( __( ‘De onderstaande gebruiker heeft zijn / haar telefoon nummer gewijzigd.’ ) ) . “\r\n\r\n”;
    $message .= sprintf( __( ‘Gebruikersnaam: %s’ ), $user->display_name ). “\r\n\r\n”;
    $message .= sprintf( __( ‘Oud telefoonnummer: %s’ ), $old_user_data->telefoonnr ). “\r\n\r\n”;
    $message .= sprintf( __( ‘Nieuw telefoonnummer: %s’ ), $user->telefoonnr ). “\r\n\r\n”;
    wp_mail( $admin_email, sprintf( __( ‘Een gebruiker heeft zijn telefoonnummer gewijzigd op de website van de ERB’ ), get_option(‘blogname’) ), $message );
    }

    }

    add_action( ‘profile_update’, ‘sr_user_profile_update_phone’, 10, 2 );

    Great script btw!

  3. Sue

    Hi Chris,
    thank you so much for your code-snippet!
    II have adapted it to my request and it works the way I want it!
    Great job!

    • Chris Perryman

      Great news! Happy to help :)

  4. Thank you for sharing this code so many years ago. Email update notification works perfectly still today!

    • Chris Perryman

      You are so welcome! Glad to hear that and I really need to get back to working on this blog haha.

  5. Hi Chris,

    I used your code and got everything working just fine – many thanks. However, using transients does seem to present potential problems if they expire early (“it is possible for the transient to not be available before the expiration time”; “Transients might disappear one second after you set them, or 24 hours, but they will never be around after the expiration time”).

    Why wouldn’t you use the Options API?

    Regards Giles

    • Chris Perryman

      Hi Giles, glad you found this helpful. To be honest, as this solution/post is 4 years old, I can’t recall the reasoning. I seem to remember having the discussion (maybe?) but then ruling it out for one reason or another. There was a lot of back and forth with some other devs on the best way to do this, and at the time, this was the solution that fit.

{ Respond }

Leave a response