32

Structuring a form

Create site8 by copying site7.

  1. /cms
    1. ...
    2. site7
    3. site8

In this chapter, we are going to add a contact page with a form for sending an email to the webmaster of the site.

To test the result online, enter http://www.frasq.org/cms/site8 in the address bar of your navigator. Fill in the form and send it. Try sending the form with empty fields or an invalid email address.

Create an action called contact by adding the file contact.php in the folder actions with the following content:

  1. /cms/site8
    1. actions
      1. contact.php
  1. function contact($lang) {
  2.     head('title', translate('contact:title', $lang));
  3.     head('description', false);
  4.     head('keywords', false);
  5.     head('robots', 'noindex, nofollow');
  6.  
  7.     $banner = build('banner', $lang);
  8.  
  9.     $mailme = build('mailme', $lang);
  10.  
  11.     $content = view('contact', $lang, compact('mailme'));
  12.  
  13.     $output = layout('standard', compact('banner', 'content'));
  14.  
  15.     return $output;
  16. }

Notice that the page has no description and no keywords. The tag robots tells search engines to ignore this page.

To give access to the form, add an alias for every language in the file includes/aliases.inc:

  1. $aliases = array(
  2.     'fr' => array(
  3.         'accueil'               => 'home',
  4.         'contact'               => 'contact',
  5.     ),
  6.     'en' => array(
  7.         'home'                  => 'home',
  8.         'contact'               => 'contact',
  9.     ),
  10. );

Add the title of the page in the file includes/strings.inc.

In English in the array 'en':

  1.         'contact:title'         => 'Contact us',

In French in the array 'fr':

  1.         'contact:title'         => 'Nous contacter',

Add the views of the contact page in the folders views/en for the English version and views/fr for the version in French.

  1. /cms/site8
    1. views
      1. en
        1. contact.phtml
      2. fr
        1. contact.phtml
  1. <h3>Contact</h3>
  2. <?php echo $mailme; ?>
  1. <h3>Contact</h3>
  2. <?php echo $mailme; ?>

The view is the same in English and French and you could save it directly under views but if the title line changes or if another language is added later, the views will be different anyway. So, stick to the general plan.

The form for sending an email is in a bloc. Begin by writing the view, first in one language:

  1. /cms/site8
    1. views
      1. en
        1. contact.phtml
        2. mailme.phtml
  1. <div class="form" id="mailme_form">
  2. <form action="" method="post">
  3. <div class="fields">
  4. <p class="label">What is your email address?</p>
  5. <p class="input"><input type="text" name="mailme_mail" id="mailme_mail" size="50" maxlength="100" title="Email" value="<?php echo htmlspecialchars($mail, ENT_COMPAT, 'UTF-8'); ?>" /></p>
  6. <p class="info">Your email address is strickly confidential.</p>
  7. <p class="label">Type in the subject and the text of your message:</p>
  8. <p class="input"><input class="monospace" type="text" name="mailme_subject" id="mailme_subject" size="60" maxlength="100" title="Subject" value="<?php echo htmlspecialchars($subject, ENT_COMPAT, 'UTF-8'); ?>" /></p>
  9. <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>
  10. <p class="submit"><button type="submit" name="mailme_send" id="mailme_send">Send</button></p>
  11. </div>
  12. </form>

The form has 3 input fields: sender's email address, subject of the message and text of the message. 3 variables are necessary: $mail, $subject and $message. All the field names are prefixed with mail_. The form has only one button called mailme_send.

