Ajouter la validation et la gestion des erreurs lors de l'enregistrement des champs personnalisés?

27

J'ai une fonction qui définit un champ personnalisé sur un type de message. Disons que le champ est "sous-titre".

Lorsque le message est enregistré, je souhaite effectuer une validation sur l'entrée et afficher un message d'erreur sur l'écran d'édition du message si nécessaire. Quelque chose comme:

// Handle post updating
function wpse_update_post_custom_values($post_id, $post) {

    // Do some checking...
    if($_POST['subhead'] != 'value i expect') {

        // Add an error here
        $errors->add('oops', 'There was an error.');

    }

    return $errors;

} 
add_action('save_post','wpse_update_post_custom_values',1,2);

J'essaie de lier cela à l'action save_post, mais je ne sais pas comment gérer les erreurs. Il ne semble pas y avoir d'objet d'erreur passé dans la fonction, et si je crée mon propre obj WP_Error et le renvoie, il n'est pas respecté par quelque mécanisme que ce soit qui crache des erreurs sur la page de post-édition.

J'ai actuellement un message d'erreur sur la page dans ma boîte de méta personnalisée, mais ce n'est pas idéal - je préfère avoir une grosse erreur rouge, en haut comme WP s'affiche normalement.

Des idées?

MISE À JOUR:

Sur la base de la réponse de @Denis, j'ai essayé plusieurs choses différentes. Le stockage des erreurs en tant que global n'a pas fonctionné, car Wordpress effectue une redirection pendant le processus save_post, qui tue le global avant de pouvoir l'afficher.

J'ai fini par les stocker dans un méta-champ. Le problème avec cela est que vous devez les effacer, ou ils ne disparaîtront pas lorsque vous accédez à une autre page, j'ai donc dû ajouter une autre fonction attachée à admin_footer qui efface simplement les erreurs.

Je ne m'attendais pas à ce que la gestion des erreurs pour quelque chose d'aussi commun (mise à jour des messages) soit aussi maladroite. Suis-je en train de manquer quelque chose d'évident ou est-ce la meilleure approche?

// Handle post updating
function wpse_5102_update_post_custom_values($post_id, $post) {

    // To keep the errors in
    $errors = false;

    // Do some validation...
    if($_POST['subhead'] != 'value i expect') {

        // Add an error here
        $errors .= 'whoops...there was an error.';

    }

    update_option('my_admin_errors', $errors);

    return;

} 
add_action('save_post','wpse_5102_update_post_custom_values',1,2);


// Display any errors
function wpse_5102_admin_notice_handler() {

    $errors = get_option('my_admin_errors');

    if($errors) {

        echo '<div class="error"><p>' . $errors . '</p></div>';

    }   

}
add_action( 'admin_notices', 'wpse_5102_admin_notice_handler' );


// Clear any errors
function wpse_5102__clear_errors() {

    update_option('my_admin_errors', false);

}
add_action( 'admin_footer', 'wpse_5102_clear_errors' );
MathSmath
la source
Bonne question. Je pense que vous pourriez vous débarrasser du admin_footercrochet si vous supprimez les erreurs à la fin de votre fonction de gestionnaire d'avis. Simplifie un peu les choses.
Geert
Comment gérez-vous le repeuplement des champs du formulaire (avec les éventuelles données invalides)?
Geert
J'ai une question basique. Dans quel fichier Wordpress php se trouve-t-il?
@Karen Ce serait dans un fichier de plugin personnalisé ou dans votre functions.php.
MathSmath
Il se peut que je manque quelque chose d'évident, mais serait-il un peu plus efficace de s'exécuter update_option('my_admin_errors', false);immédiatement après l'instruction if à la fin de wpse_5102_admin_notice_handler()?
Andrew Odri

Réponses:

6

Stockez les erreurs dans votre classe ou en tant que global, éventuellement dans un transitoire ou une méta, et affichez-les dans les notifications d'administration sur les requêtes POST. WP ne comporte aucun gestionnaire de messages flash.

Denis de Bernardy
la source
Merci de m'avoir orienté dans cette direction! J'ai fini par utiliser une méta pour stocker les erreurs, car j'avais des problèmes à essayer de le faire en tant que global ou en tant que propriété. Je mets à jour ma réponse en ce moment pour expliquer comment je le fais ... s'il vous plaît laissez-moi savoir si c'est le genre de chose que vous proposiez, ou s'il y a une meilleure façon que je n'obtiens pas.
MathSmath
Ce genre de chose, oui. Peut-être que le stocker dans une variable de session, à la réflexion, cependant. Ceci, afin de permettre à plusieurs auteurs de modifier des articles en même temps. :-) De plus, je pense qu'il n'est pas possible de stocker des faux dans une option. Stockez une chaîne vide à la place.
Denis de Bernardy
6

