159

Éviter le renvoi d'un formulaire

Créez site13 en copiant site12.

  1. /cms
    1. ...
    2. site12
    3. site13

Dans ce chapitre, nous allons programmer la lecture d'un formulaire afin d'éviter de l'exécuter après un simple renvoi par un navigateur ou un robot.

Pour tester le résultat en ligne, entrez http://www.frasq.org/cms/site13/fr/contact dans la barre d'adresse de votre navigateur. Remplissez le formulaire et appuyez sur Envoyer. Le formulaire revient avec un message de confirmation. Le sujet et le texte du message sont effacés. Revenez à la page précédente dans l'historique du navigateur. La page affiche le formulaire avec le sujet et le texte du message que vous avez déjà envoyés. Cliquez sur Envoyer. Le formulaire revient inchangé et sans message de confirmation. Il n'a pas été exécuté une seconde fois.

Pour s'assurer que le dialogue est continu, non répété, le programme doit vérifier que le formulaire qui est reçu est le dernier qui a été envoyé. Un marqueur unique, aussi appelé un jeton, qui est sauvé dans la session, est ajouté au formulaire dans un champ caché, puis, quand le formulaire revient, le marqueur extrait du formulaire est comparé avec celui en mémoire. Si les valeurs ne correspondent pas, le formulaire n'est pas validé.

Ajoutez le fichier tokenid.php dans le dossier library avec le contenu suivant :

  1. /cms/site13
    1. library
      1. tokenid.php
  1. function token_id() {
  2.     return md5(uniqid(rand(), TRUE));
  3. }

token_id retourne le hachage MD5 sur 32 octets de l'identifiant unique retourné par la fonction PHP uniqid.

$ php -a
php > echo md5(uniqid(rand(), TRUE));
3018357bade6cfcab3d0b1abd51add45

Modifiez le fichier blocks/mailme.php qui définit la fonction mailme qui génère le bloc du formulaire :

  1. require_once 'tokenid.php';

Charge le code de la fonction token_id.

  1.     $_SESSION['mailme_token'] = $token = token_id();

Enregistre dans la session le marqueur unique généré par la fonction token_id.

  1.     $mail=$subject=$message=$code=$token=false;

Initialise les variables lues dans le formulaire.

  1.             if (isset($_POST['mailme_token'])) {
  2.                 $token=readarg($_POST['mailme_token']);
  3.             }

Lit la valeur du jeton dans le formulaire.

  1.     $bad_token=false;

