Easy Umbraco Forms

How to build robust and easily-maintained forms, based on a document type.

Forms are something you'll probably need on most websites you build, but they don't come out of the box with Umbraco.

Normally you'll code your own, using user controls or surface controllers, but if you're working with a lot of smallish sites with various forms requirements that change from time to time, that will mean getting dirty with code each time.

Umbraco's paid-for solution Umbraco Forms (which used to be called Contour) is great value at €99 per license, but for some projects it's overkill, or just not worth the admin hassle.

Wouldn't it be nice if all you had to do was create a document type containing the fields you want on the form, and let a script build and handle the form dynamically?

Yes, yes it would, with the benefit that adding a new field to the form becomes as simple as adding a property to a document type. No need to update code and retest, or grapple with Contour.

What you need for this task is a razor script that:

  • Creates the form fields from a specified data type, using the Umbraco reflection API

  • Adds validation using the mandatory flag and validation regex specified on the property

  • Protects against cross-site scripting and other attacks

  • Saves the input to a specified folder in the CMS as an instance of the document type

  • On submission, displays a Thank You message to the user and redisplays the submitted form data

  • Sends the site admin an email containing the submitted form data

Then whenever you need a form on a website you just:

  • Create a folder in the content tree to store the form submissions

  • Create a document type and template for the page that will contain the form

  • Add the script to the page template

  • Create a document type that will define the form fields and add a property for each form field

  • Set the mandatory checkbox and validation rule for each property if required

  • Create the page in the content tree, set the properties

If you're working on an existing project, you might already have the appropriate structures in place, so you can skip or adapt some of these steps.

The code and sample document types are available to download and use as it is or however you like, but I'd appreciate it if you let me know if, how, or where you've used it.

For an example, let's see how this works by using the Umbraco starter package, called TXT, which is installed by default when you install Umbraco v7. You'll find the Umbraco downloads here.

Umbraco Form on TXT.png

Creating the folder

We'll need somewhere to store the data when people submit the form, so in the settings tab, add a document type called Folder that goes in the root...

Folder document type

Then in the content tree, create a new node under the root with type Folder and call it Form submissions.

Form submissions in content tree

Creating the form document type

This defines the input fields that you want on your form. They could be textboxes, radio buttons, check boxes, dropdown lists, etc.

You could of course add these properties to any document type you may already be using, such as a Contact Page for example, but let's create a document type called Example Form with alias ExampleForm, and add Textstring properties for Name and Email, and a Textbox multiple property for Comments.

To demonstrate client-side validation, we'll tick the Mandatory box on Name and Email, and add a regex to the Validation box on the Email property. Sadly there's no perfect regex that will validate every possible email address correctly, so here I'm using the best one I could find.

^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$

Email field properties

Creating the page document type

We'll need a page to hold the form, and we want some unique properties on this page, so we'll create a new document type under Master called Page with a Form. Keep Create Matching Template checked.

Add properties to specify the document type that the form is based on, the folder where the data will be stored, and the thank you message.

Page with a form document type properties

Again, you could put these properties elsewhere, say on a master document type for the form, but this will do for now. Remember to add this document type to the structure tab of the types of node that will contain it. In this case it's the Home page.

Another way would be to convert this to be called from a macro in which case you can add these fields as macro parameters.

Now I'm just using a textstring to specify the document type (NB it's the alias not the name), but you might prefer to use a document type picker package. e.g. http://our.umbraco.org/projects/backoffice-extensions/document-type-picker

Add the page to the content tree

Create a new Page with a Form in the content tree, and set the document ­type alias, storage folder and thank you message.

Page with a form in content tree

The script

So all we need now is the code. I don't believe in over-complicating things, so a razor script will do me fine. For simplicity in this example, I'm building the input fields with bog-standard HTML form here, but you go ahead with whatever you're comfortable with. You can download it here so I'll just pick out the interesting bits.

We've got a document type with some properties on it, how do we find out what they are? Easy with the Umbraco reflection API. This code works in v7.1.4, you might need to use the umbraco.cms.businesslogic library in earlier versions.

// name of the doctype we are using to build and store this form

string doctype = CurrentPage.DocumentType == null ? "form" : CurrentPage.DocumentType.ToString();

// Umbraco content API - get the content type of the form

IContentService contentService = ApplicationContext.Current.Services.ContentService;

IContentTypeService contentTypeService = ApplicationContext.Current.Services.ContentTypeService;

IContentType dt = contentTypeService.GetContentType(doctype);

