Passaggio di messaggi di errore/avviso da una meta box a "admin_notices"
Ho una semplice meta box che aggiorna i campi personalizzati del post (usando update_post_meta()
).
Come posso inviare un messaggio di errore o avviso alla pagina successiva quando l'utente pubblica/aggiorna il post senza compilare uno dei campi della meta box (o li compila con dati non validi)?

puoi usare l'hook admin_notices
prima definisci la funzione per il notice:
function my_admin_notice(){
//stampa il messaggio
echo '<div id="message">
<p>messaggio di errore del metabox durante il salvataggio qui!!!</p>
</div>';
//assicurati di rimuovere il notice dopo che è stato visualizzato, così viene mostrato solo quando necessario
remove_action('admin_notices', 'my_admin_notice');
}
Poi nella tua funzione di salvataggio del metabox, in base alla necessità aggiungi:
...
...
if($errors){
add_action('admin_notices', 'my_admin_notice');
}
...
...
Aggiornamento
Come promesso, ecco un esempio di come aggiungere un messaggio di errore dal mio metabox
<?php
/*
Plugin Name: one-trick-pony-notice
Plugin URI: http://en.bainternet.info
Description: Solo per dimostrare un punto usando admin notice da un metabox
Version: 1.0
Author: Bainternet
Author URI: http://en.bainternet.info
*/
/* admin notice */
function my_admin_notice(){
//stampa il messaggio
global $post;
$notice = get_option('otp_notice');
if (empty($notice)) return '';
foreach($notice as $pid => $m){
if ($post->ID == $pid ){
echo '<div id="message" class="error"><p>'.$m.'</p></div>';
//assicurati di rimuovere il notice dopo che è stato visualizzato, così viene mostrato solo quando necessario
unset($notice[$pid]);
update_option('otp_notice',$notice);
break;
}
}
}
//hooks
add_action('add_meta_boxes', 'OT_mt_add');
add_action('save_post', 'OT_mt_save');
add_action('admin_notices', 'my_admin_notice',0);
//aggiungi metabox
function OT_mt_add() {
add_meta_box('OT_mt_sectionid', __( 'Meta Box notice One Trick', 'textdomain' ),'OT_mt_display','post');
}
//visualizza metabox
function OT_mt_display() {
// Usa nonce per la verifica
wp_nonce_field( plugin_basename(__FILE__), 'myplugin_noncename' );
// I campi effettivi per l'inserimento dei dati
echo '<label for="myplugin_new_field">';
_e("lascia vuoto per ottenere un notice durante la pubblicazione o l'aggiornamento", 'textdomain' );
echo '</label> ';
echo '<input type="text" id="ot_field" name="ot_field" value="" size="25" />';
}
//salva metabox qui è dove controllo i campi e se vuoti mostro un messaggio
function OT_mt_save( $post_id ) {
// verifica che provenga dalla nostra schermata e con la giusta autorizzazione,
// perché save_post può essere attivato in altri momenti
if (!isset($_POST['myplugin_noncename'])) return $post_id;
if ( !wp_verify_nonce( $_POST['myplugin_noncename'], plugin_basename(__FILE__) ) )
return $post_id;
// verifica se si tratta di un salvataggio automatico
// Se lo è, il nostro form non è stato inviato, quindi non vogliamo fare nulla
if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE )
return $post_id;
if(!isset($_POST['ot_field']) || empty($_POST['ot_field'])){
//campo lasciato vuoto quindi aggiungiamo un notice
$notice = get_option('otp_notice');
$notice[$post_id] = "Hai lasciato il campo vuoto";
update_option('otp_notice',$notice);
}
}
Ora cercando questo codice ho trovato il mio vecchio modo di farlo usando l'hook filter post_updated_messages
più o meno nello stesso modo, quindi lo aggiungo anche:
<?php
/*
Plugin Name: one-trick-pony-notice2
Plugin URI: http://en.bainternet.info
Description: come quello sopra ma questa volta usando l'hook post_updated_messages
Version: 1.0
Author: Bainternet
Author URI: http://en.bainternet.info
*/
//hooks
add_filter('post_updated_messages','my_messages',0);
add_action('add_meta_boxes', 'OT_mt_add');
add_action('save_post', 'OT_mt_save');
//aggiungi metabox
function OT_mt_add() {
add_meta_box('OT_mt_sectionid', __( 'Meta Box notice One Trick', 'textdomain' ),'OT_mt_display','post');
}
//visualizza metabox
function OT_mt_display() {
// Usa nonce per la verifica
wp_nonce_field( plugin_basename(__FILE__), 'myplugin_noncename' );
// I campi effettivi per l'inserimento dei dati
echo '<label for="myplugin_new_field">';
_e("lascia vuoto per ottenere un notice durante la pubblicazione o l'aggiornamento", 'textdomain' );
echo '</label> ';
echo '<input type="text" id="ot_field" name="ot_field" value="" size="25" />';
}
//salva metabox qui è dove controllo i campi e se vuoti mostro un messaggio
function OT_mt_save( $post_id ) {
// verifica che provenga dalla nostra schermata e con la giusta autorizzazione,
// perché save_post può essere attivato in altri momenti
if (!isset($_POST['myplugin_noncename'])) return $post_id;
if ( !wp_verify_nonce( $_POST['myplugin_noncename'], plugin_basename(__FILE__) ) )
return $post_id;
// verifica se si tratta di un salvataggio automatico
// Se lo è, il nostro form non è stato inviato, quindi non vogliamo fare nulla
if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE )
return $post_id;
if(!isset($_POST['ot_field']) || empty($_POST['ot_field'])){
//campo lasciato vuoto quindi aggiungiamo un notice
$notice = get_option('otp_notice');
$notice[$post_id] = "Hai lasciato il campo vuoto";
update_option('otp_notice',$notice);
}
}
//filtro messaggi
function my_messages($m){
global $post;
$notice = get_option('otp_notice');
if (empty($notice)) return $m;
foreach($notice as $pid => $mm){
if ($post->ID == $pid ){
foreach ($m['post'] as $i => $message){
$m['post'][$i] = $message.'<p>'.$mm.'</p>';
}
unset($notice[$pid]);
update_option('otp_notice',$notice);
break;
}
}
return $m;
}

