13

I have a custom post type crm, and i need to send a mail after each crm saved or updated. i user cmb2 for some custom meta like subject, to users etc. I know the save_post hook fires after post save (according to WordPress codex) in my case when i call save_post with two parameters (id and post) the post does not contains update values. here is my code :

function send_mail_to_user($id, $post){
    $crm = $post;
    $user_email = array();
    if($crm->vc_all_vc == 'on'){
        $args = array('orderby' => 'display_name');
        $wp_user_query = new WP_User_Query($args);
        $authors = $wp_user_query->get_results();
        if (!empty($authors)) {
            foreach ($authors as $author) {
                array_push($user_email , $author->user_email );
            }
        } 
    }
    else{
        $to_users = $crm->vc_users;
        $to_program = $crm->vc_program;
        $to_group = $crm->vc_group;
        $to_excode = $crm->vc_ex_code;
        foreach ($to_users as $key => $value) {
            $user_data = get_userdata($value);
            array_push($user_email, $user_data->user_email);
        }
        foreach ($to_program as $key => $value) {
            $users = get_users( array('meta_key'     => 'programs'  ) );
            if($users){ 
                foreach ($users as $index => $data) {
                    if(in_array($value , explode('#', $data->programs))){
                        if(! in_array($data->user_email, $user_email)  )
                        {
                            array_push($user_email, $data->user_email);
                        }
                    }
                }
            }
        }
        foreach($to_group as $group) {
            $term = get_term_by('slug', esc_attr($group), 'user-group');
            $user_ids = get_objects_in_term($term->term_id, 'user-group');
            foreach($user_ids as $user_id){
                $fc_user = get_userdata($user_id);
                if(! in_array($fc_user->user_email, $user_email)  )
                {
                    array_push($user_email, $fc_user->user_email);
                }
            }   
        }
        foreach($to_excode as $codes) {
            $value = explode('*',$codes)[1];
            $users = get_users( array('meta_key'     => 'programs'  ) );
            if($users){ 
                foreach ($users as $index => $data) {
                    if(in_array($value , explode('#', $data->programs))){
                        if(! in_array($data->user_email, $user_email)  )
                        {
                            array_push($user_email, $data->user_email);
                        }
                    }
                }
            }   
        }
    }
    foreach($user_email as $index => $email){
        $to      = $email;
        $subject = $crm->vc_subject;
        $body    = $crm->post_content;
        $headers = array(
        'Content-Type: text/html; charset=UTF-8'
        );
        wp_mail($to, $subject, $body, $headers);
    }
}

add_action( 'save_post', 'send_mail_to_user', 10, 2 ); 

And i also try publish_post hook , that works fine when new post created but when updated it works same. I have tried edit_post and post_updated hook also, but i never be able to retrieve my update data.

So how can i solve it? which action hook will give me all the new data? thanks in advance.

M. K Hossain
  • 807
  • 1
  • 12
  • 28
  • My work around for this has been to actually schedule a cron job to check the post meta about 10 seconds after the post has been updated. I have yet to find a hook that I can use that will consistently give me the post object with the updated meta, same problem as yourself – robobobobo Jul 10 '18 at 11:32

8 Answers8

8

You can use something like this,

function your_custom_function($meta_id, $post_id, $meta_key='', $meta_value='') {
    if($meta_key=='_edit_lock') {
        // if post meta is updated
    }
}
add_action('updated_post_meta', 'your_custom_function', 10, 4); 
Abdul Waheed
  • 1,060
  • 9
  • 16
  • Thanks for sharing that! This is what I was looking for as the other actions/filters are firing multiple times. – Mohammed AlBanna Oct 12 '19 at 02:37
  • Since from version 5.6 a `wp_after_insert_post` hook is available. Check this answer: https://stackoverflow.com/a/71773904/1435770 – Djanym Apr 28 '22 at 04:08
4

This might be a bit old but just wanted to give an update since from version 5.6.0 a new hook is available. The hook is wp_after_insert_post and you can find more information here . This hook is triggered after a post is created or updated and all of its terms and meta are updated. You can find an example below:

add_action( 'wp_after_insert_post', 'send_mail_to_user', 90, 4 );

/**
 * Callback to: 'wp_after_insert_post'
 * Fires once a post, its terms and meta data has been saved
 * @param int     $post_id Post ID.
 * @param WP_Post $post    Post object.
 * @param bool    $update  Whether this is an existing post being updated.
 * @param null|WP_Post $post_before Null for new posts, the WP_Post object prior to the update for updated posts.
 *
 */