// get the properties of the document type

IEnumerable<PropertyType> propertyTypes = dt.CompositionPropertyTypes.OrderBy(x => x.SortOrder);


// loop through properties

foreach (PropertyType pt in propertyTypes)

{

...

}

If you have a need to group your form fields into fieldsets, then create tabs in your form document type and iterate through the PropertyGroups

IEnumerable<PropertyGroup> tabs = dt.PropertyGroups;

foreach (PropertyGroup pg in tabs)

{

foreach (PropertyType pt in pg.PropertyTypes.OrderBy(x => x.SortOrder))

{ ...

Property Types

The interesting properties of the property types are:

  • Id

  • Alias

  • Name

  • Mandatory

  • ValidationRegExp (we can use this for validating input)

  • PropertyEditorAlias (the data type of the property)

You can add almost all form input types. For dropdowns, checkbox and radiobutton lists, you can use a custom data type with added prevalues. Here's how the various data types translate:

Umbraco Data Type

HTML5 Input Control

Umbraco.DropDown, Umbraco.DropDownMultiple

<select …/>

Umbraco.RadioButtonList

<input type="radio" … />

Umbraco.CheckBoxList

<input type="checkbox" …/>

Umbraco.TextboxMultiple, Umbraco.TinyMCEv3

<textarea …/>

Umbraco.Integer

<input type="number" …/>

Umbraco.Date

<input type="date" …/>

Umbraco.DateTime

<input type="datetime-local" …/>

Other

<input type="text" …/>

Script logic

If it's a postback, we'll run some server-side validation checks against the Mandatory and ValidationRegExp properties, then if valid, we'll display the thank you message and show the data that we have received, looping through each property of the document type and displaying the corresponding form data.

If not a postback, we'll build the form by looping through the properties and outputting the appropriate input control for each. Note that, for dates and numbers, I'm using HTML5 input types like datetime-local, which are not fully supported by all browsers and will then display as text inputs.

We'll add some inline javascript to perform client-side validation checks again using the Mandatory and ValidationRegExp properties. See below for more info on the javascript validation process.

At the end we'll add a sum-based captcha and the submit button.

We should also protect against XSS (cross-site scripting) attacks. This is just one way and you can find out more here.

@Html.AntiForgeryToken()

Finally we use the Umbraco Content API to save the submitted data in the specified folder.

@helper SaveContentItem(IContentService contentService, IEnumerable<PropertyType> properties, string doctype, int folderId, string title)

{

var ct = contentService.CreateContent(doctype + " : " + title + " : " + DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"), folderId, doctype, 0);

foreach (PropertyType pt in properties)

{

var fieldName = pt.Alias + pt.Id;

var fieldValue = Request.Form[fieldName];

ct.SetValue(pt.Alias, fieldValue);

}

 contentService.Save(ct);

}

Putting the code on the page

Just put this in the template used by Page with a Form:

@Html.Partial("Form")

Javascript validation

Add jquery.validate.min.js (from http://jqueryvalidation.org/) to the project and to the the umbLayout template. In TXT, the scripts are loaded in the header.

@* added for forms validation *@

<scriptsrc="/js/jquery.validate.min.js"></script>

In your project, you may be loading your scripts at the end of your HTML, but either way the following script snippet is required in the header, in order to instantiate a variable that will be populated in the razor script.

<script>

var rules = "";

</script>

Now if you go back and add new properties to the form document type, they will appear automatically on the page. As well as text fields and text areas, you can add dropdowns, true/false for radio buttons, numeric and date fields. And there are all kinds of ways that you could improve this for your own usage:

  • CSS styles

  • Adding custom text for the submit button, or using the dictionary for multi-language support.

  • Send an email to the site admin to notify them of a form submission.

  • Send an email to the user thanking them for their input.

  • Provide a more sophisticated form layout.

  • Provide an export function to extract the data in say, CSV format.

Download and have a go yourself

You'll find the complete project on GitHub here:

https://github.com/Pixel-To-Code/Easy-Umbraco-Forms.git

Alternatively, download the exported example document types and script here:

umbraco-easy-forms.zip

Let me know if you use and improve it in any way. I welcome all feedback and suggestions. Thanks for reading.

Got thoughts on this? Then let us know!

Design, and we shall build

Fast, reliable, experienced ASP.Net Umbraco developers

Front-end, back-end, testing, deployment... Outsource your web development projects to the CMS experts.

Contact us

We can't wait to hear from you

More articles

Developing matters