The rest of the view manages all the error and information messages. The possible errors are $missing_mail, $bad_mail, $missing_message, $missing_subject, $bad_subject and $internal_error. If $email_sent is true, the form signals the user that the message has been sent.

  1. <?php if ($errors):
  2. extract($errors);
  3. ?>
  4. <div class="errors">
  5. <?php if ($missing_mail): ?>
  6. <p>You haven't given your email address.</p>
  7. <?php elseif ($bad_mail): ?>
  8. <p>The email address is invalid.</p>
  9. <?php endif; ?>
  10. <?php if ($missing_message): ?>
  11. <p>The message is empty.</p>
  12. <?php endif; ?>
  13. <?php if ($missing_subject): ?>
  14. <p>Don't forget the subject.</p>
  15. <?php elseif ($bad_subject): ?>
  16. <p>The subject of the message is invalid.</p>
  17. <?php endif; ?>
  18. <?php if ($internal_error): ?>
  19. <p>An internal error has occurred.</p>
  20. <?php endif; ?>
  21. </div>
  22. <?php endif; ?>
  23. <?php if ($infos):
  24. extract($infos);
  25. ?>
  26. <div class="infos">
  27. <?php if ($email_sent): ?>
  28. <p>Your message has been sent. To return to the home page, <a href="<?php echo $home_page; ?>">click here</a>.</p>
  29. <?php endif; ?>
  30. </div>
  31. <?php endif; ?>
  32. </div>