public static function sync_product_registrations_on_update( $post_id, $post, $update, $post_before ) {

    if ( 'post' !== $post->post_type ) {
        //Only to process the below when post type is 'post' else return
        return;
    }

    if ( ! in_array( $post->post_status, [ 'private', 'publish' ] ) ) {
        //To only process when status is private or publish
        return;
    }

    //The rest of your code goes here


}

Omar Tanti
  • 1,368
  • 1
  • 14
  • 29
3

you can use this save_post hook with your function. change your hook priority to 100 it will give you updated post

add_action( 'save_post', 'send_mail_to_user', 100, 2 );
Sandeep Kumar
  • 619
  • 6
  • 18
3

Try with post_updated and use $post_after object. https://codex.wordpress.org/Plugin_API/Action_Reference/post_updated

  • 1
    Not working for me, it give the update post title and post_content but not update post meta – M. K Hossain Jun 13 '17 at 10:43
  • In this context `$post_after` is an instance of `WP_Post` class, so it does not include custom post meta. Use `$meta = get_post_meta($crm->ID);` in your function to get all post meta fields. – Marian Mircea Jun 13 '17 at 11:19
  • Yeah, i already done that, but the problem is the `meta value` i got is not the update one, probably cmb2 update `meta values` after `post_updated` fires. I am only guessing that. – M. K Hossain Jun 14 '17 at 03:32
  • 1
    Good observation. Try searching the plugin files for `add_action` string, find out what hook it is using to save post meta data, then update your hook with a lower priority. Your function should run after the plugin saves the meta values and hopefully you will get the updated data. – Marian Mircea Jun 14 '17 at 07:25
  • 2
    If you are using Advanced Custum Fields (ACF), the regular WP hooks (including newer ones like rest_after_insert_{$this->post_type} mentioned in pippas answer) won't work, you have to use the ACF hook 'acf/save_post' https://www.advancedcustomfields.com/resources/acf-save_post/ – makkabi Aug 25 '21 at 14:21
  • A `wp_after_insert_post` hook is available since version 5.6. Check this answer: https://stackoverflow.com/a/71773904/1435770 – Djanym Apr 28 '22 at 04:10
1

You can use the rest_after_insert_{$this->post_type} hook (where $this->post_type is replaced with the post type, eg 'post' or 'myposttype').

Thanks to Florian Brinkmann for this link.

add_action('rest_after_insert_myposttype', 'myfunction', 10, 3);

function myfunction($post, $request, $creating) {
   // $creating is TRUE if the post is created for the first time,
   // false if it's an update

   //  ...
}

See also here.

Emma
  • 27,428
  • 11
  • 44
  • 69
Pippa
  • 19
  • 4
1

Some workaround is to use $_POST['meta_field'] with sanitation:

$to_users = $_POST['vc_users'];
$to_program = $_POST['vc_program'];
$to_group = $_POST['vc_group'];
$to_excode = $_POST['vc_ex_code'];

$to_users = sanitize_text_field($to_users);
$to_program = sanitize_text_field($to_program);
$to_group = sanitize_text_field($to_group);
$to_excode = sanitize_text_field($to_excode);

Pay attention to the field names, using ACF will make you use the field key.

This problem is more complicated than seems on first sight:

Our 'post_updated' hook is running before post is updated, so every attempt for getting meta data will result with the previous data. Also, Wordpress (v5.7.2) doesn't seem to have a hook for after a post was saved.

Also, 'save_post' hook is very complicated because it runs for every post saved or created including revisions and the $update boolean is still not reliable enough.

1

The correct and simpler answer is to use the wp_insert_post action.

https://developer.wordpress.org/reference/hooks/wp_insert_post/

An important distinction of wp_insert_post action is that it is fired after update_post_meta has been called.

There are 3 parameters available - the $update flag tells you if this is a new or updated post.

do_action( 'wp_insert_post', int $post_ID, WP_Post $post, bool $update )

So - to implement your code after all post meta has been updated, use something like this:

add_action('wp_insert_post', 'run_after_post_updated', 10, 3);    
function run_after_post_updated($post_ID, $post, $update ) {
   //  ...
}
Dave Hilditch
  • 5,299
  • 4
  • 27
  • 35
  • A `wp_after_insert_post` hook is available since version 5.6. Check this answer: https://stackoverflow.com/a/71773904/1435770 – Djanym Apr 28 '22 at 04:11
  • 1
    Sure - and it's in the same position as wp_insert_post except that wp_after_insert_post may not fire, depending on parameters passed to the function wp_insert_post. See https://i.imgur.com/Ou74dKy.png – Dave Hilditch May 04 '22 at 13:35
0

You can use the save_post action wih a higher priority so that your function is called afer all meta data has been saved.

add_action( 'save_post',  'action_woocommerce_update_product', 20, 3 );
Here I have used higher priority 20