2

I'm using the group of functions below to create a checkbox in the Quick Edit area of each post in the post listing at wp-admin/edit.php that saves a value to a custom field called headline_news.

The issue is when the checkbox is checked, Quick Edit saves the value, but it won't save again when I uncheck a checked box. Via PHPMyAdmin, the custom field value changes in the database when saving a checked box, but does not change when unchecking a box and saving.

The first three functions are fairly standard to add a column, echo contents of the column and printing the checkbox. I'm guessing the issue with not saving an unchecked box must be in the saving function on in the Javascripts.

// Add column to posts listing

add_filter( 'manage_post_posts_columns', 'add_columns' );
function add_columns( $columns ) {

    $columns['headline_news'] = 'Headline news';
    return $columns;
}


// Echo contents of custom field in column

add_action( 'manage_posts_custom_column', 'columns_content', 10, 2 );
 function columns_content( $column_name, $post_id ) {

    if( $column_name == 'headline_news' ) {
        $headline_news = get_post_meta( $post_id, 'headline_news', true );
        echo $headline_news ;
}
}



// Print checkbox in Quick Edit

add_action( 'quick_edit_custom_box', 'quick_edit_add', 10, 2 );
function quick_edit_add( $column_name, $post_type ) {

    printf( '
        <input type="checkbox" name="headline_news" class="headline_news"> %s',
        'Headline news position'
    );
}



// Save checkbox value

add_action( 'save_post', 'qedit_save_post', 10, 2 );
function qedit_save_post( $post_id, $post ) {

    // pointless if $_POST is empty (this happens on bulk edit)
    if ( empty( $_POST ) )
        return $post_id;

    // verify quick edit nonce
    if ( isset( $_POST[ '_inline_edit' ] ) && ! wp_verify_nonce( $_POST[ '_inline_edit' ], 'inlineeditnonce' ) )
        return $post_id;

    // don't save for autosave
    if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
        return $post_id;

    // dont save for revisions
    if ( isset( $post->post_type ) && $post->post_type == 'revision' )
        return $post_id;

     // Save only the custom field

            $custom_fields = array( 'headline_news' );

            foreach( $custom_fields as $field ) {

                if ( array_key_exists( $field, $_POST ) )
                    update_post_meta( $post_id, $field, $_POST[ $field ] );

            }
}





//  Javascript functions to set/update checkbox

add_action( 'admin_footer', 'quick_edit_javascript' );
 function quick_edit_javascript() {

    global $current_screen;
    if ( 'post' != $current_screen->post_type )
    {
        return;
    }
    ?>
    <script type="text/javascript">
        function checked_headline_news( fieldValue )
        {
            inlineEditPost.revert();
            jQuery( '.headline_news' ).attr( 'checked', 0 == fieldValue ? false : true );
        }
    </script>
    <?php
}


add_filter( 'post_row_actions', 'expand_quick_edit_link', 10, 2 );
function expand_quick_edit_link( $actions, $post ) {

    global $current_screen;

    $data = get_post_meta( $post->ID, 'headline_news', true );
    $data = empty( $data ) ? 0 : 1;
    $actions['inline hide-if-no-js']    = '<a href="#" class="editinline" title="';
    $actions['inline hide-if-no-js']    .= esc_attr( 'Edit this item inline' ) . '"';
    $actions['inline hide-if-no-js']    .= " onclick=\"checked_headline_news('{$data}')\" >";
    $actions['inline hide-if-no-js']    .= 'Quick Edit';
    $actions['inline hide-if-no-js']    .= '</a>';

    return $actions;
}

Edit 1/23/17

Changing part of the save function to this now works:

    // Save only the custom field

   if (isset($_POST['headline_news'])) {

                    update_post_meta( $post_id, 'headline_news', 'yes' );

                        } else {

                    delete_post_meta( $post_id, 'headline_news' );

                }

It allows me to save and delete the custom field from quick edit. But, when I edit a post in the full post editor - edit text, change category, etc. - the custom field is deleted. So why is that happening? How do I keep saving in the full post editor from changing this meta field?

And: why are there two checkbox fields in quick edit?:

enter image description here

