Contact form
We’ll set up a simple contact form to show how forms work. The data will be sent to a fixed email address so that the request can be answered from there.
Bots and forms
Do not send automated replies directly to form senders. Automated bot traffic has increased massively in recent years. Unprotected auto-replies can damage your domain reputation and cause your mail server to be blocked.
That’s why we’re creating a contact form with a Honeypot in this example. For this we need
- a section
- a data source
- an email template
- an event
Email delivery
By default, emails are sent via sendmail
. Many mailbox providers treat these emails as unsafe and deliver them to the SPAM folder. Therefore, switch the email delivery method to SMTP
in the “Preferences”.
Create a section
To save the requests from the front end, we need a section.
Create a new section (“Blueprints“ → “Sections“ → “Create New“).
Name → “Contact form“
Navigation Group → “Forms“
Next, we add the following fields to the section:
- a field “Text Input“ named “First name“ and
required
- a field “Text Input“ named “Last name“ and
required
- a field “Email“ named “Email“ and
required
(you can use aplaceholder
-> “jane.doe@example.net” too) - a field “Text area“ named “Message“ and
required
Create a data source
To retrieve the latest entry from the section, we need a data source.
Create a new data source (“Blueprints“ → “Data Sources“ → “Create New“).
- Choose as source the section “Contact form“.
- Name the data source e.g. “ETM: Contact form“.
- Set as “Required Parameter“ →
$etm-entry-id
, - Choose as filter “System ID“ →
{$etm-entry-id}
- Enable “Pagination“ and set the amount to
1
- Under “Included Elements“, select the following fields:
- First name,
- Last name,
- Email,
- Message
- Attach this data source to a page (e.g. “Contact“).
- Create the data source by clicking the button “Create Data Source“.
Parameter to filter
$etm-entry-id
will contain the id of the entry inserted by the event. You can use this parameter to filter a datasource and to email the data entered by the user.
Create an email template
Since we named our previously created data source “ETM: Contact form,” we know that a new entry will be written in the following node: /data/etm-contact-form/entry
. With this knowledge and the naming of the fields in the section, we can build the recipient as a “Reply-To” address in the email template.
Create a new email template (“Blueprints“ → “Email Templates“ → “Create New“).
- Name the template “CF: New message”.
- Select the previously created data source “ETM: Contact form”.
- As layout, a “Plain only” is sufficient.
- Email settings:
- Enter a subject line, e.g., “New message via contact form.”
- As the recipient, we select a fixed email address from which the request can be answered →
your-email@example.net
. - The “From Name” and “From Email Address” fields can be left blank and will be loaded from the email settings in “Preferences”.
- For “Reply-To Name” we use the entry from the data source:
{/data/etm-contact-form/entry/first-name} {/data/etm-contact-form/entry/last-name}
- For “Reply-To Email” we use the entry from the data source:
{/data/etm-contact-form/entry/email}
- Create the template by clicking the button “Create Template”.
The email template can be found in the directory workspace/email-templates/cf-new-message/template.plain.xsl
. Open the template and add the following:
<xsl:template match="/">
<xsl:variable name="first-name" select="/data/etm-contact-form/entry/first-name" />
<xsl:variable name="last-name" select="/data/etm-contact-form/entry/last-name" />
<xsl:variable name="email" select="/data/etm-contact-form/entry/email" />
_______________________________
New Message:
From: <xsl:value-of select="$first-name" /><xsl:text> </xsl:text><xsl:value-of select="$last-name" /><xsl:text> </xsl:text><xsl:value-of select="$email" />
Message:
<xsl:value-of select="/data/etm-contact-form/entry/message" />
</xsl:template>
Create an event
Finally, we need an event. It ensures that
- the data from the contact form is saved from the frontend to the section,
- the email is sent.
Create a new event (“Blueprints“ → “Events“ → “Create New“).
- Select as target the section “Contact form“.
- Name the event “CF: Send message“.
- Choose as flters the Email Template previously created: “Send Email Template: CF: New message“.
- Create the event by clicking the button “Create Event”.
You can copy the generated “Example Front-end Form Markup” and paste it into your page template:
<form method="post" action="{$current-url}/" enctype="multipart/form-data">
<!-- To validate the event result (‘success’ | ‘error’) as well as possible error messages for the fields in the front-end, switch the `action` to `{$current-url}/?debug`. -->
<label>First name
<span aria-label="Required field" class="required-mark">*</span>
<input name="fields[first-name]" type="text" required="required" />
</label>
<label>Last name
<span aria-label="Required field" class="required-mark">*</span>
<input name="fields[last-name]" type="text" required="required" />
</label>
<label>Email
<span aria-label="Required field" class="required-mark">*</span>
<input name="fields[email]" type="email" spellcheck="false" autocapitalize="off" required="required" placeholder="jane.doe@example.net" autocomplete="email" />
</label>
<label>Message
<span aria-label="Required field" class="required-mark">*</span>
<textarea name="fields[message]" rows="15" cols="50" required="required"></textarea>
</label>
<input name="action[cf-send-message]" type="submit" value="Submit" />
</form>
Page Template
If you have pasted the “Example Front-end Form Markup” generated from the event into your page template, you can run a first test. Fill out all the fields and send the message.
The message should be saved in the section “Contact form” as a new entry and sent to the fixed email address:
_______________________________
New Message:
From: Tim Böden tim@example.net
Message:
Hello world!
Regards
Tim
You can reply to the email: The data from the Reply-To
header is automatically set as the To
address.
Debug the form
During development, switch action
of the form to {$current-url}/?debug
. This will switch to debug mode after the form is submitted, allowing you to view the result of the event as well as possible error messages for the fields.
If the submitted form contains errors, the corresponding errors are listed in the event for the fields:
<data>
<param>
...
</params>
<events>
<cf-send-message result="error">
<message message-id="102">Entry encountered errors when saving.</message>
<email label="Email" type="invalid" message-id="302" message="Invalid email format." />
<message label="Message" type="missing" message-id="301" message="‘Message’ is a required field." />
<post-values>
<first-name>Tim</first-name>
<last-name>Böden</last-name>
<email>tim@example</email>
</post-values>
</cf-send-message>
</events>
<etm-contact-form>
<section id="1" handle="contact-form">Contact form</section>
<error required-param="$etm-entry-id">Data source not executed, required parameter is missing.</error>
</etm-contact-form>
</data>
If the form does not contain any errors, the event will look like this in debug mode:
<data>
<params>
...
</params>
<events>
<cf-send-message result="success" type="created" id="122">
<message message-id="100">Entry created successfully.</message>
<filter name="etm-cf-new-message" status="passed" total="1" sent="1" />
<post-values>
<first-name>Tim</first-name>
<last-name>Böden</last-name>
<email>tim@example.net</email>
<message>Hello world!</message>
</post-values>
</cf-send-message>
</events>
<etm-contact-form>
<section id="1" handle="contact-form">Contact form</section>
<entry id="122">
<first-name handle="tim">Tim</first-name>
<last-name handle="boeden">Böden</last-name>
<email placeholder="jane.doe@example.net">tim@example.net</email>
<message><![CDATA[Hello world!]]></message>
</entry>
</etm-contact-form>
</data>
Validating the form
Use the attribute aria-invalid
to mark fields as valid
or invalid
:
aria-invalid="false"
means that the field contains no errors,aria-invalid="true"
means that the field contains errors.
Since all fields in the form are required
, the following errors may occur:
- First name:
missing
- Last name:
missing
- Email:
missing
|invalid
- Message:
missing
With Pico CSS, the fields are already marked accordingly by this attribute:
Honeypot
Measures should be taken to protect the contact form against flooding by bots. One possible option would be a reCaptcha alternative. However, so-called honeypots are still effective. A honeypot can be an additional field in the form that is invisible to regular users.
This field will not be saved in the section.
Add an additional text field to your form (e.g. before the submit button) and use a value, e.g. "fields[additional-info]"
for the attribute name
:
<input type="text" name="fields[additional-info]" class="sr-only" autocomplete="off" tabindex="-1" aria-label="Leave blank" placeholder="Leave blank" value="" />
You can also use a field of the type url
– bots love to insert dubious URLs. 😂
It is important that you give users and screenreader users a hint to not fill out this field:
placeholder="Leave blank"
aria-label="Leave blank"
Make the field visually hidden via style (e.g. class="sr-only"
) and add the attribute tabindex="-1"
so it cannot be reached by users.
In the event "CF: Send message"
you can easily check for the field:
public function load()
{
if (isset($_POST['action']['cf-send-message'])) {
$trap = $_POST['fields']['additional-info'];
if ( !empty($trap) ) {
// It's a bot - do nothing.
return false;
} else {
return $this->__trigger();
}
}
}
With return false;
the page is simply reloaded. No new entry is created in the section and no email is sent.
Tip for forms
Please do not train bots like an AI by displaying error messages. In this case, the page may simply be reloaded.
But make sure, that the event is not writable through Symphony’s blueprints editor. In the event set the function allowEditorToParse()
to false
:
public static function allowEditorToParse()
{
return false;
}