WordPress: Send Admin Notification when User Updates Specific Profile Fields


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

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 = "";
    $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 );

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 = "";
    $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 );

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 = "";
    $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 );


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!


  1. Ben

    I’m also using the code which Wayne has kindly posted.

    But unfortunately I’m getting similar errors to Amanda but with added extra ‘Invalid argument supplied for foreach()’ errors on both foreach() statements in the code.

    Anyone got any suggestions?


  2. Amanda

    I was using the code Wayne posted, and I changed the admin email to my own…

    I’m not sure if I got the fields correct, though. I am new to all of this…

    function rc_buddypress_profile_before_update_fields(){

    $user_id = get_current_user_id();

    $user_data = array();

    $user_data[‘Full Name’] = bp_get_profile_field_data(‘field=1’) ;
    $user_data[‘Phone Number’] = bp_get_profile_field_data(‘field=9’) ;
    $user_data[‘Bio’] = bp_get_profile_field_data(‘field=field_125’) ;
    $user_data[‘Amount of Caregiver Experience’] = bp_get_profile_field_data(‘field=2’) ;
    $user_data[‘Desired Hourly Wage’] = bp_get_profile_field_data(‘field=79’) ;
    $user_data[‘Hourly Wage’] = bp_get_profile_field_data(‘field=192’) ;
    $user_data[‘Certifications and Credentials’] = bp_get_profile_field_data(‘field=42’) ;
    $user_data[‘Monday Availability’] = bp_get_profile_field_data(‘field=117’) ;
    $user_data[‘Tuesday Availability’] = bp_get_profile_field_data(‘field=159’) ;
    $user_data[‘Wednesday Evening’] = bp_get_profile_field_data(‘field=180’) ;
    $user_data[‘Thursday Availability’] = bp_get_profile_field_data(‘field=257’) ;
    $user_data[‘Friday Availability’] = bp_get_profile_field_data(‘field=261’) ;
    $user_data[‘Saturday Availability’] = bp_get_profile_field_data(‘field=265’) ;
    $user_data[‘Sunday Availability’] = bp_get_profile_field_data(‘field=269’) ;

    When I inspected the source code the field names were listed as “name=”field_1” if that makes sense. I tried having the field be both “$user_data[‘Friday Availability’] = bp_get_profile_field_data(‘field=261’);” and “$user_data[‘Friday Availability’] = bp_get_profile_field_data(‘field=field_261’) ;”

    Neither way worked.

    Thank you for your quick reply!

    • Chris Perryman

      So, I’m not sure what the issue is…but you need to make sure that things like:

      $user_data['First Name']


      bp_get_profile_field_data('field=First Name')

      are exact matches to the names of the fields you have setup. Off the top of my head I think the field numbers are wrong, you should be using the fields names.

    • Amanda

      Thanks so much for your help and replies. While perusing your site, I found this article and I realized that might be my problem. Almost all of my values are checkboxes… I’ll start figuring out how to do a foreach. Thanks!

    • Amanda

      Sorry to be a nuisance, but even when I make the fields an exact match, I’m not getting the changes I’m looking for… The email simply says that ‘the following changes have been made’ but displays nothing else.

      ` $user_data[‘Full Name’] = bp_get_profile_field_data(‘field=Full Name’) ;
      $user_data[‘Phone Number’] = bp_get_profile_field_data(‘field=Phone Number’) ;
      $user_data[‘Bio’] = bp_get_profile_field_data(‘field=Bio’) ;
      $user_data[‘Amount of Caregiver Experience’] = bp_get_profile_field_data(‘field=Amount of Caregiver Experience’) ;
      $user_data[‘Desired Hourly Wage’] = bp_get_profile_field_data(‘field=Desired Hourly Wage’) ;
      $user_data[‘Hourly Wage’] = bp_get_profile_field_data(‘field=Hourly Wage’) ;
      $user_data[‘Certifications and Credentials’] = bp_get_profile_field_data(‘field=Certifications and Credentials’) ;
      $user_data[‘Monday Availability’] = bp_get_profile_field_data(‘field=Monday Availability’) ;
      $user_data[‘Tuesday Availability’] = bp_get_profile_field_data(‘field=Tuesday Availability’) ;
      $user_data[‘Wednesday Evening’] = bp_get_profile_field_data(‘field=Wednesday Evening’) ;
      $user_data[‘Thursday Availability’] = bp_get_profile_field_data(‘field=Thursday Availability’) ;
      $user_data[‘Friday Availability’] = bp_get_profile_field_data(‘field=Friday Availability’) ;
      $user_data[‘Saturday Availability’] = bp_get_profile_field_data(‘field=Saturday Availability’) ;
      $user_data[‘Sunday Availability’] = bp_get_profile_field_data(‘field=Sunday Availability’) ;

      Any idea? Thank you for your time.

  3. verry usefull post for our development brothers.

  4. Vladimir P

    Which file can I insert this code into and does it need to be after a certain line of code or before certain closing code? This would help us out a great deal. Wish this was a plugin ;) Great work.

    • Vladimir P

      Well i put it into a php file by itself and activated it as a plugin. tried email changes but it still doesn’t send any emails. any recommendations?

    • Chris Perryman

      You can add this to your theme’s functions.php file…just put it in at the bottom.

  5. Hey Chris,

    I ended up having to hire someone to do a bunch of work on my website. In the process I asked them to emulate this code to also allow for email alerts on changes to buddypress fields. It looks like there’s some good code here. Thought you and others might appreciate it.

    function rc_buddypress_profile_before_update_fields(){
    				$user_id = get_current_user_id();
    				$user_data = array();
    				$user_data['First Name'] = bp_get_profile_field_data('field=First Name') ;
    				$user_data['Last Name'] = bp_get_profile_field_data('field=Last Name') ;
    				$user_data['Spouse'] = bp_get_profile_field_data('field=Spouse') ;
    				$user_data['Address Line 1'] = bp_get_profile_field_data('field=Address Line 1') ;
    				$user_data['Address Line 2'] = bp_get_profile_field_data('field=Address Line 2') ;
    				$user_data['City, State Zip'] = bp_get_profile_field_data('field=City, State Zip') ;
    				$user_data['Country'] = bp_get_profile_field_data('field=Country') ;
    				$user_data['City/Locality'] = bp_get_profile_field_data('field=City/Locality') ;
    				$user_data['Zip/Postal Code'] = bp_get_profile_field_data('field=Zip/Postal Code') ;
    				$user_data['Email Address'] = bp_get_profile_field_data('field=Email Address') ;
    				$user_data['Home Phone'] = bp_get_profile_field_data('field=Home Phone') ;
    				$user_data['Office Phone'] = bp_get_profile_field_data('field=Office Phone') ;
    				$user_data['Mobile Phone'] = bp_get_profile_field_data('field=Mobile Phone') ;
    				$user_data['Fax'] = bp_get_profile_field_data('field=Fax') ;
    				$user_data['Fellowship Area'] = bp_get_profile_field_data('field=Fellowship Area') ;
    				$user_data['Area Representative'] = bp_get_profile_field_data('field=Area Representative') ;
    				$user_data['EF Credential'] = bp_get_profile_field_data('field=EF Credential') ;
    	if ( false === ( $value = get_transient( 'sr_old_data_' . $user_id ) ) ) {  // check if already transient created because this hook is being called for every field.
    		set_transient( 'sr_old_data_' . $user_id, serialize($user_data), 60 * 60 );
    function rc_buddypress_profile_update_fields( $user_id ) { 
    		$old_data = unserialize(get_transient( 'sr_old_data_'.$user_id ));  //fetch old data from transient
    		delete_transient( 'sr_old_data_' . $user_id );  //delete transient 
            $admin_email = "";
           	/* count number of changes*/
    	    foreach($old_data as $key => $value){
    			$newvalue = bp_get_profile_field_data('field='.$key);
    			if($value != $newvalue) 
    	    $message = sprintf( __( '%1$s has done following changes:', 'buddypress' ), bp_core_get_user_displayname( $user_id ) ) . "\r\n\r\n";;
    	    foreach($old_data as $key => $value){
    			if($changes == 1)  // if only one change
    				$newvalue = bp_get_profile_field_data('field='.$key);
    				if($value != $newvalue) 
    					$message = sprintf( __( '%1$s has changed '.$key.':', 'buddypress' ), bp_core_get_user_displayname( $user_id ) ) . "\r\n\r\n"; 
    					$message .= sprintf( __( ' Old '.$key.': %1$s', 'buddypress' ), ( $value ) ) . "\r\n\r\n"; 
    					$message .= sprintf( __( ' New '.$key.': %1$s', 'buddypress' ), bp_get_profile_field_data( 'field='.$key ) ) . "\r\n\r\n"; 
    			else  // if more than one change
    				$newvalue = bp_get_profile_field_data('field='.$key);
    				if($value != $newvalue) 
    					$message .= sprintf( __( '-> Old '.$key.': %1$s', 'buddypress' ), ( $value ) ) . "\r\n\r\n"; 
    					$message .= sprintf( __( '   New '.$key.': %1$s', 'buddypress' ), bp_get_profile_field_data( 'field='.$key ) ) . "\r\n\r\n"; 
        wp_mail( $admin_email, sprintf( __( 'EF Members Only User Profile Update' ), get_option('blogname') ), $message );
    add_action( 'xprofile_updated_profile', 'rc_buddypress_profile_update_fields', 10, 5 ); 
    • Hi. Sorry. I thought I wrapped that in code properly…

    • Chris Perryman

      I wrapped it for you…thanks for sharing this!!!!

    • Amanda

      Hi! Thanks so much for sharing this code. I tested it out and got an email back, which is great, but it said:

      “Amanda Joy Smith has done following changes:”

      and then nothing underneath it…

      Any ideas? Thanks so much!

    • Chris Perryman

      What fields did you change to initiate the email?

{ Respond }

Leave a response