7
  • This is actually related to how HTML handles checkboxes in forms. If the box is checked, the $_POST['headline_news'] is populated. But if not checked, the field is not included in the posted data. That's why it doesn't get triggered. One way around this might be radio buttons. Another might be javascript to set a hidden field when the radio box is unchecked.
    – Joel
    Commented Jan 8, 2017 at 20:51
  • Thanks, I'm not clear myself what $_POST[ $field ] does anyway; the save function is cribbed from another example of using Quick Edit. I can probably delete the foreach, too, as there is only one custom field. I'm using a checkbox because I eventually need to change the save function to delete_post_meta on save if the box is unchecked; I need to query posts on the existence of the custom field, not its content, the end result being so I don't have to add that custom field to thousands of posts already in a database. Commented Jan 8, 2017 at 21:17
  • $_POST[$field] is an array of all the data submitted by the form. I'd simplify your code to get rid of that foreach. Then have if (isset($_POST['headline_news'])) {update_post_meta( $post_id, 'headline_news', <value if checked, which I think is 1> );} else {update_post_meta( $post_id, 'headline_news', <value if unchecked, which I think is 0> );}
    – Joel
    Commented Jan 19, 2017 at 2:20
  • Thanks, I'll test this. I was about to get back to my Q, as I need this to work. Commented Jan 20, 2017 at 16:41
  • Joel, thanks, that now works, but something else now gives me a different issue, outlined in my edit. Commented Jan 23, 2017 at 20:58

1 Answer 1

3
+50

Why the headline_news meta data is deleted on the Edit Post screen, with a fix

The qedit_save_post() function stomps on headline_news because $_POST['headline_news'] is not set when using the post edit screen.

Since the custom fields editor is being used for headline_news and there is no custom meta box involved, we'll let the built-in custom field editor handle the saving of the meta data and deliberately NOT fire the quick edit saving logic when using the Edit Post screen. E.g.:

// Handle saving of headline_news via Quick Edit. This code will not fire on the post edit screen.
// The post edit screen will handle the field via the custom field editor.
if ( isset( $_POST[ '_inline_edit' ] ) && wp_verify_nonce( $_POST[ '_inline_edit' ], 'inlineeditnonce' ) ) {
    if ( isset( $_POST['headline_news'] ) ) {   
        update_post_meta( $post_id, 'headline_news', 'yes' );
    } else {
        delete_post_meta( $post_id, 'headline_news' );
    }                   
}

The duplicated Headline news position checkbox and fix

Initially I was unable to reproduce the phenomenon where you were getting a duplicate Headline news position checkbox, but after further review, I noticed that the original code was not checking $column_name in the quick_edit_add() function. The quick_edit_custom_box action is called once for each custom column, so I presume that there are actually multiple custom columns being added to your post listings and this code was not part of the original question (the columns could be added via plugins or the theme). We can add a check for the appropriate column to ensure that the output is only rendered once for each column:

// Print checkbox in Quick Edit for each custom column.
add_action( 'quick_edit_custom_box', 'quick_edit_add', 10, 2 );
function quick_edit_add( $column_name, $post_type ) {
    // Handle the headline_news checkbox
    
    // Note the added check. This prevents the output from being
    // rendered for every custom column & allows us to handle each individual column.
    switch ( $column_name ) {
        
        case 'headline_news' :
            printf( '<input type="checkbox" name="headline_news" class="headline_news"> %s',
                    __( 'Headline news position', 'text-domain' )
            );
        break;
        
        // Example of how another column could be incorporated:
        //case 'another_column' :
            //printf( '<input type="checkbox" name="another_column" class="another_column"> %s',
            //      __( 'Another Column', 'text-domain' )
            //);
        //break;        
    }
}

The full working solution:

Here is the original code with the fix for the edit post screen mentioned above, the fix for the duplicated Headline news position checkbox, and a couple of minor tweaks mainly related to WordPress coding standards.

// Add column to posts listing
add_filter( 'manage_post_posts_columns', 'add_columns' );
function add_columns( $columns ) {
    // Add the Headline News custom column.
    $columns['headline_news'] = __( 'Headline news', 'text-domain' );
    
    // Perhaps add other custom columns too.
    // $columns['another_column'] = __( 'Another Column', 'text-domain' );
    
    return $columns;
}

// Echo contents of custom field in column
add_action( 'manage_posts_custom_column', 'columns_content', 10, 2 );
function columns_content( $column_name, $post_id ) {
    switch ( $column_name ) {
        
        case 'headline_news' :
            $headline_news = get_post_meta( $post_id, 'headline_news', true );
            echo esc_html( $headline_news );
        break;
    
        // Example of how another column could be incorporated:
        //case 'another_column' :
        //  $another_column = get_post_meta( $post_id, 'another_column', true );
        //  echo esc_html( $another_column );
        //break;
    }
}

