3

How can I add a new column to quick edit and put a custom field in it? I read this question but one of the links lead to an error 404. I need a new column for my custom meta key, "summary" and post types, "post" and "episode".

2 Answers 2

13

There are a few steps to create the custom quick edit box and custom column

  1. create a custom meta key (assumed that you have 1 already)
  2. add custom admin column title and data (assumed that you want to shows the custom meta key in the column, if not, you may also modify a bit of the logic to accomplish the same effect because the principal is the same)
  3. add custom quick edit box
  4. add save logic
  5. load script to modify original inline-edit-post function in order to support custom meta value
  6. prepare the script file

This example is going to add a quick edit input box in post type Page. The following code is proved to work by putting in functions.php .

1. create a custom meta key (assumed that you have 1 already)

I have prepared a custom meta key remark for post type page

2. add custom admin column title and data

for custom post type, you may use

Add column title

Add column content

The following is an example for post type Page as well as custom post type + default Post as an example for multiple post types setup.

// add custom column title for custom meta value
add_filter('manage_edit-page_columns', 'ws365150_add_custom_columns_title' );
function ws365150_add_custom_columns_title( $columns ) {
    $columns['page_remark'] = 'Remark'; // you may use __() later on for translation support
    
    return $columns;
}
    
// add custom column data with custom meta value
add_action('manage_pages_custom_column', 'ws365150_add_custom_column_data', 10, 2 );
function ws365150_add_custom_column_data( $column_name, $post_id ) {
    switch ( $column_name ) {
        case 'page_remark':
            echo get_post_meta( $post_id, 'remark', true );
            break;
        
        default:
            break;
    }
}

The following is an example for custom post type episode + default Post with meta key summary

// add custom column title for custom meta value
// 'manage_pages_columns' or 'manage_edit-post_columns' both works
add_filter('manage_posts_columns', 'ws365150_add_custom_columns_title_pt', 10, 2 );
function ws365150_add_custom_columns_title_pt( $columns, $post_type ) {
    switch ( $post_type ) {
        case 'post':
        case 'episode':
            $columns['ws365150_summary'] = 'Summary'; // you may use __() later on for translation support
            break;
        
        default:
            
            break;
    }
    
    return $columns;
}
    
// add custom column data with custom meta value for custom post types
add_action('manage_posts_custom_column', 'ws365150_add_custom_column_data_pt', 10, 2 );
function ws365150_add_custom_column_data_pt( $column_name, $post_id ) {
    switch ( $column_name ) {
        case 'ws365150_summary': // specified for this column assigned in the column title
            echo get_post_meta( $post_id, 'summary', true );
            break;
        
        default:
            break;
    }
}

3. add custom quick edit box

// for page
add_action( 'quick_edit_custom_box', 'ws365150_custom_edit_box', 10, 3 );
function ws365150_custom_edit_box( $column_name, $post_type, $taxonomy ) {
    global $post;

    switch ( $post_type ) {
        case 'page':

        if( $column_name === 'page_remark' ): // same column title as defined in previous step
        ?>
                <?php // echo get_post_meta( $post->ID, 'remark', true ); ?>
            <fieldset class="inline-edit-col-right" id="#edit-">
                <div class="inline-edit-col">
                    <label>
                        <span class="title">Remark</span>
                        <span class="input-text-wrap"><input type="text" name="remark" class="inline-edit-menu-order-input" value=""></span>
                    </label>
                </div>
            </fieldset>
            <?php
        endif;
            // echo 'custom page field';
            break;
        
        default:
            break;
    }
}

// for Post + custom post type
add_action( 'quick_edit_custom_box', 'ws365150_custom_edit_box_pt', 10, 3 );
function ws365150_custom_edit_box_pt( $column_name, $post_type, $taxonomy ) {
    global $post;

    switch ( $post_type ) {
        case 'post':
        case 'episode':

        if( $column_name === 'ws365150_summary' ): // same column title as defined in previous step
        ?>
                <?php // echo get_post_meta( $post->ID, 'remark', true ); ?>
            <fieldset class="inline-edit-col-right" id="#edit-">
                <div class="inline-edit-col">
                    <label>
                        <span class="title">Summary</span>
                        <span class="input-text-wrap"><input type="text" name="summary" class="inline-edit-menu-order-input" value=""></span>
                    </label>
                </div>
            </fieldset>
            <?php
        endif;
            // echo 'custom page field';
            break;
        
        default:
            break;
    }
}

4. add save logic