non funziona davvero perché dopo aver salvato il post, vieni reindirizzato e quindi quell'azione non viene mai eseguita...

Reindirizzato dove? E il codice sopra è quello che uso io, quindi so che funziona.

la tua funzione di salvataggio del metabox è agganciata a save_post
?

perché non funziona :) Dopo aver premuto il pulsante di pubblicazione, il post viene salvato e poi si viene reindirizzati nuovamente alla pagina di modifica.

Mi hai fatto preoccupare per un momento, l'avviso viene mostrato nella schermata di modifica e funziona perfettamente.

puoi postare il codice che stai utilizzando, la metabox e tutto il resto? Perché ho provato il tuo codice sopra ma non vedo alcun avviso proveniente dalla mia meta box...

grazie, ma questo fa la stessa cosa che Rarst ha evidenziato: il messaggio di errore viene salvato nel db, e poi recuperato e cancellato nella pagina successiva.

@One Trick Pony: sì, salva nel db ma è solo una bella opzione (una riga nel db) e viene cancellata automaticamente quando si usa il codice sopra, quindi non c'è bisogno di preoccuparsi che il db si riempia di transizioni.

-1 per l'uso di un DB. Non puoi garantire che l'utente corretto veda l'errore. Inoltre, non vale la pena il sovraccarico non necessario. Per non avere un modo chiaro per gestire gli errori dei metabox questo è un buon workaround ma comunque non efficiente. Ho aggiunto un esempio del modo in cui lo faccio in una nuova risposta per aiutare gli altri.

Questa risposta [mirror] di Otto su WP Tavern risolve effettivamente il problema dei transient facendo ciò che WordPress stesso fa per superare il problema di reindirizzamento. Ha funzionato perfettamente nel mio caso.
Il problema è che i transient sono disponibili per tutti. Se hai più di un utente che esegue operazioni contemporaneamente, il messaggio di errore può essere visualizzato dalla persona sbagliata. È una condizione di competizione.
WordPress gestisce questo passando un parametro message nell'URL. Il numero del messaggio indica quale messaggio visualizzare.
Puoi fare lo stesso agganciando il filtro
redirect_post_location
e poi usandoadd_query_arg
per aggiungere il tuo parametro alla richiesta. In questo modo:add_filter('redirect_post_location','my_message'); function my_message($loc) { return add_query_arg( 'my_message', 123, $loc ); }
Questo aggiunge
my_message=123
alla query. Poi, dopo il reindirizzamento, puoi rilevare l'impostazione my_message in$_GET
e visualizzare il messaggio appropriato di conseguenza.