A little retouching of the style sheet:

  1. img {vertical-align:middle;}
  2.  
  3. .monospace {font-family:monospace;}
  1. .info {color:#666666;font-size:smaller;font-style:normal;}
  2. .error {color:#df6f6f;font-size:smaller;font-style:italic;}
  3.  
  4. .input .monospace {font-size:medium;}
  5. .errors {color:#df6f6f;font-size:small;font-style:normal;}
  6. .infos {font-style:normal;}

To validate the view, write a first version of the mailme function which is limited to displaying the form:

  1. /cms/site8
    1. blocks
      1. mailme.php
  1. function mailme($lang) {
  2.     $mail=$subject=$message=false;
  3.  
  4.     $missing_mail=false;
  5.     $bad_mail=false;
  6.  
  7.     $missing_subject=false;
  8.     $bad_subject=false;
  9.  
  10.     $missing_message=false;
  11.  
  12.     $email_sent=false;
  13.     $home_page=url('home', $lang);
  14.  
  15.     $internal_error=false;
  16.  
  17.     $errors = compact('missing_mail', 'bad_mail', 'missing_subject', 'bad_subject', 'missing_message', 'internal_error');
  18.     $infos = compact('email_sent', 'home_page');
  19.  
  20.     $output = view('mailme', $lang, compact('mail', 'subject', 'message', 'errors', 'infos'));
  21.  
  22.     return $output;
  23. }

Enter http://localhost/cms/site8/en/contact in the address bar of your navigator. Edit the mailme function. Give values to the input fields variables. Set all the error and info variables to true. Check the link to the home page.

Add the view in French:

  1. /cms/site8
    1. views
      1. fr
        1. contact.phtml
        2. mailme.phtml
  1. <div class="form" id="mailme_form">
  2. <form action="" method="post">
  3. <div class="fields">
  4. <p class="label">Quelle est votre adresse d'email ?</p>
  5. <p class="input"><input type="text" name="mailme_mail" id="mailme_mail" size="50" maxlength="100" title="Email" value="<?php echo htmlspecialchars($mail, ENT_COMPAT, 'UTF-8'); ?>" /></p>
  6. <p class="info">Votre adresse d'email est strictement confidentielle.</p>
  7. <p class="label">Tapez le sujet et le texte de votre message&nbsp;:</p>
  8. <p class="input"><input class="monospace" type="text" name="mailme_subject" id="mailme_subject" size="60" maxlength="100" title="Sujet" value="<?php echo htmlspecialchars($subject, ENT_COMPAT, 'UTF-8'); ?>" /></p>
  9. <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>
  10. <p class="submit"><button type="submit" name="mailme_send" id="mailme_send">Envoyer</button></p>
  11. </div>
  12. </form>
  13. <?php if ($errors):
  14. extract($errors);
  15. ?>
  16. <div class="errors">
  17. <?php if ($missing_mail): ?>
  18. <p>Vous n'avez pas donné votre adresse d'email.</p>
  19. <?php elseif ($bad_mail): ?>
  20. <p>L'adresse d'email est invalide.</p>
  21. <?php endif; ?>
  22. <?php if ($missing_message): ?>
  23. <p>Le message est vide.</p>
  24. <?php endif; ?>
  25. <?php if ($missing_subject): ?>
  26. <p>N'oubliez pas le sujet.</p>
  27. <?php elseif ($bad_subject): ?>
  28. <p>Le sujet du message est invalide.</p>
  29. <?php endif; ?>
  30. <?php if ($internal_error): ?>
  31. <p>Une erreur interne s'est produite.</p>
  32. <?php endif; ?>
  33. </div>
  34. <?php endif; ?>
  35. <?php if ($infos):
  36. extract($infos);
  37. ?>
  38. <div class="infos">
  39. <?php if ($email_sent): ?>
  40. <p>Votre message a bien été envoyé. Pour revenir à la page d'accueil, <a href="<?php echo $home_page; ?>">cliquez ici</a>.</p>
  41. <?php endif; ?>
  42. </div>
  43. <?php endif; ?>
  44. </div>

Enter http://localhost/cms/site8/fr/contact in the address bar of your navigator to validate the French version.

Once the views are tuned, you can proceed to the analysis of the user's input. Structuring the code properly is very important. The development is always the same:

  1. Determine the requested action and initialize the variables of the form.
  2. Read the input fields and assign the variables of the form.
  3. Initialize the error and the information variables then filter and validate the data.
  4. Execute the requested action if no error is detected.
  5. Generate the view of the form with the calculated values.

Complete mailme with the following code:

  1. require_once 'readarg.php';
  2. require_once 'validatemail.php';
  3. require_once 'ismailinjected.php';

Loads the code for the functions readarg, validate_mail and is_mail_injected. Each function is defined in a separate file in the folder library:

  1. /cms/site8
    1. library
      1. readarg.php
      2. validatemail.php
      3. ismailinjected.php
  1. function readarg($s, $trim=true) {
  2.     if ($trim) {
  3.         $s = trim($s);
  4.     }
  5.     return get_magic_quotes_gpc() ? stripslashes($s) : $s;
  6. }

readarg removes all the \ (BACKSLASH) from $s automatically added by PHP to the content of the variables $_GET and $_POST if the configuration parameter magic_quotes_gpc is true. If $trim is $true, which the case by default, readarg removes spaces at the beginning and at the end of $s. Systematically call this function to filter all the fields returned by a form.

  1. function validate_mail($email) {
  2.     return preg_match('/^[a-z0-9._%-]+@[a-z0-9.-]+\.[a-z]{2,4}$/', $email);
  3. }

validate_mail returns true if $email is a valid email address, false otherwise.

  1. function is_mail_injected($s) {
  2.     $injections = array(
  3.     '(\n+)',
  4.     '(\r+)',
  5.     '(\t+)',
  6.     '(%0A+)',
  7.     '(%0D+)',
  8.     '(%08+)',
  9.     '(%09+)'
  10.     );
  11.     $reg = implode('|', $injections);
  12.     $reg = "/$reg/";
  13.  
  14.     return preg_match($reg, $s);
  15. }

is_mail_injected returns true if $s contains characters likely to permit an injection in the content of an email, false if $s is clean.

  1. function mailme($lang) {
  2.     $action='init';
  3.     if (isset($_POST['mailme_send'])) {
  4.         $action='send';
  5.     }
  6.  
  7.     $mail=$subject=$message=false;

mailme starts by initializing $action to 'send' if the user has pressed the Send button or to 'init' by default, when the form is directly displayed.

  1.     switch($action) {
  2.         case 'send':
  3.             if (isset($_POST['mailme_mail'])) {
  4.                 $mail=strip_tags(readarg($_POST['mailme_mail'], true));
  5.             }
  6.             if (isset($_POST['mailme_subject'])) {
  7.                 $subject=strip_tags(readarg($_POST['mailme_subject'], true));
  8.             }
  9.             if (isset($_POST['mailme_message'])) {
  10.                 $message=strip_tags(readarg($_POST['mailme_message'], true));
  11.             }
  12.             break;
  13.         default:
  14.             break;
  15.     }

Reads the input fields and filters them with readarg then the PHP function strip_tags which removes all the tags which are potentially dangerous.

  1.     $missing_mail=false;
  2.     $bad_mail=false;
  3.  
  4.     $missing_subject=false;
  5.     $bad_subject=false;
  6.  
  7.     $missing_message=false;
  8.  
  9.     $email_sent=false;
  10.     $home_page=url('home', $lang);
  11.  
  12.     $internal_error=false;
  13.  
  14.     switch($action) {
  15.         case 'send':
  16.             if (!$mail) {
  17.                 $missing_mail=true;
  18.             }
  19.             else if (!validate_mail($mail)) {
  20.                 $bad_mail=true;
  21.             }
  22.             if (!$subject) {
  23.                 $missing_subject=true;
  24.             }
  25.             else if (is_mail_injected($subject)) {
  26.                 $bad_subject=true;
  27.             }
  28.             if (!$message) {
  29.                 $missing_message=true;
  30.             }
  31.  
  32.             break;
  33.         default:
  34.             break;
  35.     }

Initializes all the error and information variables before validating the data sent by the form.

  1.     switch($action) {
  2.         case 'send':
  3.             if ($missing_mail or $bad_mail or $missing_subject or $bad_subject or $missing_message) {
  4.                 break;
  5.             }
  6.  
  7.             require_once 'emailme.php';
  8.  
  9.             $r = emailme($subject, $message, $mail);
  10.  
  11.             if (!$r) {
  12.                 $internal_error=true;
  13.                 break;
  14.             }
  15.  
  16.             $subject=$message=false;
  17.             $email_sent=true;
  18.  
  19.             break;
  20.         default:
  21.             break;
  22.     }

Checks that no error has been detected and sends an email to the webmaster with the emailme function.

  1.     $errors = compact('missing_mail', 'bad_mail', 'missing_subject', 'bad_subject', 'missing_message', 'internal_error');
  2.     $infos = compact('email_sent', 'home_page');
  3.  
  4.     $output = view('mailme', $lang, compact('mail', 'subject', 'message', 'errors', 'infos'));
  5.  
  6.     return $output;
  7. }

Prepares all the parameters for the view, generates it and returns its content.

Define the function emailme in the file library/emailme.php with the following content:

  1. function emailme($subject, $msg, $sender=false) {
  2.     global $webmaster, $mailer;
  3.  
  4.     if (empty($sender)) {
  5.         $sender = $webmaster;
  6.     }
  7.  
  8.     $headers = <<<_SEP_
  9. From: $sender
  10. Return-Path: $sender
  11. Content-Type: text/plain; charset=utf-8
  12. X-Mailer: $mailer
  13. _SEP_;
  14.  
  15.     return @mail($webmaster, $subject, $msg, $headers);
  16. }

The global variables $webmaster and $mailer are defined in the configuration file includes/config.inc:

  1. global $sitename, $webmaster, $mailer;
  2.  
  3. $sitename = 'frasq.org';

Replace the address nobody@frasq.org by webmaster@localhost. You can also change the name of the mailer.

Edit the file /etc/aliases and redirect the local mail sent to webmaster to your personal account:

webmaster:	frasq

NOTE: Replace frasq by your connection name. Restart the service:

$ sudo newaliases

Read the article The web developer tools to learn how to setup a local mail service.

Enter http://localhost/cms/site8/en/contact in the address bar of your navigator. Fill in the form and send it. Check if you have received an email at webmaster@localhost. Edit the source code of the email. Check the fields of the MIME header. Try sending the form with empty fields, entering an invalid email address, etc.

The last step consists in adding a menu with a Contact button to the banner of the home page and a link to the footer.

  1. /cms/site8
    1. views
      1. en
        1. footer.phtml
      2. fr
        1. footer.phtml
  1. <div id="footer">
  2. <p>&copy;2010-2011 frasq.org - All rights reserved - <a href="http://www.frasq.org">www.frasq.org</a></p>
  3. <p><a href="<?php echo $contact_page; ?>">Contact</a>&nbsp;|&nbsp;<img src="<?php echo $base_path; ?>/images/ubuntu.png" alt="" width="16" height="16"/>&nbsp;<a href="http://www.ubuntu.com" target="_blank">Ubuntu</a></p>
  4. </div>
  1. <div id="footer">
  2. <p>&copy;2010-2011 frasq.org - Tous droits réservés - <a href="http://www.frasq.org">www.frasq.org</a></p>
  3. <p><a href="<?php echo $contact_page; ?>">Contact</a>&nbsp;|&nbsp;<img src="<?php echo $base_path; ?>/images/ubuntu.png" alt="" width="16" height="16" />&nbsp;<a href="http://www.ubuntu.com" target="_blank">Ubuntu</a></p>
  4. </div>

The variable $contact_page is defined by the home action:

  1. function home($lang) {
  2.     head('title', translate('home:title', $lang));
  3.    
  4.     $languages='home';
  5.     $contact=true;
  6.     $banner = build('banner', $lang, compact('languages', 'contact'));
  7.  
  8.     $contact_page=url('contact', $lang);
  9.     $footer = view('footer', $lang, compact('contact_page'));
  10.  
  11.     $content = view('home', $lang);
  12.  
  13.     $output = layout('standard', compact('banner', 'footer', 'content'));
  14.  
  15.     return $output;
  16. }

home passes the URL of the contact page to the view footer and adds contact to the component list of the banner.

  1. function banner($lang, $components=false) {
  2.     $home_page=url('home', $lang);
  3.     $logo = view('logo', $lang, compact('home_page'));
  4.  
  5.     $menu=$contact=false;
  6.     $languages=false;
  7.     $contact_page=false;
  8.    
  9.     if ($components) {
  10.         foreach ($components as $v => $param) {
  11.             switch ($v) {
  12.                 case 'languages':
  13.                     if ($param) {
  14.                         $languages = build('languages', $lang, $param);
  15.                     }
  16.                     break;
  17.                 case 'contact':
  18.                     $contact_page=url('contact', $lang);
  19.                     $contact = true;
  20.                     break;
  21.                 default:
  22.                     break;
  23.             }
  24.         }
  25.     }
  26.  
  27.     if ($contact) {
  28.         $menu = view('bannermenu', $lang, compact('contact', 'contact_page'));
  29.     }
  30.  
  31.     $output = view('banner', false, compact('logo', 'menu', 'languages'));
  32.  
  33.     return $output;
  34. }

banner passes the URL of the contact page to view bannermenu which generates the menu.

  1. /cms/site8
    1. views
      1. en
        1. bannermenu.phtml
      2. fr
        1. bannermenu.phtml
  1. <div id="bannermenu">
  2. <ul class="menu menubar">
  3. <?php if (isset($contact) and $contact): ?>
  4. <li><a id="mail" href="<?php echo $contact_page; ?>" title="Contact us"><span>Contact</span></a></li>
  5. <?php endif; ?>
  6. </ul>
  7. </div>
  1. <div id="bannermenu">
  2. <ul class="menu menubar">
  3. <?php if (isset($contact) and $contact): ?>
  4. <li><a id="mail" href="<?php echo $contact_page; ?>" title="Nous contacter"><span>Contact</span></a></li>
  5. <?php endif; ?>
  6. </ul>
  7. </div>

Modify the style sheet to display a button for the Contact link and recenter the menus:

  1. #bannermenu {width:160px;float:left;margin-top:13px;margin-left:40px;}
  2. #bannermenu #mail {width:24px;height:24px;float:right;margin-left:6px;background:transparent url(../buttons/mail.png) no-repeat center center;}
  3.  
  4. #languages {width:100px;float:left;margin-top:13px;margin-left:20px;}

Copy the icon for the Contact button in the folder buttons:

  1. /cms/site8
    1. buttons
      1. mail.png

  1. <div id="banner">
  2. <?php
  3. echo $logo;
  4. if (isset($menu)) {
  5.     echo $menu;
  6. }
  7. if (isset($languages)) {
  8.     echo $languages;
  9. }
  10. ?>
  11. </div>

The view adds the code for the menu to the banner if $menu is defined.

Enter http://localhost/cms/site8 in the address bar of your navigator.

Comments

To add a comment, click here.