I've created an admin page where I'm displaying a list of a MySQL Table using WP_List_Table. In this table I need to be able to delete a record when I want to. And that's where I have a problem.
To delete a record I've set up the following code.
class Genres_List extends WP_List_Table {
[...]
public static function delete_genre( $id ) {
global $wpdb;
$wpdb->delete(
"{$wpdb->prefix}genres",
[ 'id' => $id ],
[ '%d' ]
);
}
function column_cb( $item ) {
return sprintf('<input type="checkbox" name="id[]" value="%s" />', $item['id']);
}
function column_name( $item ) {
$delete_nonce = wp_create_nonce( 'sp_delete_genre' );
$title = '<strong>' . stripslashes($item['name']) . '</strong>';
$actions = [
'edit' => sprintf( '<a href="?page=%s&action=%s&id=%s">Bewerken</a>', esc_attr( $_REQUEST['page'] ), 'edit', absint( $item['id'] ) ),
'delete' => sprintf( '<a href="?page=%s&action=delete&id=%s&_wpnonce=%s">Verwijderen</a>', esc_attr( $_REQUEST['page'] ), absint( $item['id'] ), $delete_nonce )
];
return '<a href="?page=genres&action=edit&id=' . $item['id'] . '"><strong>' . stripslashes($item['name']) . '</strong>' . $this->row_actions( $actions );
}
function get_columns() {
$columns = [
'cb' => '<input type="checkbox" />',
'name' => 'Naam'
];
return $columns;
}
public function get_bulk_actions() {
$actions = [
'delete' => 'Verwijderen'
];
return $actions;
}
public function process_bulk_action() {
if ( $this->current_action() === 'delete' ) {
if ( ! wp_verify_nonce( $_REQUEST['_wpnonce'], 'sp_delete_genre' ) ) {
die('This is a secure website. Your nonce did not verify. Go get a coffee.');
} else {
self::delete_genre( absint( $_GET['id'] ) );
wp_redirect( esc_url( add_query_arg() ) );
exit;
}
if ( $_POST['action'] === 'delete' || $_POST['action2'] === 'delete') {
$delete_ids = esc_sql( $_POST['id'] );
foreach ( $delete_ids as $id ) { self::delete_genre( $id ); }
wp_redirect( esc_url( add_query_arg() ) );
exit;
}
}
}
}
The problem is that wp_verify_nonce( $_REQUEST['_wpnonce'], 'sp_delete_genre' )
returns false when trying to delete a record. I can't seem to find what I'm doing wrong, since I'm exactly following the Wordpress Codex:
- Creating a nonce using
wp_create_nonce( 'sp_delete_genre' )
. - Using the nonce in a
_wpnonce=
parameter. - Verifying the nonce using
wp_verify_nonce( $_REQUEST['_wpnonce'], 'sp_delete_genre' )
$_REQUEST
can also contain cookies and session variables, it's always better to be specific an use either GET or POST for security reasons. I also notice you useadd_query_arg
with no parameters, this is highly unusual, that function has required arguments, and needs both a key and value, as well as a URL. Your use ofesc_sql
is also odd, it would be better to usewpdb prepare
inside thedelete_genre
method, and instead useintval
on the POST id$_REQUEST['_wpnonce']
, it returns a nonce, so that's okay. Theadd_query_arg()
returns a legit url, even without parameters. But I'm not even getting that far because of the problem I described. I didn't write the code myself, so I wasn't aware of the problems with it.talks
on my website, with a taxonomy for locations. They have their own menu in the admin, their own URLs archives and templates, independent REST APIs, hooks, and access controls, etc. Have you confirmed that the nonce is making it through to the other side, even if it's not being accepted?$_REQUEST['_wpnonce']
and that returns the nonce, but how could I confirm that the nonce is making it through to the other side, even if it's not being accepted ?