Initialise la variable d'erreur qui contient le résultat du test du jeton.

  1.     switch($action) {
  2.         case 'send':
  3.             if (!isset($_SESSION['mailme_token']) or $token != $_SESSION['mailme_token']) {
  4.                 $bad_token=true;
  5.                 break;
  6.             }

Vérifie si le jeton en mémoire et le jeton du formulaire correspondent.

  1.     switch($action) {
  2.         case 'send':
  3.             if ($bad_token or $missing_code or $bad_code or $missing_mail or $bad_mail or $missing_subject or $bad_subject or $missing_message) {
  4.                 break;
  5.             }

N'exécute pas le formulaire si le jeton n'a pas été validé.

  1.     $with_captcha=false;
  1.             if ($with_captcha) {
  2.                 if (!$code) {
  3.                     $missing_code=true;
  4.                     break;
  5.                 }
  6.                 $captcha=isset($_SESSION['captcha']) ? $_SESSION['captcha'] : false;
  7.                 if (!$captcha or $captcha != strtoupper($code)) {
  8.                     $bad_code=true;
  9.                     break;
  10.                 }
  11.             }

Rend le captcha optionnel. NOTE : L'utilisation d'un captcha rend un jeton redondant.

  1.     $output = view('mailme', $lang, compact('token', 'with_captcha', 'mail', 'subject', 'message', 'errors', 'infos'));

Génère la vue en lui passant la valeur du jeton et l'option du captcha.

Remarquez que mailme filtre l'adresse d'email avec la fonction strflat :

  1.     switch($action) {
  2.         case 'send':
  3.             if (isset($_POST['mailme_mail'])) {
  4.                 $mail=strtolower(strflat(strip_tags(readarg($_POST['mailme_mail'], true))));
  5.             }

Ajoutez le fichier strflat.php dans le dossier library avec le contenu suivant :

  1. /cms/site13
    1. library
      1. strflat.php
  1. function strflat($s) {
  2.     $from = array(
  3.                 'à', 'â', 'ä', 'á', 'ã', 'å',
  4.                 'î', 'ï', 'ì', 'í',
  5.                 'ô', 'ö', 'ò', 'ó', 'õ', 'ø',
  6.                 'ù', 'û', 'ü', 'ú',
  7.                 'é', 'è', 'ê', 'ë',
  8.                 'ç', 'ñ',
  9.                 'À', 'Â', 'Ä', 'Á', 'Ã', 'Å',
  10.                 'Î', 'Ï', 'Ì', 'Í',
  11.                 'Ô', 'Ö', 'Ò', 'Ó', 'Õ', 'Ø',
  12.                 'Ù', 'Û', 'Ü', 'Ú',
  13.                 'É', 'È', 'Ê', 'Ë',
  14.                 'Ç', 'Ñ',
  15.     );
  16.  
  17.     $to = array(
  18.                 'a', 'a', 'a', 'a', 'a', 'a',
  19.                 'i', 'i', 'i', 'i',
  20.                 'o', 'o', 'o', 'o', 'o', 'o',
  21.                 'u', 'u', 'u', 'u',
  22.                 'e', 'e', 'e', 'e',
  23.                 'c', 'n',
  24.                 'A', 'A', 'A', 'A', 'A', 'A',
  25.                 'I', 'I', 'I', 'I',
  26.                 'O', 'O', 'O', 'O', 'O', 'O',
  27.                 'U', 'U', 'U', 'U',
  28.                 'E', 'E', 'E', 'E',
  29.                 'C', 'N',
  30.     );
  31.  
  32.     return str_replace($from, $to, $s);
  33. }

strflat retourne $s sans accents.

Modifiez la vue du formulaire, en français et en anglais, pour ajouter le jeton et gérer l'affichage optionnel du captcha :

  1. /cms/site13
    1. views
      1. en
        1. mailme.phtml
      2. fr
        1. mailme.phtml
  1. <form action="" method="post">
  2. <input type="hidden" name="mailme_token" value="<?php echo $token; ?>" />

Ajoute le champ caché mailme_token au formulaire.

  1. <?php if ($with_captcha): ?>
  2. <img class="captcha" src="<?php echo $base_path; ?>/captcha" alt="" title="Code de vérification" />

N'affiche pas le captcha si $with_captcha n'est pas true.

Appliquez les mêmes modifications à la version en anglais :

  1. <?php if ($with_captcha): ?>
  2. <img class="captcha" src="<?php echo $base_path; ?>/captcha" alt="" title="Verification code" />

Pour envoyer le formulaire ou déplacer le focus d'un champ à un autre quand l'utilisateur appuie sur la touche Entrée, il faut un peu de code en Javascript. Ajoutez un dossier appelé js directement à la racine du site :

  1. /cms/site13
    1. js

Ajoutez le fichier tools.js dans le dossier js avec le contenu suivant :

  1. /cms/site13
    1. js
      1. tools.js
  1. function focusonenter(e, id) {
  2.     var keycode;
  3.    
  4.     if (window.event)
  5.         keycode = window.event.keyCode;
  6.     else if (e)
  7.         keycode = e.which;
  8.     else
  9.         return true;
  10.  
  11.     if (keycode == 13) {
  12.         var field = document.getElementById(id);
  13.         if (field)
  14.             field.focus();
  15.         return false;
  16.     }
  17.     else
  18.         return true;
  19. }

La fonction focusonenter déplace le focus du navigateur sur la balise id si un événement clavier avec le code 13, la touche Entrée, a été détecté.

  1. function submitonenter(e, id) {
  2.     var keycode;
  3.    
  4.     if (window.event)
  5.         keycode = window.event.keyCode;
  6.     else if (e)
  7.         keycode = e.which;
  8.     else
  9.         return true;
  10.  
  11.     if (keycode == 13) {
  12.         var button = document.getElementById(id);
  13.         if (button)
  14.             button.click();
  15.         return false;
  16.     }
  17.     else
  18.         return true;
  19. }

La fonction submitonenter active le clic associé au bouton id si un événement clavier avec le code 13, la touche Entrée, a été détecté.

Modifiez les vues pour ajouter les appels aux fonctions focusonenter et submitonenter :

  1. <div class="fields">
  2. <p class="label">Quelle est votre adresse d'email ?</p>
  3. <p class="input"><input type="text" name="mailme_mail" id="mailme_mail" size="50" maxlength="100" title="Email" onkeypress="return focusonenter(event, 'mailme_subject')" value="<?php echo htmlspecialchars($mail, ENT_COMPAT, 'UTF-8'); ?>" /></p>
  4. <p class="info">Votre adresse d'email est strictement confidentielle.</p>
  5. <p class="label">Tapez le sujet et le texte de votre message&nbsp;:</p>
  6. <p class="input"><input class="monospace" type="text" name="mailme_subject" id="mailme_subject" size="60" maxlength="100" title="Sujet" onkeypress="return focusonenter(event, 'mailme_message')" value="<?php echo htmlspecialchars($subject, ENT_COMPAT, 'UTF-8'); ?>" /></p>
  7. <p class="input"><textarea class="monospace" name="mailme_message" id="mailme_message" cols="70" rows="8" title="Texte"><?php echo htmlspecialchars($message, ENT_COMPAT, 'UTF-8'); ?></textarea></p>
  8. <p class="input">
  9. <?php if ($with_captcha): ?>
  10. <img class="captcha" src="<?php echo $base_path; ?>/captcha" alt="" title="Code de vérification" />
  11. &nbsp;:&nbsp;
  12. <input type="text" name="mailme_code" id="mailme_code" size="4" maxlength="4" title="4 lettres" onkeypress="return submitonenter(event, 'mailme_send')" value="" />
  13. <?php endif; ?>
  14. </p>
  15. <p class="submit"><button type="submit" name="mailme_send" id="mailme_send">Envoyer</button></p>
  16. </div>

L'attribut onkeypress="return focusonenter(event, 'mailme_subject')" du champ mailme_mail déplace le focus sur le champ mailme_subject. Appuyer sur la touche Entrée dans le champ mailme_subject passe au champ mailme_message. L'attribut onkeypress="return submitonenter(event, 'mailme_send')" du champ mailme_code envoie le formulaire si l'utilisateur appuie sur Entrée dans le champ de saisie du captcha.

Appliquez les mêmes modifications à la version en anglais :

  1. <div class="fields">
  2. <p class="label">What is your email address?</p>
  3. <p class="input"><input type="text" name="mailme_mail" id="mailme_mail" size="50" maxlength="100" title="Email" onkeypress="return focusonenter(event, 'mailme_subject')" value="<?php echo htmlspecialchars($mail, ENT_COMPAT, 'UTF-8'); ?>" /></p>
  4. <p class="info">Your email address is strickly confidential.</p>
  5. <p class="label">Type in the subject and the text of your message:</p>
  6. <p class="input"><input class="monospace" type="text" name="mailme_subject" id="mailme_subject" size="60" maxlength="100" title="Subject" onkeypress="return focusonenter(event, 'mailme_message')" value="<?php echo htmlspecialchars($subject, ENT_COMPAT, 'UTF-8'); ?>" /></p>
  7. <p class="input"><textarea class="monospace" name="mailme_message" id="mailme_message" cols="70" rows="8" title="Text"><?php echo htmlspecialchars($message, ENT_COMPAT, 'UTF-8'); ?></textarea></p>
  8. <p class="input">
  9. <?php if ($with_captcha): ?>
  10. <img class="captcha" src="<?php echo $base_path; ?>/captcha" alt="" title="Verification code" />
  11. &nbsp;:&nbsp;
  12. <input type="text" name="mailme_code" id="mailme_code" size="4" maxlength="4" title="4 letters" onkeypress="return submitonenter(event, 'mailme_send')" value="" />
  13. <?php endif; ?>
  14. </p>
  15. <p class="submit"><button type="submit" name="mailme_send" id="mailme_send">Send</button></p>
  16. </div>

Ajoutez le chargement du fichier js/tools.js directement dans la fonction run définie dans le fichier library/engine.php :

  1.     head('javascript', 'tools');

Entrez http://localhost/cms/site13/fr/contact dans la barre d'adresse de votre navigateur. Remplissez le formulaire en validant les champs avec Entrée. Appuyez sur Envoyer. Revenez en arrière dans l'historique. Renvoyez le formulaire et vérifiez qu'il n'a pas été exécuté une seconde fois.

Mettez $with_captcha dans blocks/mailme.php à true. Rechargez le formulaire et remplissez-le. Validez le code de vérification avec Entrée pour envoyer le formulaire.

Commentaires

Votre commentaire :
[p] [b] [i] [u] [s] [quote] [pre] [br] [code] [url] [email] strip aide 2000

Entrez un maximum de 2000 caractères.
Améliorez la présentation de votre texte avec les balises de formatage suivantes :
[p]paragraphe[/p], [b]gras[/b], [i]italique[/i], [u]souligné[/u], [s]barré[/s], [quote]citation[/quote], [pre]tel quel[/pre], [br]à la ligne,
[url]http://www.izend.org[/url], [url=http://www.izend.org]site[/url], [email]izend@izend.org[/email], [email=izend@izend.org]izend[/email],
[code]commande[/code], [code=langage]code source en c, java, php, html, javascript, xml, css, sql, bash, dos, make, etc.[/code].