Page 1 sur 2

Valider plusieurs fois un formulaire sans actualiser la page

Posté : 19 avr. 2021, 11:25
par jeicko
Bonjour à tous,

Sur mon site web, j’ai une rubrique « Bla-Bla » pour laisser des commentaires.

Actuellement, quand on envoie un commentaire, on clique sur le bouton « envoyer » puis il y a une alerte « Ton message a bien été envoyé »

Si je veux envoyer un 2e message dans la foulée le message part bien car je le reçois par texto mais il est vide (avec l'api free dans ma configuration php) et le message ne s'affiche pas sur mon site et l'erreur "OOPS ! TON MESSAGE NE VEUT PAS PARTIR.. RÉESSAIE !" apparait. Seul le premier envoi s'affiche. Je suis bloqué. J’ai passé des heures dessus et c’est la dernière partie de mon site après mon site est terminé..

Si quelqu’un veut bien me montrer ce que je fais de mal svp
Je mets tout le code si quelqu’un veut l’essayer.
En espérant que quelqu’un veut bien m’aider

Je vous montre mon code :

add_comment.php

Code : Tout sélectionner

<?php //add_comment.php $connect = new PDO('***'); $error = ''; $comment_name = ''; $comment_content = ''; $email = ''; $date = new \DateTime($row["date"]); $formatedDate = (new IntlDateFormatter('fr_FR', IntlDateFormatter::LONG, IntlDateFormatter::SHORT)); // DEBUT RECAPTCHA // On vérifie si le champ "recaptcha-response" contient une valeur if (empty($_POST['recaptcha-response'])) { header('Location: index.html'); die(); } else { // On prépare l'URL $url = "https://www.google.com/recaptcha/api/siteverify?secret=***&response={$_POST['recaptcha-response']}"; // On vérifie si curl est installé if (function_exists('curl_version')) { $curl = curl_init($url); curl_setopt($curl, CURLOPT_HEADER, false); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl, CURLOPT_TIMEOUT, 1); curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); $response = curl_exec($curl); } else { // On utilisera file_get_contents $response = file_get_contents($url); } // On vérifie qu'on a une réponse if (empty($response) || is_null($response)) { header('Location: index.html'); die(); } else { $data = json_decode($response); if ($data->score >= 0.5) { // je réalise mon action de formulaire. if (empty($_POST["comment_name"])) { $error .= '<p class="text-danger"></p>'; } else { $comment_name = $_POST["comment_name"]; } if (empty($_POST["comment_content"])) { $error .= '<p class="text-danger"></p>'; } else { $comment_content = $_POST["comment_content"]; } if (empty($_POST["email"])) { $error .= '<p class="text-danger"></p>'; } else { $email = $_POST["email"]; } if ($error == '') { $query = " INSERT INTO tbl_comment (parent_comment_id, comment, comment_sender_name, email) VALUES (:parent_comment_id, :comment, :comment_sender_name, :email) "; $statement = $connect->prepare($query); $statement->execute(array( ':parent_comment_id' => $_POST["comment_id"], ':comment' => $comment_content, ':comment_sender_name' => $comment_name, ':email' => $_POST["email"] )); $error = '<label class="validation">Ton message a bien été envoyé !</label>'; } else { header('Location: index.html'); die(); } } else { $error = '<label class="validation">OOPS ! Ton message ne veut pas partir.. Réessaie !</label>'; // tu es potentiellement un robot. } } } // FIN RECAPTCHA // DEBUT FREE MOBILE SMS $commentaireHTML = "<html> <head> <title>Nouveau Commentaire !</title> </head> <body> <p>Monsite.com</p> <p>Rubrique Bla-Bla :</p> <ul> <li><b>Le </b>" . strip_tags($formatedDate->format($date)) . "</li> <li><b>Email : </b>" . strip_tags($email) . "</li> <li><b>Pseudo : </b>" . strip_tags($comment_name) . "</li> <li><b>Commentaire : </b>" . nl2br(strip_tags($comment_content)) . "</li> </ul> </body> </html> "; // inclure ici le fichier de la classe require_once "FreeMobileSMS.php"; $sms = new FreeMobileSMS(); /** * configure l'ID utilisateur et la clé disponible dans * le compte Free Mobile après avoir activé l'option. */ $sms->setKey("***") ->setUser("***"); try { // envoi d'un message $sms->send(html_entity_decode(strip_tags("$commentaireHTML"))); } catch(Exception $e) { // il y aura peut-être des erreurs. $this ->Session ->setFlash("Erreur sur envoi de SMS: (" . $e->getCode() . ") " . $e->getMessage() , 'danger'); } // FIN FREE MOBILE SMS $data = array( 'error' => $error ); echo json_encode($data); ?>
Fetch_comment.php

Code : Tout sélectionner

<?php //fetch_comment.php $connect = new PDO('***'); $output = get_reply_comment($connect, 0); // appel la fonction récursive à partir du niveau 0 echo $output; function get_reply_comment($connect, $parentId = 0, $marginleft = 0) { $output = ''; $query = " SELECT * FROM tbl_comment WHERE parent_comment_id = :parentId ORDER BY comment_id DESC "; // j'ai ajouté le order by ici $statement = $connect->prepare($query); $statement->execute(array( ':parentId' => $parentId )); $result = $statement->fetchAll(); if ($parentId == 0) { $marginleft = 0; } else { $marginleft = $marginleft + 50; } if ($result) { foreach ($result as $row) { $date = new \DateTime($row["date"]); $formatedDate = (new IntlDateFormatter('fr_FR', IntlDateFormatter::LONG, IntlDateFormatter::SHORT)); $output .= ' <div class="separator"</div> <div class="box-light" style="margin-left:' . $marginleft . 'px"> <div class="chapeau">@ <b>' . $row["comment_sender_name"] . '</b></div><div class="white"><i>' . $formatedDate->format($date) . '</i></div> <div class="texte-com">' . nl2br($row["comment"]) . '</div> <div class="reponse"><button type="button" class="button1 reply" id="' . $row["comment_id"] . '">Répondre</button></div> </div> '; $output .= get_reply_comment($connect, $row["comment_id"], $marginleft); } } return $output; } ?>

Commentaires.Js

Code : Tout sélectionner

$(document).ready(function() { //quand le DOM est là, on sélectionne les éléments avec lesquels on va travailler //c'est mieux de le faire une fois au début, pour des questions de perf //mais surtout pour éviter d'avoir à faire un gros ctrl + r pour changer les sélecteurs. const $comment_form = $("#comment_form"); const $comment_message = $("#comment_message"); const $display_comment = $("#display_comment"); $comment_form.on("submit", function(event) { event.preventDefault(); const form_data = $comment_form.serialize(); $.ajax({ url: "add_comment.php", method: "POST", data: form_data, dataType: "JSON", success: function(data) { if (data.error !== "") { $comment_form[0].reset(); $comment_message.html(data.error); //un formulaire (élément) contient tous ses inputs //c'est rangé par nom $comment_form[0].comment_id.value = "0"; load_comment(); } }, }); }); reload(); ////////////////////////////////// function load_comment() { $.ajax({ url: "fetch_comment.php", //POST pour envoyer des données, et GET pour obtenir des données method: "POST", success: function(data) { $display_comment.html(data); $(".reply").on("click", function() { $comment_form[0].comment_id.value = $(this).attr("id"); $comment_form[0].comment_name.focus(); }); }, }); } function reload() { load_comment(); setTimeout(reload, 1000); } });
Html

Code : Tout sélectionner

<!-- Début Commentaires --> <form action="add_comment.php" id="comment_form" method="POST"> <div class="fields"> <div class="field half"> <label for="name">Pseudo</label> <input type="text" name="comment_name" id="comment_name" required /> </div> <div class="field half"> <label for="email">Email</label> <input type="email" name="email" id="email" required /> </div> <div class="field"> <label for="message">Message</label> <textarea name="comment_content" id="comment_content" rows="4" required ></textarea> <span id="comment_message"></span> <!--Ton message a bien été envoyé--> <!--Une «alerte» div cachée ci-dessous pour afficher le message reçu du serveur une fois le formulaire soumis--> <div id="alert2"></div> <!--recaptcha cachée ci-dessous--> <input type="hidden" class="grecaptcha-badge" id="recaptchaResponse-com" name="recaptcha-response" /> <input type="hidden" name="comment_id" id="comment_id" value="0" /> <!--Fin alerte--> </div> </div> <ul class="actions"> <li> <button class="button1 primary" type="submit">Envoyer</button> </li> <li> <input class="button1" type="reset" value="Effacer" /> </li> </ul> </form> <div id="display_comment"></div> <!-- Fin Commentaires -->
Sql

Code : Tout sélectionner

-- -- Database: `commentaires` -- -- -------------------------------------------------------- -- -- Table structure for table `tbl_comment` -- CREATE TABLE IF NOT EXISTS `tbl_comment` ( `comment_id` int(11) NOT NULL, `parent_comment_id` int(11) NOT NULL, `comment` varchar(200) NOT NULL, `comment_sender_name` varchar(40) NOT NULL, `email` varchar(50) NOT NULL, `date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- -- Indexes for dumped tables -- -- -- Indexes for table `tbl_comment` -- ALTER TABLE `tbl_comment` ADD PRIMARY KEY (`comment_id`); -- -- AUTO_INCREMENT for dumped tables -- -- -- AUTO_INCREMENT for table `tbl_comment` -- ALTER TABLE `tbl_comment` MODIFY `comment_id` int(11) NOT NULL AUTO_INCREMENT;

Re: Valider plusieurs fois un formulaire sans actualiser la page

Posté : 19 avr. 2021, 11:45
par @rthur
Quelle est la condition pour que s'affiche le message "OOPS ! TON MESSAGE NE VEUT PAS PARTIR" ?
Du coup il faut commencer par chercher par là :)

Re: Valider plusieurs fois un formulaire sans actualiser la page

Posté : 19 avr. 2021, 12:13
par jeicko
En théorie j’ai cette erreur si le recaptcha détecte que je suis un robot, mais je ne comprends pas pourquoi après 1 premier envoi le recaptcha me refuserait le 2nd envoi

Re: Valider plusieurs fois un formulaire sans actualiser la page

Posté : 19 avr. 2021, 13:45
par jeicko
Il semblerait que le problème vienne de recaptcha, si je le le mets à 0 je peux envoyer autant de message que je veux, à partir du moment où il est supérieur à 0 je peux envoyer une seule fois un commentaire.. Mais à 0 il ne sert plus à rien.

Quelqu’un a une idée ?

Re: Valider plusieurs fois un formulaire sans actualiser la page

Posté : 19 avr. 2021, 15:03
par @rthur
A priori il y a une fonction grecaptcha.reset(); qui permet de réinitaliser Recaptcha après l'avoir utilisé, peut être est-ce une piste (mais je ne connais pas recaptcha donc à toi de voir si ça vient de là)

Re: Valider plusieurs fois un formulaire sans actualiser la page

Posté : 19 avr. 2021, 15:18
par jeicko
Pas d'autres idées ? :/ j'ai aussi le recaptcha sur mon formulaire de contact et je n'ai pas ce problème, je n'ai pas eu besoin de rajouter des fonctions supplémentaires

Re: Valider plusieurs fois un formulaire sans actualiser la page

Posté : 20 avr. 2021, 11:25
par Saian
Salut, je dirais que le recaptcha ne peut fonctionner que pour une requête. Comment ça se passe sur la page quand l'utilisateur envoie plusieurs messages ? valide-t-il le recaptcha pour chaque envoi ?

Re: Valider plusieurs fois un formulaire sans actualiser la page

Posté : 20 avr. 2021, 15:16
par jeicko
Salut Saian,

Il n'y a rien a valider avec ce recaptcha, c'est la version v3.

"Le système va analyser les activités sur le site Web et fournir une note entre 0,0 et 1,0, qui va évaluer à quel point les intéractions entre le site et l’utilisateur sont suspectes. Si la note est proche de 1,0, les intéractions seront considérées comme normales alors que si elles sont proches de 0,0, l’API considérera que le trafic a probablement été généré par un bot."

Ca détecte les mouvements de souris ou les cliques par exemple.
En théorie et si j'ai bien compris le fonctionnement, le recaptcha est soumis avant chaque envoi. Sur mon formulaire de contact j'ai la version v3 aussi et je peux valider le formulaire autant de fois que je le souhaite.

Re: Valider plusieurs fois un formulaire sans actualiser la page

Posté : 20 avr. 2021, 15:25
par Saian
Pourtant si ça marche au premier envoi et qu'aux autres tu obtiens l'erreur en lien avec le recaptcha, c'est bien que le problème doit venir de la. Et je vois aucun code particulier au recaptcha si ce n'est l'input hidden.

https://developers.google.com/recaptcha/docs/v3
D'après la doc ici, pour que ce soit automatique il devrait y avoir des attributs data sur le bouton submit. Je crois pas les avoir vu dans ton code. Mais même avec ça j'ai un doute car la callback ne fait que soumettre le formulaire. Or je pense qu'il faut renouveler le token à chaque appel.

Re: Valider plusieurs fois un formulaire sans actualiser la page

Posté : 20 avr. 2021, 15:42
par jeicko
Ca devient compliqué pour mon niveau..

Ca voudrait dire que mon formulaire de contact n'est pas protégé non plus malgré le fait que je n'ai aucun bug ?

As-tu une solution à tout ça ? Tu viens de me déprimer ^^

Je pensais avoir enfin terminé mon site et j'ai l'impression que rien ne va..

Re: Valider plusieurs fois un formulaire sans actualiser la page

Posté : 20 avr. 2021, 15:51
par Saian
Sans voir les codes ou sans voir ce que fait la page je ne tirerais aucune conclusion. Déjà as tu vérifié que tu as bien quelque chose dans $_POST['recaptcha-response'] pour chaque appel ? et si oui est ce que c'est un token différent où est ce que c'est toujours le même ? Dans le cas où tu as une erreur as tu regarder en détail le retour du call curl pour voir si tu n'as pas une erreur détaillée de l'api recaptcha ? parce que la tu ne fais qu'un if ($data->score >= 0.5) alors que la réponse peut contenir des codes d'erreur, tu as peut être une erreur timeout-or-duplicate par exemple.

La pour moi en parcourant vite fait ton code, j'imagine que l'input hidden est définit automatiquement lors du chargement de la page et qu'ensuite il ne bouge plus. Et je suppose que le token est a usage unique, raison pour laquelle ça ne fonctionnerait qu'une fois. Peut être as tu un code quelque part que tu n'as pas dévoilé ou que je n'ai pas vu où l'input hidden est renseigné suite à un call javascript avec l'api recaptcha ?

PS : sinon inspire toi de l'exemple "Programmatically invoke the challenge" de la doc.

Je te remets les liens, à priori y a tout ce qu'il faut dedans.
https://developers.google.com/recaptcha/docs/v3
https://developers.google.com/recaptcha/docs/verify

Re: Valider plusieurs fois un formulaire sans actualiser la page

Posté : 20 avr. 2021, 16:10
par jeicko
Je vais regarder tout ce que tu me dis en detail, mais je me sens un peu dépassé là.

Je te mets l'url de mon site ou je fais les tests si jamais tu veux voir comment ça fonctionne : lien supprimé

N'hesite pas à utiliser les formulaires ça ne pose pas de problème

Re: Valider plusieurs fois un formulaire sans actualiser la page

Posté : 20 avr. 2021, 16:23
par Saian
Je viens de regarder et donc c'est bien le même token lors des deux appels. Je pense que tu dois avoir une erreur lors du deuxième verify parce que c'est le même token.

Le code que tu as mis dans le grecaptcha.ready() tu devrais le basculer dans le submit du formulaire et tu mets la soumission ajax dans le then().

Sinon la page est très propre.

PS : pour le formulaire de contact tu auras surement le même problème si l'utilisateur veut envoyer plusieurs messages et qu'il ne rafraichit pas la page entre temps.

PPS : autre solution, au lieu de déplacer le code comme je le suggère plus haut, lors de la soumission du formulaire tu renouvelles le token après le call ajax (tu refais le même appel que dans le grecaptcha.ready()). L'idéal serait de verrouiller le bouton d'envoi tant que le token n'est pas renouvelé.

PPPS : tu pourrais également re-remplir les champs du formulaire en cas d'erreur pour que l'utilisateur n'ait pas à les re-saisir. De même tu pourrais remettre le pseudo et le mail saisis lors du premier envoi.

Re: Valider plusieurs fois un formulaire sans actualiser la page

Posté : 20 avr. 2021, 17:16
par jeicko
PPPS : tu pourrais également re-remplir les champs du formulaire en cas d'erreur pour que l'utilisateur n'ait pas à les re-saisir. De même tu pourrais remettre le pseudo et le mail saisis lors du premier envoi.
Alors j'aimerais pouvoir ajouter toutes ces fonctionnalités mais honnêtement je n'ai pas les bases et je ne sais pas le faire..
PPS : autre solution, au lieu de déplacer le code comme je le suggère plus haut, lors de la soumission du formulaire tu renouvelles le token après le call ajax (tu refais le même appel que dans le grecaptcha.ready()). L'idéal serait de verrouiller le bouton d'envoi tant que le token n'est pas renouvelé.
Est-ce que tu pourrais me dire si ça doit ressembler à ça ? Je ne suis pas sûr de moi mais en tout cas l'envoi de plusieurs messages d'affilé fonctionne :

Code : Tout sélectionner

//Connexion Recaptcha grecaptcha.ready(function () { grecaptcha .execute("***", { action: "homepage", }) .then(function (token) { document.getElementById("recaptchaResponse-com").value = token; }); }); $(document).ready(function () { //quand le DOM est là, on sélectionne les éléments avec lesquels on va travailler //c'est mieux de le faire une fois au début, pour des questions de perf //mais surtout pour éviter d'avoir à faire un gros ctrl + r pour changer les sélecteurs. const $comment_form = $("#comment_form"); const $comment_message = $("#comment_message"); const $display_comment = $("#display_comment"); $comment_form.on("submit", function (event) { event.preventDefault(); const form_data = $comment_form.serialize(); $.ajax({ url: "add_comment.php", method: "POST", data: form_data, dataType: "JSON", success: function (data) { if (data.error !== "") { $comment_form[0].reset(); $comment_message.html(data.error); //un formulaire (élément) contient tous ses inputs //c'est rangé par nom $comment_form[0].comment_id.value = "0"; load_comment(); //on renouvelle le token pour envoyer un 2nd commentaire grecaptcha .execute("***", { action: "homepage", }) .then(function (token) { document.getElementById("recaptchaResponse-com").value = token; }); } }, }); }); reload(); ////////////////////////////////// function load_comment() { $.ajax({ url: "fetch_comment.php", //POST pour envoyer des données, et GET pour obtenir des données method: "POST", success: function (data) { $display_comment.html(data); $(".reply").on("click", function () { $comment_form[0].comment_id.value = $(this).attr("id"); $comment_form[0].comment_name.focus(); }); }, }); } function reload() { load_comment(); setTimeout(reload, 10000); } });
PS : pour le formulaire de contact tu auras surement le même problème si l'utilisateur veut envoyer plusieurs messages et qu'il ne rafraichit pas la page entre temps.
Je dois donc appliquer le même principe que le precedent code (dans l'éventualité ou le code est juste)

Re: Valider plusieurs fois un formulaire sans actualiser la page

Posté : 20 avr. 2021, 17:35
par Saian
Je l'aurais plutôt mis après le $.ajax du add_comment que dans la callback success mais sinon c'est ça oui. En le mettant après le $.ajax, le recaptcha est renouvelé parallèlement à la soumission du message si je puis dire, alors qu'en le mettant dans la callback success, il n'est renouvelé que s'il y a un retour en succès à la soumission du formulaire. Et oui dans l'idée, pareil pour le formulaire de contact.