Je suggère d'utiliser des sessions car cela ne créera pas d'effets étranges lorsque deux utilisateurs éditeront en même temps. Voilà donc ce que je fais:

Les sessions ne sont pas démarrées par wordpress. Vous devez donc démarrer une session dans votre plugin, functions.php ou même wp-config.php:

if (!session_id())
  session_start();

Lors de l'enregistrement du message, ajoutez des erreurs et des avis à la session:

function my_save_post($post_id, $post) {
   if($something_went_wrong) {
     //Append error notice if something went wrong
     $_SESSION['my_admin_notices'] .= '<div class="error"><p>This or that went wrong</p></div>';
     return false; //might stop processing here
   }
   if($somthing_to_notice) {  //i.e. successful saving
     //Append notice if something went wrong
     $_SESSION['my_admin_notices'] .= '<div class="updated"><p>Post updated</p></div>';
   }

   return true;
} 
add_action('save_post','my_save_post');

Imprimez les notifications et les erreurs, puis nettoyez les messages de la session:

function my_admin_notices(){
  if(!empty($_SESSION['my_admin_notices'])) print  $_SESSION['my_admin_notices'];
  unset ($_SESSION['my_admin_notices']);
}
add_action( 'admin_notices', 'my_admin_notices' );
davidn
la source
correction pour la version de session: lors de la première utilisation de la variable de session, n'utilisez pas. = seulement = si vous activez le débogage, vous pouvez vérifier pourquoi ...
3
Je l'ai fait aussi, mais si vous publiez un plugin à un large public comme ça, les gens finiront par vous détester pour ça. Wordpress n'instancie pas les sessions car il est conçu pour être apatride et ne pas en avoir besoin, et certaines configurations de serveur étranges vont le casser. Utilisez l'API transitoires - codex.wordpress.org/Transients_API au lieu des sessions et vous conserverez la compatibilité. Je pensais juste qu'il valait la peine de signaler une raison pour laquelle ne pas le faire ici.
pospi
@pospi, cela semble avoir des problèmes similaires à l'utilisation d'origine des fonctions get_option et update_option. Donc je suppose que la solution serait d'ajouter l'ID de l'utilisateur actuel à la clé?
Gazillion
Ouais, ça marcherait totalement! Tant que vous ajoutez quelque chose pour identifier l'utilisateur de manière unique, vous éviterez que les messages ne soient mélangés entre les utilisateurs connectés (:
pospi
5

Sur la base de la suggestion de pospi d'utiliser des transitoires , j'ai trouvé ce qui suit. Le seul problème est qu'il n'y a pas de crochet pour mettre le message en dessous de l' endroit où vont les autres messages, j'ai donc dû faire un hack jQuery pour y arriver.h2

Tout d'abord, enregistrez le message d'erreur concernant votre gestionnaire save_post(ou similaire). Je lui donne une courte durée de vie de 60 secondes, il est donc juste assez long pour que la redirection se produise.

if($has_error)
{
  set_transient( "acme_plugin_error_msg_$post_id", $error_msg, 60 );
}

Ensuite, récupérez simplement ce message d'erreur lors du chargement de la page suivante et affichez-le. Je le supprime également pour qu'il ne s'affiche pas deux fois.

add_action('admin_notices', 'acme_plugin_show_messages');

function acme_plugin_show_messages()
{
  global $post;
  if ( false !== ( $msg = get_transient( "acme_plugin_error_msg_{$post->ID}" ) ) && $msg) {
    delete_transient( "acme_plugin_error_msg_{$post->ID}" );
    echo "<div id=\"acme-plugin-message\" class=\"error below-h2\"><p>$msg</p></div>";
  }
}

Étant donné que des admin_noticesincendies avant que le contenu de la page principale ne soit généré, l'avis n'est pas là où vont les autres messages de post-édition, j'ai donc dû utiliser ce jQuery pour le déplacer là-bas:

jQuery('h2').after(jQuery('#acme-plugin-message'));

Étant donné que l'ID de publication fait partie du nom transitoire, cela devrait fonctionner dans la plupart des environnements multi-utilisateurs, sauf lorsque plusieurs utilisateurs modifient simultanément la même publication.

Joshua Coady
la source
Pourriez-vous élaborer sur «Puisque l'ID de poste fait partie du nom transitoire»? J'ai créé une classe pour gérer les messages d'erreur à l'aide de cette technique, mais j'ai besoin que mon constructeur passe un ID_utilisateur. L'API transitoire utilise-t-elle l'ID utilisateur lors du hachage de la clé? (Je demande parce que le codex ne semble pas le mentionner)
Gazillion
Non, mais vous pouvez l'ajouter manuellement. Dans le code que j'ai affiché ci-dessus, le nom du transitoire est acme_plugin_error_msg_POSTID. Vous pouvez simplement ajouter un ID utilisateur à cela acme_plugin_error_msg_POSTID_USERID.
Joshua Coady
2

