Le tuto présente une alternative au captcha lors de l'utilisation d'un formulaire de contact (applicable également pour un livre d'or).
Suite à quelques messages sur le forum concernant des problèmes liés aux captchas, je propose une petite alternative (déjà existante) à ceux-ci. L'alternative que nous allons voir est dans le cadre d'un formulaire de contact (applicable au livre d'or également). Il s'agit de la confirmation par email avant l'envoi définitif du message dans notre boîte mail ; un peu comme pour confirmer un mot de passe.
Alors, pour changer nous allons faire de l'objet avec du MVC, parce qu'il y en a un peu marre de voir/faire QUE du procédural avec des codes mélangés partout dans les applications comme ceux qui traînent sur le forum notamment. Attention ! Je n'explique pas l'objet, ce n'est pas le but ici. D'ailleurs je n'explique pas grand-chose, partant du principe que le tout a été très simplifié pour un résultat optimum. La compréhension n'est donc pas difficile.
Niveau requis : Novice et initié
Il faut avoir une base d'objet et de MVC embarquée dans le cerveau.
L'internaute doit « sortir » de la page, et effectuer des opérations supplémentaires (ouvrir sa boîte mail, confirmer l'envoi)
On supposera notre site web sous l'intitulé `MonSite.com'
On bossera dans 1,2,3...6 dossiers, qui sont :
Ce dossier contiendra la classe phpmailer. Cette classe permet d'envoyer des emails en toute simplicité. On se servira donc d'une solution existante et performante. Je ne m'attarderais pas sur la façon de l'utiliser ! De toute façon vous le verrez (de visu) par vous-même au cours de ce tuto.
On va créer un fichier, qu'on nommera : config.inc.php
Ce fichier est la pièce maîtresse. C'est dedans qu'on initialisera notre site.
/* Initialisation du site */ define('NAME_SITE', 'Mon Site'); define('URL_SITE', 'http://www.monsite.com'); define('MAIL_POSTMASTER', 'postmaster@monsite.com'); define('MAIL_CONTACT', 'contact@monsite.com'); /* Connexion MYSQL */ define('DB_SERVEUR', ' '); // Nom du serveur define('DB_BASE', ' '); // Nom de la base define('DB_USER', ' '); // Nom de l'utilisateur de la base define('DB_PASSWD', ' '); // Mot de passe pour accéder à la base define('DB_DSN', 'mysql:host='.DB_SERVEUR.';dbname='.DB_BASE); // Driver PDO pour l'accès à la bdd /* Définitions des dossiers */ define('CONTROL_DIR', dirname(__FILE__).'/../controllers/'); define('CLASS_DIR', dirname(__FILE__).'/../classes/'); define('THEME_DIR', dirname(__FILE__).'/../themes/'); define('MAILS_DIR', dirname(__FILE__).'/../mails/'); define('PHPMAILER_DIR', dirname(__FILE__).'/../tools/phpmailer/'); /* Autoload */ function __autoload($class) { if (!class_exists($class, false)) require_once(CLASS_DIR.$class.'.php'); }
La fonction __autoload() s'occupera de charger nos classes automatiquement.
Le reste c'est du classique.
Dans ce dossier nous allons y placer tous les `gabarits' pour nos emails.
Pour les besoins de notre tuto il va en falloir 2 :
Le Lorem Ipsum est simplement du faux texte employé dans la composition et la mise en page avant impression. Le Lorem Ipsum est le faux texte standard de l'imprimerie depuis les années 1500, quand un peintre anonyme assembla ensemble des morceaux de texte pour réaliser un livre spécimen de polices de texte. Copier ce lien dans votre navigateur pour confirmer : L'équipe {nom_site} |
Ce fichier est celui qui envoie un lien de confirmation, pour vérifier que c'est bien un humain qui a pris contact avec nous.
/*********************************************************/ Vous avez reçu un message depuis {nom_site} /*********************************************************/ {nom} {prenom} a écrit : {message} /*********************************************************/ |
Ce fichier contiendra le message que l'internaute a voulu nous envoyer. En gros c'est l'email envoyé par l'internaute.
Il s'agit tout simplement du formulaire de contact.
<span style="color:#FF0000;"> {errors} {statement_contact} </span><br/> <form action="contact.php" method="post"> Nom : <input type="text" name="nom" value="{form_nom}"/> <br/><br/> Prénom : <input type="text" name="prenom" value="{form_prenom}"/> <br/><br/> Votre adresse mail : <input type="text" name="expediteur" value="{form_expediteur}"/> <br/><br/> Sujet du message : <select name="sujet"> <option value="Contact" {form_sujet_option_contact}>Contact</option> <option value="Partenariat" {form_sujet_option_partenariat}>Partenariat</option> <option value="Autre" {form_sujet_option_autre}>Autre</option> </select><br/><br/> Message : <textarea name="message" rows="15" cols="30"> {form_message} </textarea> <br/><br/> <input type="submit" value="Envoyer"/> </form>
Le nom de nos classes porte le nom de nos fichiers. Donc quand je ferais référence à la classe `Kapout', le nom du fichier est également Kapout ; ceci fait partie intégrante du chargement automatique des classes par __autoload().
Elle contient les méthodes qui permettent d'effectuer la connexion avec la base de données ainsi que l'exécution des requêtes.
abstract class Database { private static $_instance = null; static public function instance() { if(is_null(self::$_instance)) { try { self::$_instance = new PDO(DB_DSN, DB_USER, DB_PASSWD); self::$_instance->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } catch(Exception $e) { die($e->getMessage()); } } return self::$_instance; } static public function query($query, $args=array(), $mode=FALSE) { $r = self::instance()->prepare($query); if(sizeof($args)) $r->execute($args); else $r->execute(); return (!$mode)? $r->fetch(PDO::FETCH_ASSOC) : $r->fetchAll(PDO::FETCH_ASSOC); } static public function exec($query, $args=array()) { $r = self::instance()->prepare($query); if(sizeof($args)) return $r->execute($args); else return $r->execute(); }
Gère l'envoie des emails à l'aide de la classe phpmailer, et les gabarits d'emails.
class Mail { function sendMessage($to, $from, $fromName, $subject, $mailName, $mailVars, $toName=NULL) { require(PHPMAILER_DIR.'class.phpmailer.php'); if(empty($to) OR empty($from) OR empty($fromName) OR empty($subject) OR empty($mailName)) die('Erreur: les paramètres mail sont corrompus'); $mail = new PHPMailer; $mail->SetLangage('fr'); $mail->IsMail(); $mail->From = $from; $mail->FromName = $formName; if($toName AND is_array($toName)) die('Erreur: les paramètres mail sont corrompus'); $mail->AddAddress($to, $toName); $mail->Subject = $subject; if(!file_exists(MAIL_DIR.$mailName.'.txt')) die('Erreur - Le template e-mail suivant est manquant : '.MAILS_DIR.$mailName.'.txt'); else $bodyMail = strip_tags(html_entity_decode(file_get_contents(MAILS_DIR.$mailName.'.txt'), NULL, 'utf-8')); $mail->IsHTML(FALSE); foreach($mailVars as $key=> $value) $bodyMail = preg_replace('/\{'.$keys.'\}/', $value, $bodyMail); $mail->Body = $bodyMail; return ($mail->Send()) ? TRUE : FALSE; } }
Prend 5 arguments dont 1 facultatif :
Ce n'est pas du lourd comme moteur de template ! Mais il fera ce qu'on lui dit pour les besoins du tuto.
P.S : il ne gère pas les boucles...j'ai prévenu !!
class Template { public function setBundle($file, $vars=array()) { $contentTemplate = ' '; if(sizeof($file)) { foreach($file as $value) $contentTemplate .= Template::getContentFile(THEME_DIR.$value); } else $contentTemplate .= Template::getContentFile($file); if(sizeof($vars)) { foreach($vars as $keys=> $value) $contentTemplate = preg_replace('/\{'.$key.'\}/', $value, $contentTemplate); } return $contentTemplate; } static public function getContentFile($file) { if(!file_exists($file)) die('Erreur - Le template suivant est manquant : '.$file); return file_get_contents($file); } }
- $vars = valeurs à remplacer dans le template {X}
Enregistre dans la base de données le message que l'internaute tente de nous envoyer :
static public function saveMessage($nom, $prenom, $expediteur, $sujet, $message) { Contact::cleannerMessage() $skey = Contact::returnKey($message.mktime()); $mailVars = array('prenom' => $prenom, 'url_active_mail'=> URL_SITE.'/contact.php?idkey='.$skey, 'nom_site') => NAME_SITE); if(Mail::sendMessage($expediteur, MAIL_POSTMASTER, NAME_SITE, $sujet, 'valid_contact', $mailVars, $nom.' '.$prenom)) { $insert = Database::exec('INSERT INTO contact (skey, nom, prenom, expediteur, sujet, message, date_contact) VALUES (?, ?, ?, ?, ?, ?, CURRENT_TIMESTAND())', array($key, $nom, $prenom, $expediteur, $sujet, trim($message))); if(Database::instance()->lastInsertId()) return true; } return false; }
Confirme qu'il s'agit bien d'un humain à l'aide de l'id de confirmation contenu dans le lien de confirmation fournit en url :
static public function validMessage($skey) { $row = Database::query('SELECT skey, nom, prenom, expediteur, sujet, message FROM contact WHERE skey=?', array($skey)); if($row) { $mailVars = array( 'prenom' => $row['prenom'], 'nom' => $row['nom'], 'message' => htmlspecialchars($row['message']."\n", ENT_NOQUOTES, 'UTF-8'), 'nom_site' => NAME_SITE); if(Mail::sendMessage(MAIL_CONTACT, $row['expediteur'], NAME_SITE, $row['sujet'], 'send_contact', $mailVars, $row['nom'].' '.$row['prenom'])) { Contact::cleannerMessage($skey); return true; } } return false }
static public function cleannerMessage($key=FALSE) { if(!$key) { return Database::exec('DELETE FROM contact WHERE DATEDIFF(CURRENT_TIMESTAMP(), date_contact)>2'); } else { return Database::exec('DELETE FROM contact WHERE skey=?', array($key)); } } static public function returnkey($str) { return sha1($str); }
Ce fichier effectue les opérations de contrôle, pour la validation et l'envoi du formulaire ainsi que la confirmation du message par l'id.
Ce fichier utilise une classe de gestion pour formulaire nommée « Form », que j'ai développé auparavant et qui est disponible sur 'phpcs'.
try { $form = new Form('post'); $getUrl = new Form('get'); // On vérifie la validité du champs "nom" $form->issetValue('nom', 'Vous devez renseigner votre nom'); $form->isExtendType('nom', '/[^a-zÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÝ]/i', 'Votre nom doit comporter uniquement des lettres'); // On vérifie la validité du champs "prenom" $form->issetValue('prenom', 'Vous devez renseigner votre prénom'); $form->isExtendType('prenom', '/[^a-zÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÝ]/i', 'Votre prénom doit comporter uniquement des lettres'); // On vérifie la validité du champs "expediteur" $form->issetValue('expediteur', 'Vous devez renseigner votre adresse email'); $form->isEmail('expediteur', 'saisissez une adresse email valide'); // On vérifie la validité du champs "sujet" $form->issetValue('sujet', 'Veuillez indiquer pour quel service le message est adressé'); $form->compareValue('sujet', $form->sujet, array('Contact','Partenariat','Autre'), ' ', 'Veuillez indiquer un sujet parmi ceux fournis'); // On vérifie la validité du champs "message" $form->compareValue('message', strlen(trim($form->message)), 1, '<', 'Veuillez saisir votre message'); } catch(Exception $e) { die($e->getMessage()); } $varTemplate['statement_contact'] = ' '; // Si le formulaire est OK if($form->sendForm()) { if(Contact::saveMessage($form->nom, $form->prenom, $form->expediteur, $form->sujet, $form->message)) { $varTemplate['statement_contact'] = 'Un email vient de vous être envoyé pour confirmer l\'envoi de votre message'; unset($form->nom, $form->prenom, $form->expediteur, $form->sujet, $form->message); } else $varTemplate['statement_contact'] = 'Veuillez tenter à nouveau'; } // On récupère les erreurs du formulaire $varTemplate['errors'] = $form->getError(); // On récupère les infos du formulaire $varTemplate['form_nom'] = $form->nom; $varTemplate['form_prenom'] = $form->prenom; $varTemplate['form_expediteur'] = $form->expediteur; $varTemplate['form_sujet_option_contact'] = ($form->sujet=='Contact') ? 'selected="selected"' : ' '; $varTemplate['form_sujet_option_partenariat'] = ($form->sujet=='Partenariat') ? 'selected="selected"' : ' '; $varTemplate['form_sujet_option_autre'] = ($form->sujet=='Autre') ? 'selected="selected"' : ' '; $varTemplate['form_message'] = $form->message; // Procédure pour la confirmation de l'envoi du message if($getUrl->idkey) { if(Contact::validMessage($getUrl->idkey)) $varTemplate['statement_contact'] = 'Votre message nous a bien été communiqué'; else $varTemplate['statement_contact'] = 'Il est impossible de confirmer votre message'; }
Et pour finir...
include(dirname(__FILE__).'/config/config.inc.php'); $varTemplate = array(); include(CONTROL_DIR.'contact.php'); $template = new Template(); $loadTemplate = array('header.tpl','contact.tpl','footer.tpl'); echo $template->setBundle($loadTemplate, $varTemplate);
C'est le fichier qui sera appelé par l'internaute : http://www.MonSite.com/contact.php.
C'est lui qui imbriquera les fichiers pour la page contact.php
Quand on regarde le tout on se dit c'est beaucoup de lignes pour une seule petite chose : OUI !
On aurait pu faire le tout avec moins de lignes de codes, moins de dossiers, moins de fichiers. Mais quand on fait de l'objet (du bon !) avec du MVC ce n'est pas possible de faire petit. Mais on y gagne en lisibilité !! L'application est propre et on sait qui fait quoi.
Je tiens à préciser que chaque ligne de code a été spécifiquement créée pour ce tuto, donc certaines choses ne sont pas forcément réutilisables pour d'autre cas de figure. Alors ce n'est même pas la peine de me dire « ben j'ai fait ci, j'ai fait ça et ça marche pas quand je veux... » Je ne répondrai pas !
Lien pour la télécharger la classe PhpMailer : http://phpmailer.worxware.com/
Lien pour la télécharger ma classe Form :
http://www.phpcs.com/codes/CLASSES-VALIDATION-FORMULAIRE_52367.aspx