Why does this Quick Edit checkbox save the value only when unchecked?

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

1 Answer
1

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;
}

Leave a Comment