QRdvark.com

Introduction To eZ Components part 2

Introduction To eZ Components part 2

eZ Components From go to WHOA!Part 2

Contents

  1. Abstract
  2. White Board
  3. The Template
  4. Templates Including Templates
  5. First Contact
  6. Form Definitions
  7. Putting it all together

Abstract

In part one of this series, we saw how simple and quick it was to put together a basic website using eZ Components. In part two, we will see how to create a form contact template, how to secure the form, how to sanitize and validate data coming from the form, and finally, how to safely send an email based on information from the form. You have most likely seen this on nearly any site you visit, but you would be surprised at how few of them are secured to prevent SPAM by methods of header injection.

To understand the code flow through-out this tutorial it is assumed you have read part one of this series to get you started.

In this tutorial we will be using several eZ components from the component library. We have already seen in Part One the use of the template component. Here we will introduce the UserInput component and the Mail component. By using these together, we will create a contact form that is both functional, and secure.

White Board

When considering a contact form, many regard it as a "30 minute job". This could be true but the results could be a nightmare. When planning any functionality, it is good practice to see what functionality is required, and what measures need to be taken to prevent user errors, and more importantly, abuse from malicious users.

In this contact form we will need to do following:

  • Form Template
  • Required Fields
  • Form to Post to self
  • Repopulate form if error in data
  • Mail Data
  • Success Template
  • Prevent multiple POSTs
  • Error Template

So we see our "30 minute job" has taken on larger proportions, and rightly so. SPAM is a menace on the Internet and we must play our role in preventing it, not to mention the bandwidth cost you may be liable for should a spammer find your form vulnerable.

The Template

Building on the template from the first example, a few additions can be made, not the least is a little structure so that we can build a menu to access different pages we build. We are, after all, building a web site.

To create the contact page, there are two methods we could employ to achieve this.

  1. Create a new file called contact.php
  2. Include a file based on a variable from GET

Each method has its merits. By creating a new file specifically to handle the contact form allows for ease of maintenance. By including a template file based on a GET variable, reduces the size of our code base, and can be controlled from a switch() in index.php thus providing a single point of access to the site. For the purposes of this tutorial, the first method will be used and a contact.php file will be created along with a contact_form.ezt template.

Templates including templates

The template is a simple form with the exception that we will now be needing a common header and footer to avoid multiple copies of HTML code and allow easier maintenance of site layout and template maintenance. Lets split up our main.ezt file into three new files. The will be called:

  1. common_header.ezt
  2. main.ezt
  3. common_footer.ezt

What we will be doing, is including the common_header.ezt and the common_footer.ezt templates from the main.ezt template. To archive this, the template engine language provides us with an include function like this:

{include "common_header.ezt" send $object}
The syntax provides a method to both include another template within the current template, and then to send the $object variable to the included template, thus making the variables available within it.

Our new template files will now look this way:

common_header.ezt

{use $object}
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<title>{$object->title}</title>
<style type="text/css">@import url("css/style.css");</style>
</head>

<body>
<div id="container">

<div id="header">
<h1>{$object->heading}</h1>
</div>

<!-- menu -->
<div id="nav">
<p><a href="index.php">Home</a> | <a href="contact.php">Contact</a></p>
</div>
<!-- end menu -->

main.ezt

{use $object}

{include "common_header.ezt" send $object}

<!-- content area -->
<div id="content">
<h2>This is the main page</h2>
<p>{$object->content}</p>
</div>
<!-- end content -->

{include "common_footer.ezt"}

common_footer.ezt

<div class="footer">
Footer message goes here
</div>
<!-- end of container --> </div> </body>
</html>

In the common_header.ezt template file, the file begins with {use $object} just as it would in the main.ezt template, even though the index.php file, does not send the $object variable to the common_header.ezt template. The $object variable is sent to the main.ezt template. The main.ezt template, in turn, includes the common_header.ezt template, and sends the $object variable along with it.

