Drupal for Building Standalone Forms
Forging Front-end Finesse without Back-end Bulk
By Michael Ross
This article was published as a cover story in the print magazine Drupal Watchdog, Volume 6 Issue 2, Fall 2016, on pages 36-39, by Linux New Media. The magazine was distributed at Drupalcon Dublin, 2016-09-26.
When a web project manager chooses a CMS or other technology platform for building a website critical to her enterprise or not-for-profit organization, much more than the project budget is at risk, because if the project fails spectacularly or has worse unintended consequences, the monetary and industry reputation costs could be significant — enough to result in layoffs or other unpleasantries. Website owners are rightfully worried of what security pitfalls lie within any given option. Not a month goes by that we don't read in the news of a major website or web app falling prey to digital attackers, and the detrimental effects to the organizations that chose that technology, including massive lawsuits.
Consequently, a growing number of people responsible for deciding on website technologies are opting for choices that seem much safer, such as static websites (i.e., sites built using the traditional HTML, CSS, and JavaScript, but no server-side code or database to be hacked) or site-generating tools. This is especially true for any pages allowing input from unauthorized users, including the ubiquitous form pages that solicit text and other information from site visitors.
In my work, I find prospective clients are increasingly asking for form pages that do not rely upon any backend dynamic programming, but simply send to the client all the submitted values. One prospect wanted to avoid the need for constant security updates (and the associated billable hours) and minimize the risk that an update to the framework could adversely affect the form itself. Such a project is admittedly modest in scope, but still potentially important to the site owner. This is increasingly relevant, as Drupal becomes more sizable and complex.
Should you take on such a project, you have some options, including the obvious one of building the form page by hand. This may result in tight HTML code, but can be quite time-consuming and error-prone, especially if you're not using an editor that flags such problems as unmatched tags. Tools such as the (seemingly immortal) Dreamweaver can be used for creating form pages that use a minimum of PHP or other web scripting to send the user input data on its way.
You could use an online form builder, such as Google Docs, Wufoo, FormSite, Formstack, or JotForm. But these are all hosted on their servers, giving you much less control over the site availability and customization, and may be unacceptable to your prospective client.
Drupal and other CMSs may not even be considered options, but they actually can be used for building form pages even if not used for form processing. This leads to the question: When is a Drupal form not a Drupal form? When it is created using Drupal but not rendered or processed by Drupal. We will examine how to do that, using Drupal 8 — to benefit from the native input validation of its HTML5 markup.
Building an Example Form
To illustrate one possible approach to solving this problem, let's assume we want to create a standalone contact form that has a few more fields than typically found on such forms, and is consistent with the visual styling of the rest of the web property.
We begin with a fresh installation of Drupal 8 using the Standard profile. If the form we wanted to create is quite unlike a conventional contact form, we would begin with a brand-new form (which Drupal 8 would oddly still refer to as a "contact form"). In this case, it makes sense to instead modify the default form, named "Website feedback" (found at the Drupal path admin/structure/contact). The "Message" field can be reduced in size from the default 12 rows. To illustrate some of the built-in input validation provided by HTML5, we can add extra fields for a URL, a date, and a number.
Figure 1. Content form modified
Using Drupal offers an additional benefit in that you can easily apply an existing theme, thereby saving time in manually styling the page elements. In this example, we will stick with the default public theme, Bartik.
Once you have finished adding, positioning, and otherwise customizing all of the fields for the form, log out of the website, so that the saved form page has the simplest code possible. Go to the form page in your browser (at /contact/feedback, in our example) and save it as a file, being careful to set the file type to whatever your browser uses to designate saving not just the HTML alone but also all the other needed files that comprise the elements on the page. In Firefox, the steps are: File > Save Page As, and for the "Save as type" list box choose the option "Web Page, complete (*.htm;*.html)". In Google Chrome, the steps are: hamburger button > "More tools" > "Save page as", and for the file type choose "Webpage, Complete". Open the HTML file in your browser to verify that it looks identical to the original form page that was rendered by Drupal 8 and saved locally.
All of the CSS and image files that were saved at the same time as the HTML file, should be located in a folder whose name may vary depending upon which operating system you're using. In this example, we named the HTML file contact.html
and consequently the folder of supplemental files is named contact_files
. Whatever you name it, do not include spaces in the file name to separate individual words, but instead use underscores or hyphens, if relevant. Rename the HTML file to have the file extension ".php". You may receive warnings that the file will become unusable and that it will no longer belong to the aforementioned folder. Open the PHP file in your browser, but this time loaded as a web page and not as a regular file — in other words, make sure the protocol prefix is "http://" and not "file://".
Look carefully to see if any icons or other images are missing in the new copy of the form. This usually happens when an image is only referenced in one or more of the CSS files using an absolute path that does not meet the path requirements for your local operating system, e.g., /core/misc/icons/ee0000/required.svg in Windows. A simple solution is to copy those missing image files into the contact_files folder, and then in the CSS files remove the absolute paths, thus changing the CSS rules so they look for the images within the same folder.
Also, be sure to remove the "Preview" button, as there is no longer a Drupal backend to support it.
Form Data Transmission
Earlier we renamed the locally-saved form file so that its extension indicates PHP instead of HTML, because we will need to add some code to gather the form data submitted by the user and then send that to the site owner. But first we will alter the <form></form>
tag so the page does not try to execute the Drupal form processing for which it was originally coded, but instead runs our new code. In that tag, replace action="/contact/feedback"
with action=""
. As a consequence, instead of the web server passing execution to a (now-nonexistent) path, it passes it back to the form page. For the same reason, you will need to remove the JSON script element that begins with <script type="application/json" data-drupal-selector="drupal-settings-json">
and ends with </script>
.
At the top of the form file (contact.php), we put PHP code that formats the field names and values in a readable manner, and then emails it all to the site owner's email address. There are countless ways that one could code this functionality; the following is only one possible approach.
<?phpif ( isset( $_POST[ 'op' ] ) ) { $fields = array( 'Visitor name: ' . $_POST['name'], 'Email address: ' . $_POST['mail'], 'Subject: ' . $_POST['subject'][0]['value'], 'Website URL: ' . $_POST['field_current_website_url'][0]['uri'], 'Deadline: ' . $_POST['field_project_deadline'][0]['value']['date'], 'Budget: ' . $_POST['field_project_budget'][0]['value'], 'Message: ' . $_POST['message'][0]['value'] ); mail( 'Site owner <info@example.com>', 'Message from visitor', implode( $fields, "\n" ), 'From: noreply@example.com' );}?>
Here we simply send along whatever form data has been supplied, which is the most limited extent of data handling that is possible. There are several subsequent enhancements that you could — and for a production system, certainly would — make to this starting point, specifically:
- Check if any of the fields are missing values or were supplied with unacceptable values. If either is the case, then provide to the visitor some detailed error message. (More in a moment on the validation of the data input.) When the form is rendered again for the visitor, prepopulate the fields with the values previously submitted, so they don't have to be re-entered.
- Check the return value from the mail() function call. If it shows a successful transmission of the fields string, then announce that to the visitor, or otherwise present an error message and information on an alternative method of contacting the site owner.
Anyone who has worked with processing Drupal form submissions knows that the way Drupal structures the submitted values within the $_POST nested array is neither simple nor is it consistent from one field to the next. Consequently, we are not able to rewrite the above code elegantly using field names as variable names. Secondly, if and when you write code like this yourself, you will need to carefully examine the $_POST global array to see how each field value is represented within that nested array. Begin by sending its contents to the screen, e.g., print_r( $_POST );
Form Data Validation
Since Drupal is not part of the final setup, one naturally cannot use its built-in server-side capabilities for verifying that the user has entered valid data into the form fields. To resolve this, one could check the value of each required field, and if the visitor has failed to enter some sort of input, then a friendly error message could be displayed. For instance, if the visitor forgot to add her name and a subject, your code could output:
<div class="messages error"> <h2 class="element-invisible">Error message</h2> <ul> <li>Your name field is required.</li> <li>Subject field is required.</li> </ul></div>
As long as you utilize the same CSS classes as what is expected by the Drupal theme that the site owner has chosen (if any), then the appropriate styling could be borrowed from the stylesheets of that theme.
But how about client-side validation using JavaScript? That of course is an option, but will not be demonstrated here, partly because much of that work is unneeded if one makes heavy use of HTML5's native capabilities. In fact, that's one of the reasons why we chose Drupal 8 for this article instead of any earlier version (and one more reason to choose Drupal 8 for your future web development work).
For instance, HTML5 can verify that any required field contains at least some value.
Figure 2. HTML5 validation of value required
It may not be able to confirm that an email address corresponds to an existing account or that it is not a disposable address, but at least HTML5 can ensure that the address has the correct format.
Figure 3. HTML5 validation of email address
There are more types of data validation that one could experiment with — whether relying upon HTML5 or not — including date and time fields.
There should be enough information in this article to get you thinking about the possibilities of using Drupal for creating framework-free forms.
Copyright © 2016 Michael J. Ross. All rights reserved.