0

I have a custom post type called "Envios". In it I've created a Custom Meta box which allows to select users and posts. When one of this custom post types is published it sends an email to the selected users with the content of the selected posts. To select the posts I use the ACF plugin, with the field Post Object.

The issue is that sometimes the emails are sent properly but in most cases nothing is delivered.

Here is the code:

    // Adds Custom Meta Box
    function email_delivery_munda_add_custom_box() {
        $screens = ['envios', 'md_cpt'];
        foreach ($screens as $screen) {
            add_meta_box(
                'md_email_delivery_box_id',           // Unique ID
                'Detalles de email',                  // Box title
                'md_email_delivery_custom_box_html',  // Content callback, must be of type callable
                $screen,                              // Post type
                'side',
                'default'
            );
        }
    }
    add_action('add_meta_boxes', 'email_delivery_munda_add_custom_box');

    // Adds the content of the Custom Meta Box
    function md_email_delivery_custom_box_html($post)
    {

        $asunto = get_post_meta($post->ID, 'email_delivery_asunto', true);

        wp_nonce_field( 'save_md_email', 'md_email_nonce' );

        if ($asunto == "") {
            $asunto = "Informe - " . date("d-m-Y");
        }
        ?>
        <div class="email-delivery-asunto">
          <label for="email_delivery_asunto">Asunto</label>
          <input type="text" name="email_delivery_asunto" id="email_delivery_asunto" class="postbox"  value=" <?php echo $asunto ?>" placeholder="Informe - <?php echo date("d-m-Y") ?>">
        </div>

        <div class="email-delivery-receptores">
            <label for="select-emails">Destinatarios</label>
            <?php
            $blogusers = get_users();
            // Array of WP_User objects.
            echo '<select name="clients-email-input[]" id="select-emails" class="admin-email-receptores" multiple="multiple"><option></option>';
            foreach ( $blogusers as $user ) {
              echo '<option value="'. esc_html( $user->user_email ) .'">' . esc_html( $user->display_name ) . '</option>';
            }
            echo '</select>';
            ?>
        </div>

        <?php $enviados = get_post_meta($post->ID, 'email_send', true); ?>

        <div class="email-delivery-asunto">
          <label for="email_send">Ultimo envio</label>
          <p id="email_send"><?php echo $enviados ?></p>
        </div>

        <?php

    }

    // Saves post data
    function md_email_save_postdata($post_id) {

        if ( ! isset( $_POST['md_email_nonce'] ) ) {
            return $post_id;
        }

        if ( ! wp_verify_nonce( $_POST['md_email_nonce'], 'save_md_email' ) ) {
            return $post_id;
        }

        $asunto = sanitize_text_field( $_POST['email_delivery_asunto'] );
        update_post_meta( $post_id, 'email_delivery_asunto', $asunto );

    }
    add_action('save_post', 'md_email_save_postdata');


    // Sends an email when custom post type Envios is published
    function md_email_publish_postdata($post_id) {

        $asunto = sanitize_text_field( $_POST['email_delivery_asunto'] );

        $headers = array(
            'Content-Type: text/html; charset=UTF-8'
        );

        $enviados = sanitize_text_field( $_POST['email_send'] );
        $blogusers = get_users();

        foreach ($_REQUEST['clients-email-input'] as $selectedOption) {
          array_push($headers, "BCC:" . $selectedOption);

          foreach ( $blogusers as $user ) {
            if ($user->user_email === $selectedOption) {
                $enviados .= "<b>" . esc_html( $user->display_name ) . "</b> " . esc_html( $user->user_email) . "<br>";
            }
          }

        }

        update_post_meta( $post_id, 'email_send', $enviados );

        $content = '';

        // Removing from here makes it work.
        $posts = get_field('casos',$post_id);

        if( $posts ): ?>
            <?php foreach( $posts as $post): // variable must be called $post (IMPORTANT) ?>
                <?php setup_postdata($post);

                $content .= '<h1>' . get_the_title( $post ) . '</h1><br>';
                $content .= '<p>' . get_field( 'descripcion_breve', $post) . '</p><br>';

            endforeach; ?>
            <?php wp_reset_postdata(); // IMPORTANT - reset the $post object so the rest of the page works correctly ?>
        <?php endif;
        // until here.


        $clients_list = "xxxxxxxx@email.com";

        wp_mail( $clients_list, $asunto , $content, $headers );

    }
    add_action('publish_envios', 'md_email_publish_postdata', 10, 2 );