At the bottom of the main.ezt template, another include is used to include the common_footer.ezt template. The $object variable is not sent to the template, as it makes no use of variables, it is simply static content. Of course, as the common_footer.ezt template file makes no use of the $object variable, there is not need to put {use $object} into the that file. Only files that will use a variable, need to declare {use $object}.

The index.php file located in the html directory remains much the same as seen in the part one of this series. It sets some configuration settings and creates an object which contains the variables that will be used within the templates. The index.php file looks like this:

<?php

/*** path to the base file ***/
 
$ezBase '/www/ezcomponents/trunk/Base/src/base.php';

 if(!
file_exists($ezBase))
    {
    echo 
'Could not find base file in '.$ezBase;
    }
 else
    {
    
/*** include the base file ***/
    
include $ezBase;

    
/*** Autoload the ezcomponent classes. ***/
    
function __autoload$className ){
      
ezcBase::autoload$className );
    }

    
/*** set some config options ***/
    
$config ezcTemplateConfiguration::getInstance();

    
/*** set the template path ***/
    
$config->templatePath '/www/templates';

    
/*** set the compiled_templates path ***/
    
$config->compilePath '/www';

    
/*** Use the default configuration. ***/
    
$template = new ezcTemplate();

    
/*** create an object instance ***/
    
$object = new stdClass();

    
/*** page title ***/
    
$object->title 'My eZ Site';

    
/*** page heading ***/
    
$object->heading 'My Home Page';

    
/*** send a third content variable ***/
    
$object->content 'Home page content goes here';

    
/*** send the object to the template engine ***/
    
$template->send->object $object;
    try    {
        
/*** Compiles the template and returns the result. ***/
        
echo $template->process'main.ezt' );
        }
    catch(
Exception $e)
        {
        echo 
$e->getMessage();
        }
    }
?>

With the index.php file located in the html directory, your web tree should now look like this:

          /www -+     
                      | 
                     + html +
                      |           |
                      |          + index.php
                      | 
                     + ezcomponents
                      | 
                     + templates +
                      |                    |
                      |                   + common_footer.ezt
                      |                    |
                      |                   + common_header.ezt
                      |                    |
                      |                   + main.ezt
                      | 
                     + compiled_templates

When the index.php file accessed, the results look like this:

My Home Page

This is the main page

Home page content goes here

So, now the site begins to take shape, and adding further templates and pages becomes quite simple. Any further pages can be simply follow this same process and include the header and footer and any other template you wish. Before we look at the code for the contact form, we will need the form template itself, and a common_message template.

The common_message.ezt is a simple template that can be displayed when we need to send the user a message, such as an error message or a "Thank you for your mail" message. All that this template requires is a common_heading and common_message variable. I will look like this:

{use $object}

{include "common_header.ezt" send $object}

<h2>{$object->common_heading}</h2>
<p>{$object->common_message}</p>
{include "common_footer.ezt" send $object}

The common_message template above uses the same functionality we saw in the main template. It simply has the $object variable available to carry our template data, and includes the header.ezt and footer,ezt templates. All of our templates are based on this simple layout. This is a small part of the power available with eZcomponents, but demonstrates how simply and quickly new pages can be added after you have the initial framework complete.

The form template itself is a little more complex, but still uses the same basic functionality as above. We will have several fields in our form to demonstrate some of the functionality available with the eZ Components UserInput Component. The contact.ezt form template will look like this:

{use $object}

{include "common_header.ezt" send $object}

<form method="post">
<form method="post" action="{$object->action}">
<input type="hidden" name="form_token" id="form_token" value="{$object->property_formtoken}" />
<fieldset>

<legend>Please Send a Message!{$object->warning_form_token}</legend>
<label for="firstName">First Name: {$object->warning_firstName}</label><br />
<input type="text" maxlength="40" class="textfield" name="firstName" value="{$object->property_firstName}" id="firstName" />
<br />

<label for="zipCode">Zip Code: {$object->warning_zipCode}</label><br />
<input type="text" maxlength="40" class="textfield" name="zipCode" value="{$object->property_zipCode}" id="zipCode" />
<br />