// Print checkbox in Quick Edit for each custom column.
add_action( 'quick_edit_custom_box', 'quick_edit_add', 10, 2 );
function quick_edit_add( $column_name, $post_type ) {
    // Handle the headline_news checkbox
    
    // Note the added check. This prevents the output from being
    // rendered for every custom column & allows us to handle each individual column.
    switch ( $column_name ) {
        
        case 'headline_news' :
            printf( '<input type="checkbox" name="headline_news" class="headline_news"> %s',
                    __( 'Headline news position', 'text-domain' )
            );
        break;
        
        // Example of how another column could be incorporated:
        //case 'another_column' :
            //printf( '<input type="checkbox" name="another_column" class="another_column"> %s',
            //      __( 'Another Column', 'text-domain' )
            //);
        //break;        
    }
}

// Save checkbox value
add_action( 'save_post', 'qedit_save_post', 10, 2 );
function qedit_save_post( $post_id, $post ) {
    
    // pointless if $_POST is empty (this happens on bulk edit)
    if ( empty( $_POST ) ) {
        return $post_id;
    }
    
    // Ensure quick edit nonce is set.
    if ( empty( $_POST[ '_inline_edit' ] ) ) {
      return $post_id;
    }

    // Verify quick edit nonce
    if ( ! wp_verify_nonce( $_POST[ '_inline_edit' ], 'inlineeditnonce' ) ) {
        return $post_id;
    }

    // Don't save for autosave
    if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
        return $post_id;
    }

    // dont save for revisions
    if ( isset( $post->post_type ) && 'revision' === $post->post_type ) {
        return $post_id;
    }

    // Handle saving of headline_news via Quick Edit. This code will not fire on the post edit screen.
    // The post edit screen will handle the field via the custom field editor.
    if ( isset( $_POST[ '_inline_edit' ] ) && wp_verify_nonce( $_POST[ '_inline_edit' ], 'inlineeditnonce' ) ) {
        if ( isset( $_POST['headline_news'] ) ) {   
            update_post_meta( $post_id, 'headline_news', 'yes' );
        } else {
            delete_post_meta( $post_id, 'headline_news' );
        }                   
    }
}

// JavaScript functions to set/update checkbox
add_action( 'admin_footer', 'quick_edit_javascript' );
function quick_edit_javascript() {
    global $current_screen;
    if ( 'post' !== $current_screen->post_type ) {
        return;
    } ?>
    <script type="text/javascript">
        function checked_headline_news( fieldValue ) {
            inlineEditPost.revert();
            jQuery( '.headline_news' ).attr( 'checked', 0 == fieldValue ? false : true );
        }
    </script><?php
}

add_filter( 'post_row_actions', 'expand_quick_edit_link', 10, 2 );
function expand_quick_edit_link( $actions, $post ) {
    global $current_screen;
    $data = get_post_meta( $post->ID, 'headline_news', true );
    $data = empty( $data ) ? 0 : 1;
    $actions['inline hide-if-no-js']  = '<a href="#" class="editinline"';
    $actions['inline hide-if-no-js'] .=    ' title="' . esc_attr( __( 'Edit this item inline', 'text-domain' ) ) . '"';
    $actions['inline hide-if-no-js'] .=    " onclick=\"checked_headline_news('{$data}')\" >";
    $actions['inline hide-if-no-js'] .=   __( 'Quick Edit', 'text-domain' );
    $actions['inline hide-if-no-js'] .= '</a>';

    return $actions;
}
3
  • 1
    Thanks, this works really well, and I appreciate the comments as to where I can expand the functions. I did crib the functions from a few others that supported more than one column, and obviously removed too much code. I changed manage_post_posts_columns to manage_posts_columns to enable the custom field and editing for all CPTs and posts, and it appears to work fine. Commented Jan 27, 2017 at 5:36
  • 1
    Possible vulnerability to Cross-Site Request Forgery (CSRF): quick edit nonce won't be checked if it is not set.
    – Max Yudin
    Commented Nov 19, 2020 at 20:56
  • Fixed the nonce issue pointed out by @Max Yudin. Also, found this link to a more polished approach: rudrastyh.com/wordpress/quick-edit-tutorial.html Commented Feb 26, 2021 at 21:07

Your Answer

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

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