Puoi farlo manualmente, ma WordPress lo fa nativamente in questo modo per gli errori delle impostazioni:
add_settings_error()
per creare il messaggio.- Poi
set_transient('settings_errors', get_settings_errors(), 30);
settings_errors()
nell'hookadmin_notices
per visualizzare (dovrai usare l'hook anche per schermate non relative alle impostazioni).

fa quello che voglio, ma questo non riempirebbe il database con tantissimi transient?

@One Trick Pony nel processo nativo il transient viene eliminato esplicitamente (vedi sorgente di get_settings_errors()
). Potresti doverlo fare manualmente se adatti la logica per una pagina che non è di impostazioni.

comunque non mi piace l'idea di memorizzare messaggi di errore temporanei nel database. Userò ajax per avvisare l'utente durante la modifica dell'input

So che questa domanda è vecchia ma trovo che le risposte qui non risolvano il problema.
Estendendo la risposta di Ana Ban, utilizzando il metodo di Otto, ho trovato che questo è il miglior metodo per gestire gli errori. Non richiede di memorizzare gli errori nel database.
Ho incluso una versione semplificata di un oggetto Metabox che utilizzo. Questo mi permette di aggiungere facilmente nuovi messaggi di errore e assicurarmi che l'utente corretto veda il messaggio di errore (usando il database, questa non è una garanzia).
<?php
/**
* Class MetaboxExample
*/
class MetaboxExample {
/**
* Definisce la whitelist per gli schermi consentiti (post_types)
*/
private $_allowedScreens = array( 'SCREENS_TO_ALLOW_METABOX' );
/**
* Parametro GET per il codice di errore del box errori
*/
const GET_METABOX_ERROR_PARAM = 'meta-error';
/**
* Definisce gli hook per l'admin
*/
public function __construct() {
add_action('add_meta_boxes', array($this, 'addMetabox'), 50);
add_action('save_post', array($this, 'saveMetabox'), 50);
add_action('edit_form_top', array($this, 'adminNotices')); // NOTA: admin_notices non posiziona correttamente questo sulle pagine dei custom post type, non ho testato questo su POST o PAGE ma non vedo questo come un problema
}
/**
* Aggiunge il metabox ai tipi di post specificati
*/
public function addMetabox() {
foreach ( $this->_allowedScreens as $screen ) {
add_meta_box(
'PLUGIN_METABOX',
__( 'TITOLO', 'text_domain' ),
array($this, 'metaBox'),
$screen,
'side',
'high'
);
}
}
/**
* Output del contenuto del metabox
* @param $post
*/
public function metaBox($post) {
// Aggiunge un campo nonce per poterlo verificare dopo
wp_nonce_field( 'metaboxnonce', 'metaboxnonce' );
// Carica i meta dati per questo metabox
$someValue = get_post_meta( $post->ID, 'META_KEY_IDENTIFIER', true );
?>
<p>
<label for="some-value" style="width: 120px; display: inline-block;">
<?php _e( 'Un campo:', 'text_domain' ); ?>
</label>
<input type="text" id="some-value" name="some_value" value="<?php esc_attr_e( $someValue ); ?>" size="25" />
</p>
<?php
}
/**
* Metodo di salvataggio per il metabox
* @param $post_id
*/
public function saveMetabox($post_id) {
global $wpdb;
// Controlla se il nostro nonce è impostato
if ( ! isset( $_POST['metaboxnonce'] ) ) {
return $post_id;
}
// Verifica che il nonce sia valido
if ( ! wp_verify_nonce( $_POST['metaboxnonce'], 'metaboxnonce' ) ) {
return $post_id;
}
// Se questo è un autosalvataggio, il nostro form non è stato inviato, quindi non vogliamo fare nulla
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return $post_id;
}
// Controlla i permessi dell'utente
if ( isset( $_POST['post_type'] ) && 'page' == $_POST['post_type'] ) {
if ( ! current_user_can( 'edit_page', $post_id ) ) {
return $post_id;
}
} else {
if ( ! current_user_can( 'edit_post', $post_id ) ) {
return $post_id;
}
}
// Assicurati che sia impostato
if ( !isset( $_POST['some_value'] ) ) {
return $post_id;
}
// Sanifica l'input dell'utente
$someValue = sanitize_text_field( $_POST['some_value'] );
// Controlla per assicurarsi che ci sia un valore
if (empty($someValue)) {
// Aggiungi il nostro codice di errore
add_filter('redirect_post_location', function($loc) {
return add_query_arg( self::GET_METABOX_ERROR_PARAM, 1, $loc );
});
return $post_id; // assicurati di restituire per non permettere ulteriori elaborazioni
}
// Aggiorna il campo meta nel database
update_post_meta( $post_id, 'META_KEY_IDENTIFIER', $someValue );
}
/**
* Notifiche admin del metabox
*/
public function adminNotices() {
if (isset($_GET[self::GET_METABOX_ERROR_PARAM])) {
$screen = get_current_screen();
// Assicurati che siamo nel tipo di post corretto
if (in_array($screen->post_type, $this->_allowedScreens)) {
$errorCode = (int) $_GET[self::GET_METABOX_ERROR_PARAM];
switch($errorCode) {
case 1:
$this->_showAdminNotice( __('È successo un errore', 'text_domain') );
break;
// Altri codici di errore vanno qui per mostrare errori
}
}
}
}
/**
* Mostra la notifica admin per il metabox
* @param $message
* @param string $type
*/
private function _showAdminNotice($message, $type='error') {
?>
<div class="<?php esc_attr_e($type); ?> below-h2">
<p><?php echo $message; ?></p>
</div>
<?php
}
}

L'unico problema che ho con questa risposta è che non funziona con PHP 5.2. Non sto dicendo che dovremmo tutti supportare PHP 5.2, ma finché WordPress non avrà PHP 5.2 come requisito minimo, dobbiamo supportarlo se distribuiamo il plugin :(

Se rimuovi la funzione anonima e la trasformi in un metodo pubblico, dovrebbe funzionare perfettamente. Capisco il tuo problema ma personalmente non svilupperò per una versione EOL di PHP (http://php.net/eol.php) 5.2 EOL era il 6 gennaio 2011. WordPress dovrebbe fare più sforzi per non supportare versioni EOL ma questa è un'altra storia, per non parlare dei tanti hosting provider scadenti che continuano a offrire versioni EOL...