<label for="age">Age: {$object->warning_age}</label><br />
<input type="text" maxlength="40" class="textfield" name="age" value="{$object->property_age}" id="age" />
<br />

<label for="subject">Subject: {$object->warning_subject}</label><br />
<input type="text" maxlength="40" class="textfield" name="subject" value="{$object->property_subject}" id="subject" />
<br />

<label for="email">Email: admin@qrdvark.com {$object->warning_email}</label><br />
<input type="text" maxlength="40" class="textfield" name="email" value="{$object->property_email}" id="email" />
<br />

<label for="message">Message:{$object->warning_email}</label><br />
<textarea name="message" id="message" cols="40" rows="4">{$object->property_message}</textarea>
<br />

<input type="submit" value="submit"/><br/>
</form>

{include "common_footer.ezt" send $object}

The contact.ezt form above provides for accessing variables in the same manner as we have seen in all the other templates. And like all other templates, includes the header.ezt and footer.ezt templates. Site creation has never been easier. In the contact.ezt template we once again use the $object object to carry our variables form the contact.php page to the template. You will note in the contact.ezt template that the form has a variety of values that you might find in most common contact pages on the web. Each of these forms has a corresponding property value and warning value. These will be generated by the contact.php file for use to repopulate the form if there is an error and to display a warning for the field that has incorrect information. Note also a hidden field called form_token.

A form token is simply a value set in a session variable, and in a hidden field in the form. When the form is POSTed, the session variable must match the POST variable. If these do not match, then we know there has been an illegal access attempt. The SESSION variable is destroyed when the form is POSTed to stop re-posting either by refreshing the browser, or somebody trying to exploit the form using CURL.

Lets move now to the contact.php file itself. As we have seen up to this point, this file will carry the logic side of the contact. It will determine if the form is valid and if the form data is valid. Should all the form values be valid, then it will send an email to you with the values filled in by the form user.

First Contact

The contact.php file begins just as with the main file, and looks initially to be very much the same. In fact, it is. The same start-up procedure is used to include the eZ Components base, and to set variables for the form. The file begins by setting a session variable. This will be used later as we set a form token to help secure the form. Lets take a look at how it begins:

<?php

 
/*** begin a session ***/
 
session_start();

 
/*** path to the base file ***/
 
$ezBase '/www/ezcomponents/trunk/Base/src/base.php';

 if(!
file_exists($ezBase))
    {
    echo 
'Could not find base file in '.$ezBase;
    }
 else
    {
        
/*** include the base file ***/
        
include $ezBase;

        
/*** Autoload the ezcomponent classes. ***/
        
function __autoload$className ){
          
ezcBase::autoload$className );
        }
    }

    
/*** set some config options ***/
    
$config ezcTemplateConfiguration::getInstance();

    
/*** set the template path ***/
    
$config->templatePath '/www/templates';

    
/*** set the compiled_templates path ***/
    
$config->compilePath '/www';

    
/*** Use the default configuration. ***/
    
$template = new ezcTemplate();

    
/*** create an object instance ***/
    
$object = new stdClass();

    
/*** page title ***/
    
$object->title 'My eZ Contact Page';

    
/*** page heading ***/
    
$object->heading 'Contact Me';

    
/*** send a content variable ***/
    
$object->content 'Please fill in all fields';
?>

So, no surprises in the above. The template variables are set just as we saw in Part One.

Never Trust User Input

Never Trust User Input

Never Trust User Input

just in case you did not get that

Never Trust User Input

I have always been a believer that each form should have its own custom validation class. Whilst some validations procedures are common to many forms, there are more than a few occurrences where custom validation is needed. This has not changed with the introduction of eZ Components. The folks at eZ Components have taken this in mind when developing and a full suite of data validation and sensitization functions are available to ensure secure transactions between users and the application or site.

