I’m trying to display a WP_List_table
inside a metabox. The metabox is for a custom taxonomy called service
which is registered to a custom post type called office
. The table columns contain input fields which manipulate the taxonomy and allow me to link additional data to each individual taxonomy term. I don’t think the fact that I’m trying to this for a custom taxonomy which is on a custom post type edit screen is relevant. I only merely mention to for clarity sake.
What’s happening is that it all looks fine until I click the Publish/Update button on the custom post type edit screen. If the WP_List_Table
has bulk actions it will redirect back to the /wp-admin/edit.php
page, it I remove the bulk actions then it displays a WordPress Failure Notice “Are you sure you want to do this? – Please try again”, which is a nonce validation error. And in both cases, the none of the data I change for the post is saved.
I’ve included a link below to the full script that registers the taxonomy, creates the table and saves the metabox data. Feel free to fiddle with it to get it working if you like.
http://www.writeurl.com/text/aawwgq1od5913fmyfjyu/0y84g3vcyptvrkvvfxq4/0dow449a06lj6ctkqjqo
A NOTE TO THE STACKOVERFLOW EDITORS: I have already checked through questions suggested while I type mine and also all questions tagged with wp-list-table plus scoured the depths of Google and none of them seem relevant to my situation.
UPDATE: If possible I’d prefer to not to have to use any bulk_actions. I’ve no need for them on this table. I only added the delete action for testing purposes. And the class method process_bulk_action
doesn’t even get called on submit anyway.
<?php
if(!class_exists('WP_List_Table')) {
require_once(ABSPATH.'wp-admin/includes/class-wp-list-table.php');
}
class servicesTable extends WP_List_Table {
private $allServices;
private $currentServices;
private $currentServicesData;
public function __construct(){
parent::__construct(
array(
'singular' => 'service',
'plural' => 'services',
'ajax' => false
)
);
}
private function table_data() {
$this->currentServices = wp_get_object_terms(get_the_ID(),array('service'),array('fields' => 'ids'));
$this->currentServicesData = (array) maybe_unserialize(get_post_meta(get_the_ID(),'currentServices',true));
$this->allServices = array();
foreach((array) get_terms(array('service'),array('hide_empty' => false)) as $service) {
$this->allServices[] = array(
'id' => $service->term_id,
'serviceTitle' => $service->name,
'attachments' => array_key_exists($service->term_id,$this->currentServicesData) ? explode(',',$this->currentServicesData[$service->term_id]['attachments']) : array(),
'lastServiced' => (array_key_exists($service->term_id,$this->currentServicesData) && strlen($this->currentServicesData[$service->term_id]['last_serviced']) > 0) ? date('Y-m-d',strtotime($this->currentServicesData[$service->term_id]['last_serviced'])) : '',
'nextService' => (array_key_exists($service->term_id,$this->currentServicesData) && strlen($this->currentServicesData[$service->term_id]['next_service']) > 0) ? date('Y-m-d',strtotime($this->currentServicesData[$service->term_id]['next_service'])) : '',
'invoices' => ''
);
}
return $this->allServices;
}
public function column_default($item,$columnName) {
return array_key_exists($columnName,$item) ? $item[$columnName] : print_r($item,true);
}
public function get_columns(){
return array(
'cb' => '<input type="checkbox" />',
'id' => 'ID',
'serviceTitle' => __('Title','brokertech'),
'attachments' => __('Attachments','brokertech'),
'lastServiced' => __('Last Service Date','brokertech'),
'nextService' => __('Next Service Date','brokertech'),
'invoices' => __('Invoices','brokertech')
);
}
public function get_hidden_columns() {
return array('id');
}
public function get_sortable_columns() {
return array(
'lastServiced' => array('lastServiced',true),
'nextService' => array('nextService',true)
);
}
private function sort_data($a,$b) {
$orderby = empty($_GET['orderby']) ? 'nextService' : $_GET['orderby'];
$order = empty($_GET['order']) ? 'asc' : $_GET['order'];
$result = strcmp($a[$orderby],$b[$orderby]);
return ($order === 'asc') ? $result : -$result;
}
public function prepare_items() {
usort($this->table_data(),array(&$this,'sort_data'));
$this->_column_headers = array($this->get_columns(),$this->get_hidden_columns(),$this->get_sortable_columns());
$this->items = $this->allServices;
wp_enqueue_script('services',JS_URI.'servicesSelector.js',array('thickbox','media-upload'));
wp_enqueue_style('thickbox');
wp_localize_script(
'services',
'servicesData',
array(
'editAttachment' => __('Edit'),
'removeAttachment' => __('Remove')
)
);
}
public function column_cb($item) {
return sprintf('<input type="checkbox" name="services[%s][enable]" value="%s" %s />',$item['id'],$item['serviceTitle'],checked(true,in_array($item['id'],$this->currentServices),false));
}
public function column_id($item) {
return sprintf('<input type="hidden" name="services[%s][id]" value="%s" />',$item['id'],$item['id']);
}
public function column_attachments($item) {
$html = sprintf(
'<input type="hidden" name="services[%s][attachments]" value="%s" class="listAttachments" />',
$item['id'],
empty($item['attachments']) ? '' : implode(',',$item['attachments'])
);
$html .= sprintf('<input type="button" value="%s" class="button button-secondary addServiceAttachment" data-service-id="%s" />',__('Add'),$item['id']);
$html .= " ";
if(empty($item['attachments'])) {
$html .= sprintf('<input type="button" value="%s" class="button button-secondary clearAll" data-service-id="%s" disabled="disabled" />',__('Clear'),$item['id']);
}
else {
$html .= sprintf('<input type="button" value="%s" class="button button-secondary clearAll" data-service-id="%s" />',__('Clear'),$item['id']);
$html .= "<br /><br />";
foreach($item['attachments'] as $attachment) {
if(is_numeric($attachment)) {
$html .= sprintf(
"<div class='serviceAttachment' style='margin-bottom: 1em;'><span>%s</span><br /><a href='%s' target='_blank' class='button button-secondary'>%s</a><input type='button' class='removeServiceAttachment button button-secondary' value='%s' style='float:right;' /><div style='clear:both;'></div></div>",
get_the_title($attachment),
get_admin_url(get_current_blog_id(),'post.php?post='.$attachment.'&action=edit'),
__('Edit'),
__('Remove')
);
}
}
}
return $html;
}
public function column_lastServiced($item) {
return sprintf('<input type="date" name="services[%s][last_serviced]" value="%s" />',$item['id'],$item['lastServiced']);
}
public function column_nextService($item) {
return sprintf('<input type="date" name="services[%s][next_service]" value="%s" />',$item['id'],$item['nextService']);
}
public function column_invoices($item) {
$html = sprintf(
'<input type="hidden" name="services[%s][invoices]" value="%s" class="listInvoices" />',
$item['id'],
empty($item['invoices']) ? '' : implode(',',$item['invoices'])
);
$html .= sprintf('<input type="button" value="%s" class="button button-secondary addInvoice" data-service-id="%s" />',__('Add'),$item['id']);
$html .= " ";
if(!empty($item['invoices'])) {
$html .= "<br /><br />";
foreach($item['invoices'] as $invoice) {
if(is_numeric($invoice)) {
$html .= sprintf(
"<div class='serviceInvoice' style='margin-bottom: 1em;'><span>%s</span><br /><a href='%s' target='_blank' class='button button-secondary'>%s</a><input type='button' class='removeInvoice button button-secondary' value='%s' style='float:right;' /><div style='clear:both;'></div></div>",
get_the_title($invoice),
get_admin_url(get_current_blog_id(),'post.php?post='.$attachment.'&action=edit'),
__('Edit'),
__('Remove')
);
}
}
}
return $html;
}
}
function saveServices($postID) {
if(empty($_POST) || (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE)) {
return;
}
$servicesData = array();
$services = array();
foreach($_POST['services'] as $service) {
if(isset($service['enable'])) {
$services[] = (string) $service['enable'];
$servicesData[$service['id']] = $service;
}
}
empty($servicesData) ? delete_post_meta($postID,'currentServices') : update_post_meta($postID,'currentServices',$servicesData);
wp_set_object_terms($postID,$services,'service');
}
add_action('save_post','saveServices');
function servicesMetaBox() {
$servicesTable = new servicesTable();
$servicesTable->prepare_items();
$servicesTable->display();
}
$serviceTaxonomy = new brokertechPostType;
$serviceTaxonomy->createTaxonomy(
'service',
array(
'office'
),
__('Service'),
__('Services'),
array(
'public' => true,
'hierarchical' => false,
'query_var' => true,
'update_count_callback' => '_update_post_term_count',
'show_ui' => true,
'rewrite' => array(
'slug' => __('services')
),
'meta_box_cb' => 'servicesMetaBox'
)
);
function addServicesTaxonomyPage() {
add_submenu_page(
'edit.php?post_type=company',
__('Services'),
__('Services'),
'edit_posts',
'edit-tags.php?taxonomy=service&post_type=office'
);
}
add_action('admin_menu','addServicesTaxonomyPage');
function changeServicesMetaBoxPosition() {
global $wp_meta_boxes;
unset($wp_meta_boxes['office']['side']['core']['tagsdiv-service']);
add_meta_box(
'tagsdiv-service',
__('Services'),
'servicesMetaBox',
'office',
'normal',
'low'
);
}
add_action('add_meta_boxes','changeServicesMetaBoxPosition',0);
You just need to override the function
display_tablenav()
with a blank function in you parent class.It is explained here.
I just ran into this issue recently.
If you are using a Custom List Table (A/K/A WP List Table) within a Meta Box (located on the post edit screen
post.php
orpost-new.php
.)The reason you get redirected to the posts listing screen (
edit.php
) is because you’re Custom List Table generates a hidden nonce field, which is also generated by thepost.php
orpost-new.php
pages. They both have the same names, there for … unexpected problems occur.Simple solution would be to override the function creating the nonce field in your Custom List Table.
The problem in your Custom List Table is that your pagination is creating a nonce.
If you open up the
list-table.php
class and search forfunction display_tablenav(
you will find the function which is creating the nonce.I’m sure there’s a better approach to this, but I simple just copied that entire function, then pasted it into my Custom List Table class, and commented out the part which creates the nonce field.
Example:
If you comment out that section of code, your post page should successfully submit like normal once again.