One of the most cool features when managing WordPress posts, is the ability to batch edit multiple posts at once or quickly edit one post to perform a simple task such as adding a tag. Learn how to extend WordPress’ Quick Edit and Bulk Edit to retrieve and save the value of your custom fields.
Overview
Let’s take a look at how it will work:
This code has been tested using TwentyEleven, the default theme of WordPress 3.2 but it should work consistently on other themes since it doesn’t uses anything from the theme. Fire up your IDE and open the functions.php
file of TwentyEleven and after the first block of comments, add the following code:
<?php if ( is_admin() ) { require_once( TEMPLATEPATH . '/ilc-admin.php' ); }
We’re going to place all of our code in this external file (that you can download at the bottom of the this article). We will be relying on jQuery to retrieve and save the custom field values using AJAX queries.
Creating a custom post type
To spice things up and show how adaptable this code is, we’re going to create a custom post type named Events to use our code:
add_action('init', 'ilc_init'); function ilc_init() { register_post_type( 'event', array( 'labels' => [ 'name' => 'Events', 'singular_name' => 'Event', ], 'public' => true, 'menu_position' => 5, 'supports' => ['title', 'editor', 'custom-fields'] ) ); }
Creating custom columns
First of all, we’re going to add some columns in the list view of your events to see the value of our custom field, before and after saving it. It’s a good way to quickly see the values of your custom fields.
In order to simplify things, we’re simply assuming that the custom field exists and has a value. You can add a meta box in the post writing screen with a super duper cool calendar to select the date if you want to or you can add a custom field in the old way. We don’t even need it to be there because it will be created if it doesn’t exists.
<?php add_filter( 'manage_event_posts_columns', 'ilc_cpt_columns' ); add_action( 'manage_event_posts_custom_column', 'ilc_cpt_custom_column', 10, 2 ); function ilc_cpt_columns($defaults) { $defaults['event'] = 'Event Date'; return $defaults; } function ilc_cpt_custom_column($column_name, $post_id) { $eventdate = get_post_meta($post_id, 'eventdate', true); if( $eventdate ) { echo '<em>' . $eventdate . '</em>'; } else echo '<em>No date set.</em>'; }
If you don’t want to display additional columns of info (why?), add only the filter disregarding the action and change the function referenced by the filter:
function ilc_cpt_columns( $defaults ) { $defaults['event'] = ''; return $defaults; }
and now no column will be shown.
Displaying and saving custom fields values in quick edit mode
Let’s begin saving our custom fields by tackling quick edit saving. The following code will create an input element to display the value of the custom field.
<?php add_action('quick_edit_custom_box', 'ilc_quickedit_show', 10, 2); function ilc_quickedit_show( $col, $type ) { if ( $type != 'event' ) { return; } ?> <fieldset class="inline-edit-col-left"> <div class="inline-edit-col"> <div class="inline-edit-group"> <label style="font: italic 12px Georgia, serif;" for="eventdate">Event Date</label> <span class="input-text-wrap"> <input id="eventdate" type="text" name="eventdate" value="" size="10" /> </span> <input type="hidden" name="is_quickedit" value="true" /> </div> </div> </fieldset> <?php }
Notice that the post type is checked to display the field only for event post types and to return if it isn’t. Squint your eyes and you’ll see a hidden field named is_quickedit. Remember it and we’ll come back to it after the next code snippet.
Retrieving and displaying the custom field value
Dissapointingly enough, the last code displayed an empty field and that’s because we need to retrieve the value first. Let’s do it:
<?php add_action('admin_head-edit.php', 'ilc_quickedit_get'); function ilc_quickedit_get() { ?> <script type="text/javascript"> jQuery(document).ready(function() { jQuery("a.editinline").live("click", function() { var ilc_qe_id = inlineEditPost.getId(this); jQuery.post( "<?php echo YOUR_THEME_URI ?>/ilc-admin.php", { ilc_post_id: ilc_qe_id, ilc_mode: 'ajaxget' }, function(data) { jQuery('#eventdate').val(data); } ); }); var ilc_post_ids = []; jQuery('.ilc-updated').hide(); jQuery('#doaction, #doaction2').click(function(e) { var n = jQuery(this).attr('id').substr(2); if (jQuery('select[name="' + n + '"]').val() == 'edit') { e.preventDefault(); jQuery('tbody th.check-column input[type="checkbox"]:checked').each(function(i) { ilc_post_ids.push(jQuery(this).val()); }); } else if (jQuery('form#posts-filter tr.inline-editor').length > 0) { t.revert(); } }); }); </script> <?php }
Things are getting messier with this last JavaScript code, but as you can see, we’re using AJAX to obtain the value and the url queried is this very same file (that’s why we’re using a separate file to keep things clean). To process our AJAX query, add the following at the top of the current file:
if(isset($_POST['ilc_mode'])){ require_once('../../../wp-blog-header.php'); if($_POST['ilc_mode'] == 'ajaxget'){ $ilc_post_id = $_POST['ilc_post_id']; echo get_post_meta($ilc_post_id, 'eventdate', true); } elseif($_POST['ilc_mode'] == 'ajaxsave'){ $ilc_ids = explode(" ", $_POST['ilc_ids']); foreach ( $ilc_ids as $ilc_post_id) update_post_meta($ilc_post_id, 'eventdate', $_POST['ilc_val']); } return; }
The variable ilc_mode sent in the AJAX query will have two values: ajaxget or ajaxsave. In this way, we know when the query is used to get the custom field value or set it to a different value. If the mode is ajaxget, we get the post ID and retrieve the post meta using it. The ajaxsave mode is only used by the bulk edit mode, so you can remove it if you’re not going to code it.
Saving the custom field value
The last piece of code we need is the one that actually saves our custom fields values:
add_action( 'edit_post', 'ilc_quickedit_save', 10, 2 ); function ilc_quickedit_save( $post_id, $post ) { if( $post->post_type != 'event' ) { return; } if ( isset( $_POST['is_quickedit'] ) ) { update_post_meta($post_id, 'eventdate', $_POST['eventdate']); } }
Again, we save the value only if the post type is event, but in this case, we also check the is_quickedit variable mentioned before: if it is set, we know that the user is in the events list view saving the value using quick edit. If it is not set, the user is in the post writing interface, so trying to save the custom field value using the quick edit variable eventdate will fail, since it’s not in the $_POST array, thus resulting in a blank field.
After all your hard work, you’re now saving the custom field value using the quick edit mode, yay! pat yourself in the back, get a coffee and come back for the final round, the bulk edit mode.
Saving values in bulk edit mode
This is when everything gets ugly and let me explain you why I’ve separated the quick edit from the bulk edit. First reason is that even though they share a lot of code, most of the people won’t ever use it. The second reason is that it requires a nasty hack: an additional saving button. Now is the time where you make that “suspicious” face. If you’ve found some other way to do it, please, go ahead and share it the comments.
Creating the input element
Like almost everything in the web, the code for the bulk edit begins with some good old HTML:
<?php add_action('bulk_edit_custom_box', 'ilc_quickedit_bulk', 10, 2); function ilc_quickedit_bulk($col, $type) { if ($type != 'event') { return; } ?> <fieldset class="inline-edit-col-left"> <div class="inline-edit-col"> <div class="inline-edit-group"><label style="font: italic 12px Georgia, serif;" for="eventdate">Event Date</label> <span class="input-text-wrap"> <input id="eventdatebulk" type="text" name="eventdatebulk" value="" size="10" /> </span></div> </div> Event date updated <a class="ilc-bulk-update button-secondary" href="#">Update event date</a> </fieldset> <?php }
Updating the custom field value
Pretty similar to the HTML for quick edit, but we don’t need the additional hidden field since the custom field value won’t be saved using the edit_post hook: we will use AJAX and the ajaxsave mode to update the value. This is the same function used for the quick edit mode but has additional code to handle the bulk editing mode:
<?php add_action( 'admin_head-edit.php', 'ilc_quickedit_get' ); function ilc_quickedit_get() { ?> <script type="text/javascript"> jQuery(document).ready(function() { jQuery("a.editinline").live("click", function() { var ilc_qe_id = inlineEditPost.getId(this); jQuery.post( "<?php echo YOUR_THEME_URI ?>/ilc-admin.php", { ilc_post_id: ilc_qe_id, ilc_mode: "ajaxget" }, function(data) { jQuery("#eventdate").val(data); } ); }); var ilc_post_ids = []; var ilc_bulk_value = ''; var ilc_post_ids_flat = ''; jQuery(".ilc-updated").hide(); jQuery('#doaction, #doaction2').click(function(e) { var n = jQuery(this).attr('id').substr(2); if (jQuery('select[name="' + n + '"]').val() == 'edit') { e.preventDefault(); jQuery('tbody th.check-column input[type="checkbox"]:checked').each(function(i) { ilc_post_ids.push(jQuery(this).val()); }); } else if (jQuery('form#posts-filter tr.inline-editor').length > 0) { t.revert(); } }); jQuery(".ilc-bulk-update").live("click", function() { ilc_bulk_value = jQuery('input[name="eventdatebulk"]').val(); for (var ilc_i = 0; ilc_i < ilc_post_ids.length; ilc_i++) { ilc_post_ids_flat = ilc_post_ids_flat + " " + ilc_post_ids[ilc_i]; } jQuery.post( "<?php echo YOUR_THEME_URI ?>/ilc-admin.php", { ilc_ids: ilc_post_ids_flat, ilc_mode: "ajaxsave", ilc_val: ilc_bulk_value }, function(data) { jQuery(".ilc-updated").fadeIn(300).delay(800).fadeOut(300); jQuery("input#bulk-edit").click(); } ); }); }); </script> <?php }
Now, when you select two or more events and enable bulk editing mode, you’ll have the additional field and the additional button to save it. After saving the value, we will trigger the click event of the main button to save the other values you might have changed. Just like common Bulk Editing, page will be reloaded and your changes will be applied.
Download the code
Here you have the sample code, place it next to your functions.php file in your WordPress theme and type the first code snippet shown in this tutorial. Have fun playing with it!