By utilizing the UserInput filter in eZ Components we can safely receive variables from forms, and then use the values posted. Lets look at the design of our mail form and submission. The UserInput Component provides a solid base set of validation and sensitization tools to help us on our way to securing our form data, whilst it allows also for a great deal for customization. By defining an array of validation definitions, we can safely dictate what variables to receive from the form. Should a malicious user tries to POST their own form with extra fields and variables to our contact.php page, then only those variables defined in the array will be accepted, no injection of variables is possible this way. Similarly, should a malicious user try to omit fields from the form, this can be dealt with also with the use of the REQUIRED keyword. By using REQUIRED we are saying that this field must be present in the form, not that it must have a value. To check that a form field value has a valid value, we can use a callback function to our custom validation class.

The custom validation class and the form definition look like this:

<?php

class checkVars {

 public function 
formToken(){
   return 
md5(rand());
 }


 public function 
checkEmpty($var){
  
$var filter_var($varFILTER_SANITIZE_STRING);
  return (
strlen($var) !== 0) ? $varnull;
 }

 function 
safeEmail($string) {
  
$string =  preg_replace'((?:\n|\r|\t|%0A|%0D|%08|%09)+)i' ''$string );
  
/*** check string length also ***/
  
$string strlen($string) > 40 null $string;
  return 
self::checkEmpty($string);
}

public function 
checkMessage($message) {
 
$string strlen($message) > 512 null $message;
 return 
self::checkEmpty($string);
}

public function 
checkToken($form_token){
 if(
$_SESSION['form_token'] != $form_token)
 {
     
$form_token null;
 }
 return 
self::checkEmpty($form_token);
}

/*** end of checkVars class ***/




 
$definition = array(
     
'form_token' => new ezcInputFormDefinitionElement(
         
ezcInputFormDefinitionElement::REQUIRED'callback',
       array( 
'checkVars''checkToken' )
     ),
     
'firstName' => new ezcInputFormDefinitionElement(
         
ezcInputFormDefinitionElement::REQUIRED'callback',
       array( 
'checkVars''checkEmpty' 
    ),
     
'zipCode' => new ezcInputFormDefinitionElement(
         
ezcInputFormDefinitionElement::REQUIRED'int',
         array( 
'min_range' => 2000'max_range' => 99999)
     ),
     
'age' => new ezcInputFormDefinitionElement(
         
ezcInputFormDefinitionElement::REQUIRED'int',
         array( 
'min_range' => 1'max_range' => 99 ),
         
FILTER_FLAG_ALLOW_HEX
     
),
     
'subject' => new ezcInputFormDefinitionElement(
         
ezcInputFormDefinitionElement::REQUIRED'callback',
       array( 
'checkVars''checkEmpty' )
     ),
    
'message' => new ezcInputFormDefinitionElement(
         
ezcInputFormDefinitionElement::REQUIRED'callback',
       array( 
'checkVars''checkMessage' )
     ),
     
'email' => new ezcInputFormDefinitionElement(
         
ezcInputFormDefinitionElement::REQUIRED'validate_email'
     
),
 );

?>

Lets just step through what has happened above. First we have a custom named checkVars that will be used by callbacks from the form definitions array to make sure the form input fields contain valid data. The strength of the UserInput component allows us to use the standard validation functionality or create our own definition of what constitutes valid data.

Following the checkVars class we have the form definition, this is essentially the heart of the form. It dictates how to handle data received from the form. For those familiar with PHP Filter you will find the syntax very similar. Lets have a look at the first definition which defines the hidden field form_token. By specifying this as REQUIRED we are say that this field MUST be part of the form. anybody trying to use another form without this present will get an error. The definition then as a callback to the checkVars class method checkToken. This class method can now be used to validate the value of form_token. In this case the form_token from POST must equal the one in our SESSION.

As we move down to the zip code definition, we see no call back is used, rather, it makes use of eZ Components built in validation of integers. Once again this field is REQUIRED and a value of between 2000 and 99999 is needed for the value to be valid. Remember, the REQUIRED value only means that this form element must present, not that it must have a value.

As we see with the email definition, other validation methods are built into eZ Components, or, you can simply define your own validation rules with a custom class to handle them for you. With eZ you get the best of both worlds.

Form Definitions

From here we need only to loop over the definitions to and preset some variables that we can then use to hold our form field values. We see the use of the ezcInputForm::hasPostData() method to first check if the form access is coming from POST, and if it is not, the session form_token is set. As we loop through the definitions, a check is add to see if the have valid data. This is taken care of for us by the components as it checks the value of each form field against the respective definition and if it is valid, it is assigned and a valid flag is set, and if the value is invalid an invalid flag is set. Here is the code:

<?php
/*** check the form was POSTed ***/
if ( ezcInputForm::hasPostData() )
    {
    
$form = new ezcInputFormINPUT_POST$definition );

    foreach ( 
array_keys($definition) as $name )
        {
        
$propertyName "property_$name";
        
$propertyWarningName "warning_$name";
        
// check for valid data
        
if ( $form->hasValidData$name ) )
            {
            
// if we have valid data
            
$$propertyName $form->$name;
            
$process 1;
            
/*** set a valid flag ***/
            
$errors[$name] = 'valid';
            }
        else
            {
                $
$propertyName htmlspecialchars$form->getUnsafeRawData$name ) );
            $
