wp_verify_nonce sigue fallando
Necesitaba asignar categorías a las publicaciones desde el frontend, así que estoy utilizando la solución sugerida en este hilo, en forma de plugin. Funciona como se esperaba. Solo quería agregar un campo nonce para hacerlo un poco más seguro. Parece que hasta ahora no he logrado que funcione, ya que sigo recibiendo "Lo sentimos, tu nonce no se pudo verificar".
El código completo se proporciona a continuación, incluyendo los cambios que realicé, que están en las líneas 42 y 103. Aquí también hay un enlace al hilo de pastebin, en caso de que necesites identificar las líneas. En ambas líneas he intentado seguir la pista de esta página de WordPress. Por favor, echa un vistazo y dime por qué el nonce sigue fallando en la verificación.
<?php
/*
Plugin Name: WPSE Crowded Cats
Plugin URI: https://wordpress.stackexchange.com/questions/43419/how-do-i-create-a-way-for-users-to-assign-categories-to-a-post-from-the-frontend
Description: Permite a los visitantes cambiar categorías de publicaciones. Listo para usar con taxonomías personalizadas y tipos de publicación.
Version: 0.1
Author: WPSE
Author URI: https://wordpress.stackexchange.com/users/2110/maugly
License: GPL2
*/
add_action('plugins_loaded','wpse_init_crowd_cats_class');
function wpse_init_crowd_cats_class(){
new WPSECrowdCatsClass();
}
class WPSECrowdCatsClass {
function __construct() {
// AÑADIR EL FORMULARIO AUTOMÁTICAMENTE A CADA PUBLICACIÓN
add_filter( 'the_content', array( $this,'append_form' ) );
// ETIQUETA DE ACCIÓN DE PLANTILLA PARA USAR EN EL TEMA
// Uso: do_action('wpse_crowd_cats_form');
// Uso: do_action('wpse_crowd_cats_form', $post_id, $taxonomy );
add_action( 'wpse_crowd_cats_form', array( $this,'wpse_crowd_cats_form' ), 10, 2 );
// PROCESAMIENTO DEL FORMULARIO
add_action( 'template_redirect', array( $this,'process_request' ) );
}
function process_request(){
// verificar envío
if ( ! isset($_POST['crowd-cat-radio']) || ! is_array($_POST['crowd-cat-radio']) )
return;
if ( !isset($_POST['nonce_name']) || !wp_verify_nonce($_POST['nonce_name'],'nonce_action') )
{
print 'Lo sentimos, tu nonce no se pudo verificar.';
exit;
} else { // continuar con el procesamiento de los datos del formulario
// sanitizar y verificar la entrada
$suggested_terms = array_map( 'absint', $_POST['crowd-cat-radio'] );
$post_id = absint( $_POST['crowd-cats-pid'] );
$tax = $_POST['crowd-cats-tax'];
if ( ! taxonomy_exists($tax) )
return;
// Permitir solo términos existentes. No estoy seguro si esto es necesario.
$args = array( 'hide_empty' => false );
$args = apply_filters( 'mcc_allowed_terms_args', $args, $post_id, $tax );
$args['fields'] = 'ids';
$allowed_terms = get_terms( $tax, $args );
foreach ( $suggested_terms as $key => $term_id )
if ( ! in_array( $term_id, $allowed_terms ) )
unset( $suggested_terms[$key] );
// Añadir términos a la taxonomía
$affected_terms = wp_set_object_terms( $post_id, $suggested_terms, $tax, false );
update_term_cache($affected_terms);
return $affected_terms;
}
}
function get_form( $post_id=null, $tax='category' ) {
if ( is_null($post_id) || ! taxonomy_exists($tax) )
return false;
$args = array( 'hide_empty' => false );
$args = apply_filters( 'mcc_get_terms_args', $args, $post_id, $tax );
$all_terms = get_terms( $tax, $args );
if ( ! $all_terms )
return false;
$post_terms = wp_get_object_terms( $post_id, $tax, array( 'fields' => 'ids' ) );
$permalink = get_permalink( $post_id );
$out = "<form id='crowd-cats' action='$permalink' method='POST' >
<ul >";
foreach ( $all_terms as $t ) :
$checked = in_array( $t->term_id, $post_terms) ? 'checked' : '';
$out .= "<li>
<input type='checkbox' id='crowd-cat-$t->term_id' name='crowd-cat-radio[]' value='$t->term_id' $checked />
<label for='crowd-cat-$t->term_id' >".esc_attr($t->name)."</label>
</li>";
endforeach;
$out .= "</ul>
<input type='submit' value='Enviar' name='crowd-cats-submit'/>
<input type='hidden' value='".wp_nonce_field('nonce_action','nonce_name')."'/>
<input type='hidden' value='".esc_attr($tax)."' name='crowd-cats-tax'/>
<input type='hidden' value='$post_id' name='crowd-cats-pid'/>";
//TODO: establecer nonce
$out .= "</form>";
return $out;
}

No estás insertando el campo nonce en tu formulario, por lo que tu script no recibirá el campo nonce y este código:
if ( !isset($_POST['nonce_name']))
Será validado porque $_POST['nonce_name']
no está definido.
En tu código, elimina esta línea:
<input type='hidden' value='".wp_nonce_field('nonce_action','nonce_name')."'/>
Y, donde dice //TODO: set nonce
, necesitas incluir:
$out .= wp_nonce_field( plugin_basename( __FILE__ ), 'nonce_name',true,false);
Nota: establece el parámetro 'echo' de wp_nonce_field()
en false para recuperar el campo de entrada nonce en lugar de imprimirlo.
Y luego verifica con:
if (!isset( $_POST['nonce_name'] ) || ! wp_verify_nonce( $_POST['nonce_name'], plugin_basename( __FILE__ ) ) )
return;
Entonces, tu function get_form()
debería ser:
function get_form( $post_id=null, $tax='category' ) {
if ( is_null($post_id) || ! taxonomy_exists($tax) )
return false;
$args = array( 'hide_empty' => false );
$args = apply_filters( 'mcc_get_terms_args', $args, $post_id, $tax );
$all_terms = get_terms( $tax, $args );
if ( ! $all_terms )
return false;
$post_terms = wp_get_object_terms( $post_id, $tax, array( 'fields' => 'ids' ) );
$permalink = get_permalink( $post_id );
$out = "<form id='crowd-cats' action='$permalink' method='POST' >
<ul >";
foreach ( $all_terms as $t ) :
$checked = in_array( $t->term_id, $post_terms) ? 'checked' : '';
$out .= "<li>
<input type='checkbox' id='crowd-cat-$t->term_id' name='crowd-cat-radio[]' value='$t->term_id' $checked />
<label for='crowd-cat-$t->term_id' >".esc_attr($t->name)."</label>
</li>";
endforeach;
$out .= "</ul>
<input type='submit' value='Enviar' name='crowd-cats-submit'/>
<input type='hidden' value='".esc_attr($tax)."' name='crowd-cats-tax'/>
<input type='hidden' value='$post_id' name='crowd-cats-pid'/>";
$out .= wp_nonce_field( plugin_basename( __FILE__ ), 'nonce_name',true,false);
$out .= "</form>";
return $out;
}
Y tu function process_request()
debería ser:
function process_request(){
// verificar envío
if ( ! isset($_POST['crowd-cat-radio']) || ! is_array($_POST['crowd-cat-radio']) )
return;
if ( !isset($_POST['nonce_name']) || !wp_verify_nonce($_POST['nonce_name'],plugin_basename( __FILE__ )) )
{
print 'Lo sentimos, tu nonce no se pudo verificar.';
exit;
} else { // continuar procesando los datos del formulario
// sanitizar y verificar la entrada
$suggested_terms = array_map( 'absint', $_POST['crowd-cat-radio'] );
$post_id = absint( $_POST['crowd-cats-pid'] );
$tax = $_POST['crowd-cats-tax'];
if ( ! taxonomy_exists($tax) )
return;
// Permitir solo términos existentes. No estoy seguro si esto es necesario.
$args = array( 'hide_empty' => false );
$args = apply_filters( 'mcc_allowed_terms_args', $args, $post_id, $tax );
$args['fields'] = 'ids';
$allowed_terms = get_terms( $tax, $args );
foreach ( $suggested_terms as $key => $term_id )
if ( ! in_array( $term_id, $allowed_terms ) )
unset( $suggested_terms[$key] );
// Añadir términos a la taxonomía
$affected_terms = wp_set_object_terms( $post_id, $suggested_terms, $tax, false );
update_term_cache($affected_terms);
return $affected_terms;
}
}

Creo que tu problema es que tienes <input type='hidden' value='".wp_nonce_field('nonce_action','nonce_name')."'/>
en tu código, pero wp_nonce_field()
ya genera todo el <input ...>
por ti. Revisa el código fuente de tu página; probablemente verás algo como:
<input type='hidden' value='<input type="hidden" value="abcdef1234">' />
Cambia tu $out
por algo como esto:
$out .= "</ul>
<input type='submit' value='Enviar' name='crowd-cats-submit'/>
" . wp_nonce_field( 'action_name', 'nonce_name' ) . "
<input type='hidden' value='".esc_attr($tax)."' name='crowd-cats-tax'/>
<input type='hidden' value='$post_id' name='crowd-cats-pid'/>";

Uso firebug y puedo ver el valor en el código fuente, pero por alguna razón el valor, por ejemplo 52f4D1ee4, permanece estable en cada envío. Además, las categorías no se establecen.
