0

I'm writing a post approval utility for the front end, because we don't want our editor to be able to access the admin dashboard. There are questions on Stack nearly identical to what I'm trying to accomplish, but that guy's problem has to do with the infinite loop issue. Otherwise, he claims his code works:

How to change post status from publish to draft using hook in wordpress?

I'm writing a plugin to do this (in my case I want to change 'draft' to 'publish'). I don't want to have to edit the functions.php in the child theme, because I want the plugin to be standalone, for any WordPress site. So all code is in my plugin's main php file.

  • I can get all posts with status of 'draft'.
  • I can use an html checkbox, javascript, and WP's ajax functions to select the post I want to change by ID.

The "change status" code looks like this (in the plugin main php file):

/* This function should toggle a post status from 'draft' to 'publish' */
add_action( 'wp_ajax_approvepost', 'approvepost_callback' );
add_action( 'wp_ajax_nopriv_approvepost', 'approvepost_callback' );
function approvepost_callback(){
    $post = array( 
                    'ID' => $_POST['postid'], // e.g., 28
                    'post_status' => 'publish',
                    'edit_date' => true
                );
    $result = wp_update_post($post,true); // (there, you like that better?)
    echo $result; // I get a result of "1". The post ID is 28. wp_update_post failed?
    wp_die(); 
}

... wp_update_post returns a 1, so I take that to mean there were no errors. But when I refresh the front-end page, the post shows up again, meaning it is still in draft mode, and when I inspect the post in the WP admin dashboard, sure enough, it's still in draft. What am I doing wrong or missing?

UPDATE: If I execute the function from my child theme functions.php, the code works, but not by using AJAX. I have to call the function directly, like by refreshing the page, and the function returns the post ID, and the post status will change to 'publish'. As soon as I invoke the function using AJAX, the post status does not change, and the function returns a 1.

UPDATE I removed the AJAX action altogether, and defaulted to an old timey standard form submit, which refreshes the form page. Under these conditions, wp_update_post works as expected. Something about wp_update_post doesn't like to be called by AJAX. My suspicion is it has something to do with security, but whatever it is, I can't find any reference to it in the documentation.

ADDITIONAL JAVASCRIPT Some have asked to see the javascript, especially the ajax code:

jQuery(document).ready(function($){

    // AJAX url
    var ajax_url = approve_posts_ajax_object.ajax_url;
    var data = {
                 'action': 'approvepost',
                };

    $('button#submit-approval').on("click",function(){

        var checkboxes = $("input[name='post_id']");
        data.postid = [];
        // loop over them all
        for (var i=0; i<checkboxes.length; i++) {
            // And stick the checked ones onto an array...
            if (checkboxes[i].checked) {
                data.postid[i] = $(checkboxes[i]).val();
            }
            else{
                data.postid[i] = "";
            }
        }

        $.ajax({
            url: ajax_url,
            type: 'post',
            data: data,
            dataType: 'json',
            success: function(response){
              // Add new rows to table
              approvePost(response);
            }
        });    
    });
});

function approvePost(data){
    jQuery("#diag").html(data); //just want to output the callback response for now.
}

SOLUTION (so that others can see what works):

Javascript, with AJAX call:

jQuery(document).ready(function($){

    // AJAX url
    var ajax_url = approve_posts_ajax_object.ajax_url;
    var data = {
                 'action': 'approvepost',
                };

    $('button#approval_submit').on("click",function(){

        var checkboxes = $("input[name='post_id']");
        data.postid = [];
        // loop over them all
        for (var i=0; i<checkboxes.length; i++) {
            // And stick the checked ones onto an array...
            if (checkboxes[i].checked) {
                data.postid[i] = $(checkboxes[i]).val();
            }
        }

        $.ajax({
            url: ajax_url,
            type: 'post',
            data: data,
            dataType: 'json',
            success: function(response){
              approvePost(response);
            }
        });    
    });
});

function approvePost(data){
    jQuery("#diag").html("");
    var string = "\n";
    jQuery.each(data,function(k,v){
        string += k + " = " + v + "<br>";
    });
    jQuery("#diag").append(string);
    
}

PHP (with ajax callback function, in main plugin file):