$propertyWarningName '[invalid]';
            
/*** set an invalid flag ***/
            
$errors[$name] = 'invalid';
            }
        }
    }
else
    {
    
/*** if nothing is posted, set a form token ***/
    
$form_token checkVars::formToken();
    
$_SESSION['form_token'] = $form_token;
    }
?>

Putting it together

Rather than piece it all together a complete file is provided here for your use. Have a play around with the definitions and the class methods to define your own form fields an what you decide is valid data for them.


<?php

 
/*** begin a session ***/
 
session_start();

 
/*** path to the base file ***/
 
$ezBase '/www/ezcomponents/trunk/Base/src/base.php';

 if(!
file_exists($ezBase))
    {
    echo 
'Could not find base file in '.$ezBase;
    }
 else
    {
        
/*** include the base file ***/
        
include $ezBase;

        
/*** Autoload the ezcomponent classes. ***/
        
function __autoload$className ){
          
ezcBase::autoload$className );
        }
    }

    
/*** set some config options ***/
    
$config ezcTemplateConfiguration::getInstance();

    
/*** set the template path ***/
    
$config->templatePath '/www/templates';

    
/*** set the compiled_templates path ***/
    
$config->compilePath '/www';

    
/*** Use the default configuration. ***/
    
$template = new ezcTemplate();

    
/*** create an object instance ***/
    
$object = new stdClass();

    
/*** page title ***/
    
$object->title 'My eZ Contact Page';

    
/*** page heading ***/
    
$object->heading 'Contact Me';

    
/*** send a content variable ***/
    
$object->content 'Please fill in all fields';


class 
checkVars {

 public function 
formToken(){
   return 
md5(rand());
 }


 public function 
checkEmpty($var){
  
$var filter_var($varFILTER_SANITIZE_STRING);
  return (
strlen($var) !== 0) ? $varnull;
 }

 function 
safeEmail($string) {
  
$string =  preg_replace'((?:\n|\r|\t|%0A|%0D|%08|%09)+)i' ''$string );
  
/*** check string length also ***/
  
$string strlen($string) > 40 null $string;
  return 
self::checkEmpty($string);
}

public function 
checkMessage($message) {
 
$string strlen($message) > 512 null $message;
 return 
self::checkEmpty($string);
}

public function 
checkToken($form_token){
 if(
$_SESSION['form_token'] != $form_token)
 {
     
$form_token null;
 }
 return 
self::checkEmpty($form_token);
}