add_action( 'save_post', 'ws365150_update_custom_quickedit_box' );
function ws365150_update_custom_quickedit_box() {
    // any checking logic here, skip and keep it simple for simple illustration purpose (nonce, existing of $_POST['remark'], ajax save and so on
    
    // remark in Page
    if( isset( $_POST ) && isset( $_POST['remark'] ) ) { // where remark is defined in the <input name="remark">
        update_post_meta($_POST['post_ID'], 'remark', $_POST['remark']);
    }

    // summary in Post, custom post type
    if( isset( $_POST ) && isset( $_POST['summary'] ) ) { // where summary is defined in the <input name="summary">
        update_post_meta($_POST['post_ID'], 'summary', $_POST['summary']);
    }
    
    // for debugging purpose in inspector, not necessary, enable this will break the saving but could see the ajax return
    // wp_send_json_success( array(
    //  'message' => 'Save test!',
    //  'post_data' => $_POST,
    // ) );
    return; // finish the function call
}

5. load script to modify original inline-edit-post function

add_action( 'admin_enqueue_scripts', function( $page ) {

    // add page checking logic, this is a simple one, you may test post type and so on...
    if ( 'edit.php' != $page ) {
        return;
    }
    
    wp_enqueue_script( 'custom-quickedit-box', get_stylesheet_directory_uri() . '/ws365150_custom_quickedit_box.js', array( 'jquery', 'inline-edit-post' ) );
});

6. prepare the script file (ws365150_custom_quickedit_box.js, when test the above code, put in the theme folder)

( function( $, wp ) {
    // clone from original function in inline-post-edit.js for override
    // actually no need to create an alias object, however, create an alias could be a note and mark of override without forgetting for later maintenance
    window.customInlineEditPost = window.inlineEditPost;

    // function override: add custom meta value, the base is copied from the source
    customInlineEditPost.edit = function(id) {
            // console.log( 'custom edit' );
            var t = this, fields, editRow, rowData, status, pageOpt, pageLevel, nextPage, pageLoop = true, nextLevel, f, val, pw;
            t.revert();

            if ( typeof(id) === 'object' ) {
                id = t.getId(id);
            }

            fields = ['post_title', 'post_name', 'post_author', '_status', 'jj', 'mm', 'aa', 'hh', 'mn', 'ss', 'post_password', 'post_format', 'menu_order', 'page_template'];
            if ( t.type === 'page' ) {
                fields.push('post_parent');
            }

            // Add the new edit row with an extra blank row underneath to maintain zebra striping.
            editRow = $('#inline-edit').clone(true);
            $( 'td', editRow ).attr( 'colspan', $( 'th:visible, td:visible', '.widefat:first thead' ).length );

            $(t.what+id).removeClass('is-expanded').hide().after(editRow).after('<tr class="hidden"></tr>');

            // Populate fields in the quick edit window.
            rowData = $('#inline_'+id);
            if ( !$(':input[name="post_author"] option[value="' + $('.post_author', rowData).text() + '"]', editRow).val() ) {

                // The post author no longer has edit capabilities, so we need to add them to the list of authors.
                $(':input[name="post_author"]', editRow).prepend('<option value="' + $('.post_author', rowData).text() + '">' + $('#' + t.type + '-' + id + ' .author').text() + '</option>');
            }
            if ( $( ':input[name="post_author"] option', editRow ).length === 1 ) {
                $('label.inline-edit-author', editRow).hide();
            }

            // populate custom meta value
            if ( $( ':input[name="remark"]', editRow ).length === 1 ) {
                $( ':input[name="remark"]', editRow ).val( $('#post-' + id + ' .page_remark').text() );
            }

            if ( $( ':input[name="summary"]', editRow ).length === 1 ) {
                $( ':input[name="summary"]', editRow ).val( $('#post-' + id + ' .ws365150_summary').text() );
            }

            for ( f = 0; f < fields.length; f++ ) {
                val = $('.'+fields[f], rowData);

                /**
                 * Replaces the image for a Twemoji(Twitter emoji) with it's alternate text.
                 *
                 * @returns Alternate text from the image.
                 */
                val.find( 'img' ).replaceWith( function() { return this.alt; } );
                val = val.text();
                $(':input[name="' + fields[f] + '"]', editRow).val( val );
            }

            if ( $( '.comment_status', rowData ).text() === 'open' ) {
                $( 'input[name="comment_status"]', editRow ).prop( 'checked', true );
            }
            if ( $( '.ping_status', rowData ).text() === 'open' ) {
                $( 'input[name="ping_status"]', editRow ).prop( 'checked', true );
            }
            if ( $( '.sticky', rowData ).text() === 'sticky' ) {
                $( 'input[name="sticky"]', editRow ).prop( 'checked', true );
            }

            /**
             * Creates the select boxes for the categories.
             */
            $('.post_category', rowData).each(function(){
                var taxname,
                    term_ids = $(this).text();

                if ( term_ids ) {
                    taxname = $(this).attr('id').replace('_'+id, '');
                    $('ul.'+taxname+'-checklist :checkbox', editRow).val(term_ids.split(','));
                }
            });

            /**
             * Gets all the taxonomies for live auto-fill suggestions when typing the name
             * of a tag.
             */
            $('.tags_input', rowData).each(function(){
                var terms = $(this),
                    taxname = $(this).attr('id').replace('_' + id, ''),
                    textarea = $('textarea.tax_input_' + taxname, editRow),
                    comma = inlineEditL10n.comma;

                terms.find( 'img' ).replaceWith( function() { return this.alt; } );
                terms = terms.text();

                if ( terms ) {
                    if ( ',' !== comma ) {
                        terms = terms.replace(/,/g, comma);
                    }
                    textarea.val(terms);
                }

                textarea.wpTagsSuggest();
            });

            // Handle the post status.
            status = $('._status', rowData).text();
            if ( 'future' !== status ) {
                $('select[name="_status"] option[value="future"]', editRow).remove();
            }

            pw = $( '.inline-edit-password-input' ).prop( 'disabled', false );
            if ( 'private' === status ) {
                $('input[name="keep_private"]', editRow).prop('checked', true);
                pw.val( '' ).prop( 'disabled', true );
            }

            // Remove the current page and children from the parent dropdown.
            pageOpt = $('select[name="post_parent"] option[value="' + id + '"]', editRow);
            if ( pageOpt.length > 0 ) {
                pageLevel = pageOpt[0].className.split('-')[1];
                nextPage = pageOpt;
                while ( pageLoop ) {
                    nextPage = nextPage.next('option');
                    if ( nextPage.length === 0 ) {
                        break;
                    }

                    nextLevel = nextPage[0].className.split('-')[1];

                    if ( nextLevel <= pageLevel ) {
                        pageLoop = false;
                    } else {
                        nextPage.remove();
                        nextPage = pageOpt;
                    }
                }
                pageOpt.remove();
            }

            $(editRow).attr('id', 'edit-'+id).addClass('inline-editor').show();
            $('.ptitle', editRow).focus();

            return false;
    };
})( jQuery, window.wp );