/* This is the function that toggles a post status from draft to published */
add_action( 'wp_ajax_approvepost', 'approvepost_callback' );
add_action( 'wp_ajax_nopriv_approvepost', 'approvepost_callback' );
function approvepost_callback(){

    $update = array();
    for($i=0;$i<count($_POST['postid']);$i++){
            $post = array( 
                'ID' => $_POST['postid'][$i],
                'post_status' => 'publish'
            );
            $update[$i] = wp_update_post($post);
    }
    echo json_encode($update);    
    wp_die(); 
}
8
  • 2
    The docs clearly say that wp_update_post returns the post ID on success, so unless your ID is 1, then something is awry there. Try checking for a wp_error.
    – vancoder
    Jun 2, 2021 at 22:17
  • 1
    Assigning values to $_POST is unusual and likely to cause fatal errors on newer versions of PHP. It also means everything passed into the AJAX endpoint also gets returned which could lead to security issues. Was there a reason not to use the Posts endpoint of the REST API for your AJAX? This code would allow any post to be published bypassing capability security checks
    – Tom J Nowell
    Jun 2, 2021 at 22:25
  • @vancoder The post ID is 28, so I guess it did not succeed.
    – TARKUS
    Jun 2, 2021 at 22:35
  • 1
    @TARKUS, "My suspicion is it has something to do with security" - maybe, and maybe not. And FYI, your code worked fine for me, both when logged-in and not. So I suggest you to replace that echo $result with var_dump( $result ) and try again making your AJAX call, then see what the dump outputted. And I also agreed with @HowdyMcGee, so can we see your AJAX/JS script? Did you try checking the console for any errors?
    – Sally CJ
    Jun 4, 2021 at 1:35
  • 1
    @SallyCJ , No matter whether I use var_dump, echo, or print_r, using the ajax callback, it only returns "1". The AJAX sends the entire POST object to the callback function. That was why I echo json_encode($_POST), to see what was operated on in the callback function. Also, I tried this plugin on my localhost and a remote site with https. Same results. I will add my js to my question for all who are interested. I ditched ajax for now, and the plain-jane html form submit works, until I can solve the ajax problem. B.T.W., I also tried the wp_error, which returned nothing.
    – TARKUS
    Jun 4, 2021 at 12:05

2 Answers 2

1

Something about wp_update_post doesn't like to be called by AJAX.

No, there is no such thing. :)

My suspicion is it has something to do with security

And I believed that also wasn't the case, hence I asked for your JS code.

And the actual problem is that the POST parameter postid (i.e. $_POST['postid']) is actually an array of post IDs, so you can't simply do 'ID' => $_POST['postid']. And regarding this: "wp_update_post returns a 1", it's because the function calls get_post() and it's actually the one which returned 1. So that also means, the post that was updated is the one with that ID, i.e. 1 (which is the "Hello world!" post in a default WordPress install).

So you need to do a foreach then call wp_update_post() to update each of the posts. Example based on your code:

function approvepost_callback() {
    $data = array();

    foreach ( (array) $_POST['postid'] as $post_id ) {
        if ( empty( $post_id ) || ! is_numeric( $post_id ) ) {
            continue;
        }

        $post = array(
            'ID'          => $post_id,
            'post_status' => 'publish',
            'edit_date'   => true // what is edit_date?
        );

        $result = wp_update_post( $post, true );
        if ( $result && ! is_wp_error( $result ) ) {
            $data[] = $post_id;
        }
    }

    // In your JS, you set dataType to 'json', so make sure to send a valid JSON response.
    wp_send_json( $data ); // this function will call wp_die()
}

Some JavaScript Notes

  1. You don't need the following else because it will add empty items to the array (data.postid):

    else{
        data.postid[i] = "";
    }
    
  2. I would ensure the above array is not empty before proceeding with the AJAX ($.ajax()) call:

    if ( data.postid.length < 1 ) {
        console.log( 'no selections' );
        return;
    }
    
    $.ajax( ... your code here. );
    

And last but not least... you should consider using the REST API instead to update your posts (see the Update a Post endpoint), because among other good things, the REST API has a better error handling and for example if you specified an invalid/non-existent post ID, the response would look like so which tells what exactly went wrong: {code: "rest_post_invalid_id", message: "Invalid post ID.", data: {status: 404}} — better than simply 0 or 1, right? :)

4
  • Yikes! 'ID' => $_POST['postid'] ... that looks like a boo boo! Will test, good eye (also on the else). I'm actually re-tooling to use REST API. My reservations are two: 1) It seems inelegant to use REST and cUrl on the same website; and 2) it seems like it should be better practice to use WP's built in core functions (wp_update_post). No?
    – TARKUS
    Jun 4, 2021 at 14:45
  • Solved! I wasn't passing a post_id array from my ajax call to my php callback function, as you indicated. Whewww!
    – TARKUS
    Jun 4, 2021 at 15:31
  • 1
    "inelegant to use REST and cUrl on the same website" - why cURL? I mean, you can use JS to retrieve/update/delete a post via the REST API, even from remote sites. In fact, it's a core feature in WordPress and is the foundation of the block editor. So because the REST API is a core feature, then of course it uses core WordPress functions like wp_update_post() and wp_insert_post(). :) Except that, as of writing, bulk post updates (via a single request) is not yet implemented..
    – Sally CJ
    Jun 4, 2021 at 15:59
  • 1
    But anyway, please see the REST API handbook for more details on how the API works, how to use it to update/delete/etc. a resource (like a post), etc. PS: When you said, "I wasn't passing a post_id array", I think you meant "I was passing a post_id array"?.. =)
    – Sally CJ
    Jun 4, 2021 at 16:06
1

This may be a weird answer since I think it has nothing to do with the ajax.... try clearing the cache memory or install 1 and purge the cache also 1s more save the permalink with post name selected

1
  • I will try it and report back to you. As for browser cache, I clear it often on localhost, in order to see my stylesheet changes. As for WordPress cache, I don't have that enabled. Thanks.
    – TARKUS
    Jun 4, 2021 at 14:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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