Lors de l' save_postexécution, il a déjà enregistré le message dans la base de données.

En regardant dans le code de base WordPress, plus précisément à la wp-includes/post.phpde » update_post()fonction, il n'y a aucun moyen intégré pour intercepter une requête avant qu'elle ne soit enregistrée sur la base de données.

Cependant, nous pouvons accrocher pre_post_updateet utiliser header()et get_post_edit_link()pour empêcher la publication du message.

<?php

/**
*   Performs validation before saving/inserting custom post type
*/
function custom_post_site_save($post_id, $post_data) {
    // If this is just a revision, don't do anything.
    if (wp_is_post_revision($post_id))
        return;

    if ($post_data['post_type'] == 'my_custom_post_type') {
        // Deny post titles with less than 5 characters
        if (strlen($post_data['post_title'] < 5)) {
            header('Location: '.get_edit_post_link($post_id, 'redirect'));
            exit;
        }
    }
}
add_action( 'pre_post_update', 'custom_post_site_save', 10, 2);

Si vous devez informer l'utilisateur de ce qui s'est mal passé, vérifiez cet essentiel: https://gist.github.com/Luc45/09f2f9d0c0e574c0285051b288a0f935

Lucas Bustamante
la source
Merci pour cela, gère parfaitement la validation, que ce soit pour la première publication ou la mise à jour du message. Vous venez de m'économiser beaucoup de temps et d'efforts.
Zade
1

Pourquoi ne validez-vous pas votre champ à l'aide de Javascript? Je pense que ce serait la meilleure approche pour cela.

Horttcore
la source
Merci pour la suggestion! Ce que j'ai laissé de côté (pour des raisons de simplicité), c'est que j'essaie de gérer les erreurs de téléchargement de fichiers, il doit donc être côté serveur. Merci pour la suggestion!
MathSmath
la validation javascript n'empêche pas certaines attaques, la validation côté serveur est la seule sécurisée. De plus, wordpress offre de bons outils pour valider les données des utilisateurs. Mais vous avez raison s'il vérifie simplement certaines valeurs avant d'envoyer des données au serveur, vous pouvez gagner du temps sur un serveur bas ^^
nderambure
1

En essayant d'utiliser le script ci-dessus, j'ai rencontré un problème étrange. Deux messages s'affichent sur l'écran d'édition, après la mise à jour post. L'un montre l'état du contenu de la sauvegarde précédente et un autre de la sauvegarde actuelle. Par exemple, si j'enregistre correctement le message et que je fais ensuite une erreur, le premier est "erreur" et le second est "ok" - bien qu'ils soient générés en même temps. Si je change le script et que j'ajoute un seul message (par exemple "erreur"), lance une mise à jour avec "erreur" et ensuite une autre avec "ok", le message "erreur" reste (s'affiche pour la deuxième fois). Je dois encore enregistrer avec "ok" pour m'en débarrasser. Je ne sais vraiment pas ce qui ne va pas, je l'ai testé sur trois serveurs locaux différents et il y a le même problème sur chacun d'eux.

jlub
la source
J'ai fait quelques tests supplémentaires de la deuxième version de script plus simple que j'ai mentionnée ci-dessus et il semble que si le message "d'erreur" est vraiment ajouté au tableau de session, il s'affiche sur l'écran d'édition. S'il n'y a pas de message (tout est "ok") et que le message précédent était une erreur, il apparaît à l'écran. Ce qui est étrange, il est généré au moment de l'enregistrement (non mis en cache) - je l'ai vérifié en utilisant date () dans le corps du message d'erreur. Je suis totalement confus maintenant.
jlub
Ok, au cas où quelqu'un d'autre arracherait les cheveux de sa tête - il s'est avéré que le système de révision Wordpress était le problème (une sorte de bug probablement?). Je l'ai désactivé et maintenant tout va bien.
0

J'ai écrit un plugin qui ajoute une gestion des erreurs flash pour les écrans de modification de publication et empêche la publication des publications jusqu'à ce que les champs obligatoires soient remplis:

https://github.com/interconnectit/required-fields

Il vous permet de rendre tous les champs de publication obligatoires, mais vous pouvez utiliser l'API qu'il fournit pour rendre tous les champs personnalisés requis également avec un message d'erreur personnalisable et une fonction de validation. Par défaut, il vérifie si le champ est vide ou non.

sanchothefat
la source
N'hésitez pas à ajouter des problèmes sur github si vous les rencontrez. Je dois également documenter un peu mieux l'API car il existe des filtres supplémentaires que vous pouvez utiliser.
sanchothefat