IF I remove the ACF part from // Removing from here makes it work. to // until here. the emails are sent okay every time but without the selected posts content.

I can't really tell which is the problem, but maybe you can impart some wisdom on the matter. Thanks in advance!


SOLUTION:

ACF use the action acf/save_post AFTER the post is published, so I was trying to get a value that wasn't there yet. So I changed the action to the acf save_post and between it I use if (get_post_status($post_id) == 'publish') { to check the status of the post.

Furthermore I update the Object Post output as follows:

  $posts = get_field( 'casos', $post_id );

    if( $posts ):
        foreach( $posts as $post):

            $content .= '<h1>' . get_the_title( $post ) . '</h1><br>';

            if ( get_field( 'descripcion_breve', $post->ID )) {
                $content .= '<p>' . get_field( 'descripcion_breve', $post->ID ) . '</p><br>';
            } else {
                $content .= '<p>Description empty</p><br>';
            }

        endforeach;
    endif;
  • 1
    setup_postdata and wp_reset_postdata are for front-end queries, they won't work reliably (or at all) on most admin pages. – Milo Oct 18 '18 at 3:22
  • ACF use the action acf/save_post AFTER the post is published - that is not entirely correct, it depends on the priority given. If the priority is less than 10, then it is called before the post save, but if it is greater than 10, it is called after the post is saved. Check the official documentation. – dboris Oct 18 '18 at 19:47
0

...Removed my prior 'solutions'...


So, the real problem here is that this function is hooked to publish_post action, at which point the post might not be saved yet, if it is a new post. Only if it is a post already saved but with some other status, we can access the ACF values using get_field().

Solution could be to use a hook acf/save_post with priority of 10 or larger, since it fires after the post has been saved, therefore allowing us to get the values using get_field().

This is a short example of usage of this hook:

function my_acf_save_post( $post_id ) {

    // bail if wrong post type
    if( get_post_type( $post_id ) !== 'envios' ) {
         return;
    }

    // get new value
    $posts = get_field( 'casos', $post_id );


    // do something

}

add_action('acf/save_post', 'my_acf_save_post', 20);
  • My friend, thanks a lot for the help, The issue remains but the results are: -With the first option the email is sent, but it fails to get the values from the ACF fields, $content value remains empty (or ' ' ). One thing I tested is what happens if I update the value $content just after " if( $posts ): " and the value doesn't update either so I'm not connecting properly with the object post field, can't say why though.. -On the second option the email is not delivered at all. – Nicolas Landgraf Oct 17 '18 at 20:59
  • I've tried debugging as you suggested, on debug.log I found an error with a line of code higher yet after solving it the results were the same and I didn't get any other error. I'm going to keep testing and maybe try to reach the ACF people for help in the matter. Again, thanks a lot for the help!! – Nicolas Landgraf Oct 17 '18 at 21:00
  • @NicolasLandgraf I updated my second option, function get_field() does not accept the post object, but only the post ID. Also, there is yet another option for debugging we can try. Write this line var_dump( get_field( 'casos', $post_id ) ); die; somewhere inside your function. Hopefully, that will output either the posts array, or the error. – dboris Oct 17 '18 at 21:09
  • I've added var_dump( get_field( 'casos', $post_id ) ); die; as you asked, I put it inside the publish_envios add action. When I publish the post the whole page goes white and It says "NULL". I guess it means that the array of post objects always returns empty. As I'm testing this inside the admin pages I'm using almost the same code in single_envios.php and the values appear okay (just to be sure that I'm not writing the wrong field name and such things). The only difference between codes is that in single_envios.php I output $content with echo $content; Same result with both options. – Nicolas Landgraf Oct 18 '18 at 14:10
  • 1
    My friend, i think I figured out the issue, I'm currently testing but I guess that's it. ACF use the action acf/save_post AFTER the post is published, so I was trying to get a value that wasn't there yet. So I changed the action to the acf save_post and between it I use if (get_post_status($post_id) == 'publish') { to check the status of the post. My guess is that it sometimes worked because I might have publish a post - then change to draft and publish it again, sending the old values. So my former questions where misleading, but debugging with your ideas was the key! Thanks a lot! – Nicolas Landgraf Oct 18 '18 at 15:43

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.