/*** end of checkVars class ***/




 
$definition = array(
     
'form_token' => new ezcInputFormDefinitionElement(
         
ezcInputFormDefinitionElement::REQUIRED'callback',
       array( 
'checkVars''checkToken' )
     ),
     
'firstName' => new ezcInputFormDefinitionElement(
         
ezcInputFormDefinitionElement::REQUIRED'callback',
       array( 
'checkVars''checkEmpty' 
    ),
     
'zipCode' => new ezcInputFormDefinitionElement(
         
ezcInputFormDefinitionElement::REQUIRED'int',
         array( 
'min_range' => 2000'max_range' => 99999)
     ),
     
'age' => new ezcInputFormDefinitionElement(
         
ezcInputFormDefinitionElement::REQUIRED'int',
         array( 
'min_range' => 1'max_range' => 99 ),
         
FILTER_FLAG_ALLOW_HEX
     
),
     
'subject' => new ezcInputFormDefinitionElement(
         
ezcInputFormDefinitionElement::REQUIRED'callback',
       array( 
'checkVars''checkEmpty' )
     ),
    
'message' => new ezcInputFormDefinitionElement(
         
ezcInputFormDefinitionElement::REQUIRED'callback',
       array( 
'checkVars''checkMessage' )
     ),
     
'email' => new ezcInputFormDefinitionElement(
         
ezcInputFormDefinitionElement::REQUIRED'validate_email'
     
),
 );


/*** loop of the definitions ***/
foreach ( array_keys($definition) as $name )
 {
     
$propertyName "property_$name";
     
$propertyWarningName "warning_$name";
     $
$propertyName '';
     $
$propertyWarningName '';
 }

/*** errors array ***/
$errors = array();


/*** check the form was POSTed ***/
if ( ezcInputForm::hasPostData() )
    {
    
$form = new ezcInputFormINPUT_POST$definition );

    foreach ( 
array_keys($definition) as $name )
        {
        
$propertyName "property_$name";
        
$propertyWarningName "warning_$name";
        
// check for valid data
        
if ( $form->hasValidData$name ) )
            {
            
// if we have valid data
            
$$propertyName $form->$name;
            
$process 1;
            
/*** set a valid flag ***/
            
$errors[$name] = 'valid';
            }
        else
            {
                $
$propertyName htmlspecialchars$form->getUnsafeRawData$name ) );
            $
$propertyWarningName '[invalid]';
            
/*** set an invalid flag ***/
            
$errors[$name] = 'invalid';
            }
        }
    }
else
    {
    
/*** if nothing is posted, set a form token ***/
    
$form_token checkVars::formToken();
    
$_SESSION['form_token'] = $form_token;
    }


/*** check for errors ***/
$values array_count_values($errors);

/*** check all the values are valid ***/
if(array_key_exists('valid'$values) && $values['valid'] == sizeof($definition))
{
    
// Create a new mail object
    
$mail = new ezcMail();
 
    
// Specify the "from" mail address
    
$mail->from = new ezcMailAddress$property_email$property_firstName );

    
// Add one "to" mail address (multiple can be added)
    
$mail->addTo( new ezcMailAddress'kevin@example.com''Big Kev' ) );

    
// Specify the subject of the mail
    
$mail->subject $property_subject;

    
// Specify the body text of the mail as a ezcMailText object
    
$mail->body = new ezcMailText$property_message );

    
// Create a new MTA transport object
    
$transport = new ezcMailMtaTransport();

    
// Use the MTA transport to send the created mail object
    
$transport->send$mail );

    
/*** destroy the session token as the contact is complete ***/
    
unset($_SESSION['form_token']);

    
/*** send to common template ***/
    
$object->common_heading "Mail Sent";
    
$object->common_message "Your mail has been sent, thanks for dropping us a note";
    
$template->send->object $object;
    echo 
$template->process'common.ezt' );
}
else
{

  
$object->property_zipCode $property_zipCode;
  
$object->property_firstName $property_firstName;
  
$object->property_subject $property_subject;
  
$object->property_email $property_email;
  
$object->property_age $property_age;
  
$object->property_message $property_message;
  
$object->property_formtoken $form_token;

  
$object->warning_zipCode $warning_zipCode;
  
$object->warning_firstName $warning_firstName;
  
$object->warning_subject $warning_subject;
  
$object->warning_email $warning_email;
  
$object->warning_age $warning_age;
  
$object->warning_message $warning_message;
  
$object->warning_form_token $warning_form_token

  
$template->send->object $object;
  echo 
$template->process'contact.ezt' );
  }

?>