The above is for quick edit using quick_edit_custom_box hook, and you may need to take care of bulk edit also. You may explore more by reading and using bulk_edit_custom_box.

12
  • Great! Could you edit the example for the post types post and episode and the custom meta key summary? Sorry, I do not like fiddling with the functions.php file, I could accidentally wreck WordPress. I have to know that there is a 100% chance the code will function.
    – Gusto
    May 4, 2020 at 22:12
  • I have added additional example code for post, episode with summary meta field with additional notes on column title and checking example when save. You may modify and add additional checking based on the content you needed. May 10, 2020 at 10:06
  • Wait a second, this code is not working and is just making my "All Pages" edit page blank.
    – Gusto
    May 12, 2020 at 20:47
  • The code is updated, please change add_filter('manage_posts_columns', 'ws365150_add_custom_columns_title_pt' ); to add_filter('manage_posts_columns', 'ws365150_add_custom_columns_title_pt', 10, 2 ); Please don't worry to hurry tick the answer before testing it out. May 12, 2020 at 23:15
  • Hm, when testing the above code, the new summary input is added to the post and episode post type's quick edit menu, but after clicking "update", the summary parameter is empty. Even if the summary parameter was already met (without using quick edit), the quick edit input still makes the parameter empty.
    – Gusto
    May 13, 2020 at 21:17
1

i didn't rewrite full js in step 6 i added just

(function($){
    lesson_orders={};
    if($('.post_type_page').length && $('.post_type_page').val()=='resource'){
        $('#the-list tr').each(function(){
            id=$(this).find('.check-column input').val();
            val=$(this).find('.lesson_order').text();
            lesson_orders['edit-'+id]=val;
        });
    }
    $(document).on('focus','.ptitle',function(){
        id =$(this).closest('.inline-edit-row').attr('id');
        $('#'+id+' input[name="lesson_order"]').val(lesson_orders[''+id]);
    }); 
})(jQuery);

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.