Tips for Forms Implementations

> And other pro tips
Cover Image for Tips for Forms Implementations

Sitecore Forms History

Forms have always been a hot topic, both on the web in general, and in Sitecore. As of Sitecore version 9, Sitecore natively shipped with new Forms functionality as a successor to Web Forms for Marketers. Everyone was eager to use the new functionality, but quickly ran into limitations. For a while, Sitecore Forms Extensions, a custom module, did a great job of filling in many of the missing pieces. Over the years, the native Sitecore Forms has improved, and is now a solid OOB option.

However, with the advent of XM Cloud, we're at the new frontier of forms again, because Sitecore Forms aren't supported in XMC. I believe we are going to see all kinds of community driven solutions pop up, as well as the adoption of dedicated third party solutions.

Over the years I have noticed quality issues with regards to both custom and OOB Sitecore Forms implementations. This post will help you provide more realistic estimations and additional client satisfaction regarding forms projects.

Note: this post is intentionally extreme and is quite paranoid and thorough in order to help you consider all possible issues. You can (and probably will) take a more relaxed approach, but I recommend that you at least consider everything mentioned here.

Take Forms Implementations Seriously

Ponder this:

Forms implementations are often under-budgeted and complexity is often underestimated. Further, clients often underestimate the importance of forms, and the negative impact that a poor form implementation form can have on user experience, and, more importantly, on their brand reputation.

As an implementation partner, it is your responsibility to understand the intricacies of forms and to educate the clients on why they should allocate sufficient budget to ensure their forms are bulletproof.

Any time that you have custom code as part of a form submission, it is important to consider all scenarios and edge cases. The goal is to prevent any of the following:

  • Silent form submission failures (user is not made aware of the failure)
  • Non-silent form submission failures that result in a poor user experience on the front end
  • Fatal infrastructure errors that go unnoticed (which is common when your forms integrate "external" systems such as SMTP and XConnect)
  • Inability for content authors to preview and test forms

XConnect + Forms Integrations and Submit Actions

One example of when forms implementations can go wrong is when you are integrating with XConnect. Consider this simplified case in which you have a custom submit action that identifies users based on their form data:

IdentifyContact.cs

_29
public class IdentifyContact : SubmitActionBase<IdentifyContactActionData>
_29
{
_29
public IdentifyContact(ISubmitActionData submitActionData) : base(submitActionData){}
_29
_29
protected override bool Execute(IdentifyContactActionData data, FormSubmitContext formSubmitContext)
_29
{
_29
IViewModel emailField = getFieldById(data.EmailFieldId, formSubmitContext.Fields);
_29
_29
using (XConnectClient client = SitecoreXConnectClientConfiguration.GetClient())
_29
{
_29
string email = getFieldValue(emailField);
_29
_29
var identifier = new IdentifiedContactReference("website", email.ToLowerInvariant());
_29
var expandOptions = new ContactExpandOptions(CollectionModel.FacetKeys.EmailAddressList);
_29
Contact contact = client.Get(identifier, expandOptions);
_29
_29
if(contact == null)
_29
{
_29
// create contact
_29
}
_29
else
_29
{
_29
// update contact
_29
}
_29
_29
return true;
_29
}
_29
}
_29
}

The call to this can throw exceptions:


_1
using (XConnectClient client = SitecoreXConnectClientConfiguration.GetClient())

This can happen when the xconnect instance is unreachable, or when there are SSL certificate issues, which happens more commonly than you might think.

Example errors:


_1
Sitecore.XConnect.XdbCollectionUnavailableException:'The HTTP response was not successful: NotFound'


_1
Sitecore.XConnect.XdbCollectionUnavailableException:'The HTTP response was not successful: Forbidden

Not wrapping this code block in a try-catch (or not handling the error elsewhere) can cause form submissions to fail entirely, resulting in unexpected states on the front end. When these errors occur, handle them and consider setting up real time alerts, as this is a critical issue.

Built-in submit actions in Sitecore Forms (that I have seen) all perform a check to see if there was an error with the submission, or an error in the previous submit action. If there is an error, they will not execute. This is a good practice to follow, and you should consider doing the same in your custom submit actions, while keeping implications in mind. Also, decide which custom submit actions are critical or not.

Testing Forms on CM

Did you know that XConnect is effectively disabled when you are logged into Sitecore? This is done to prevent testing data from polluting the real analytics data. Are you accounting for this scenario if a content author wants to test a form in preview mode?


_78
using System.Collections.Generic;
_78
using System.Linq;
_78
using Sitecore.Analytics;
_78
using Sitecore.Diagnostics;
_78
using Sitecore.ExperienceForms.Models;
_78
using Sitecore.ExperienceForms.Processing;
_78
using Sitecore.ExperienceForms.Processing.Actions;
_78
using Sitecore.Marketing.Definitions.Profiles;
_78
using Sitecore.XConnect;
_78
using Sitecore.XConnect.Client;
_78
using Sitecore.XConnect.Client.Configuration;
_78
using Sitecore.XConnect.Collection.Model;
_78
_78
public class IdentifyContact : SubmitActionBase<IdentifyContactActionData>
_78
{
_78
public IdentifyContact(ISubmitActionData submitActionData) : base(submitActionData){}
_78
_78
protected override bool Execute(IdentifyContactActionData data, FormSubmitContext formSubmitContext)
_78
{
_78
// Allow form submission to succeed if the form is being previewed by content authors
_78
// This check is required because in Preview mode on the CM instance, the contact will never resolve; i.e. IXdbContext.Get() always returns null
_78
if (isContentAuthorTestingFormSubmission())
_78
{
_78
return true;
_78
}
_78
_78
...
_78
_78
try {
_78
_78
using (XConnectClient client = SitecoreXConnectClientConfiguration.GetClient())
_78
{
_78
var identifier = new IdentifiedContactReference("website", email.ToLowerInvariant());
_78
var expandOptions = new ContactExpandOptions(
_78
CollectionModel.FacetKeys.PersonalInformation,
_78
CollectionModel.FacetKeys.EmailAddressList,
_78
CollectionModel.FacetKeys.ContactBehaviorProfile);
_78
_78
// client.Get is always null when logged into Sitecore, so there is potential for this to error out if subsequent calls are made on contact
_78
Contact contact = client.Get(identifier, expandOptions);
_78
}
_78
}
_78
catch (Sitecore.XConnect.XdbCollectionUnavailableException ex)
_78
{
_78
// This occurs when the XConnect site is unavailable or if there are certificate issues
_78
_78
// If there is an error here, how will we be notified?
_78
_78
Logger.LogError(ex.Message, ex);
_78
_78
return false;
_78
}
_78
catch (Exception ex)
_78
{
_78
// If there is an error here, how will we be notified?
_78
_78
Logger.LogError(ex.Message, ex);
_78
return false;
_78
}
_78
_78
...
_78
_78
}
_78
_78
private bool isContentAuthorTestingFormSubmission()
_78
{
_78
// Allow form submissions to proceed if the form is being previewed by content authors
_78
bool isLoggedIn = Sitecore.Context.IsLoggedIn;
_78
string serverRole = System.Configuration.ConfigurationManager.AppSettings["role:define"];
_78
bool isStandaloneOrContentManagementEnvironment = serverRole == "ContentManagenent" || serverRole == "Standalone";
_78
if (isLoggedIn && isStandaloneOrContentManagementEnvironment)
_78
{
_78
return true;
_78
}
_78
_78
return false;
_78
}
_78
}

You can also test on CM with Explore mode in Experience Editor.

Exception Alerting

Make use of tools such as Sentry and Azure Alerts to be notified when exceptions occur. This is a critical part of any production application, and should be a top priority.

File Attachments

File attachments are a common feature in forms. However, they are often not implemented correctly. What happens when a user goes to upload a file and then hits cancel? What happens if the user wants to delete a file? What happens if the user uploads a file that is too large, or uploads an invalid file extension?

  • Number of files
  • File saving location (public? private?)
  • File size limits
  • File type limits
  • File name limits
  • File name sanitization / OS friendliness / path length limits
  • File name uniqueness
  • Max POST size
  • Cleanup process
  • Disk space monitoring
  • Packet loss (the larger the size, the higher the chance). The only way to address this is to have a progressive upload before the form submission (think Gmail file attachments). This will require a good amount of code and can only be done if the page is only accessible by identified users.
  • Single POST vs multi POST

Form Validation

This goes without saying: implement both client and server side validation and validate the hell out of your forms.

Pro tip: using Sitecore Forms, understand that it is quite easy for content authors to forget to set max length validation on text fields.

Multi Page Forms

Consider splitting large forms up into multiple pages for a better user experience. In Sitecore Forms, you can specify an additional form page to be used as a success page (via form submit action).

Implement Captchas From Day One

Clients often don't realize how crucial these are until they start getting bombarded with SPAM. Honeypots aren't good enough (though they are recommended). Implement the Captcha.

Honey Pots

Honey pot implementations need to be implemented correctly:

  • Need to have accessibility metadata to prevent real users from filling out the field
  • Check on value should be a check on key
  • Failure should not be reported back to the end user

General Error Handling

What happens when your form fails to send an email? What happens when your form fails to save to the database? What happens when your form fails to save to xConnect? What happens when your form fails to save the data?

Are you being alerted? Is the end user made aware of an issue having occurred? Is it clear to them that they should try again? Is it clear to them that they should contact you another way? Is there a way for them to save their form progress?

Submission Confirmation

When a user submits a form, it should be very clear that it was received. Often times, success messages appear as single lines of grey colored text below the submit button. Bad UX.

Consider emailing the submitter their own submission data. This is a great way to ensure that they have a copy of their submission, and that they can easily refer back to it in the future. It also lets them know that their submission was successful. However, this does add more complexity.

Logging & Writing to DB

When logging form submissions, be careful not to log any sensitive data. Printing user info in the logs can be a privacy risk, depending on your situation. Also, logging is not a silver bullet. Logging can fail.

Writing to databases is also not impervious to issues. Consider worst case scenarios or edge cases such as unexpected increases in latency.

Long Forms and User Sessions

Here's a ghastly scenario: imagine that users must log into your site. On one of the pages, there is an important and long/comprehensive form. This form communicates information that is highly important.

Perhaps it's a form where you want to report professional misconduct, where you must provide lengthy explanations and extensive supporting documentation. The form takes some time to fill out. Long enough, even, that users may pause partway through and come back to finish it later.

Ask yourself: what happens when their session expires while they are away?

Email Addresses

If you are sending any kind of email as a submit action, or even collecting emails purely for informational purposes, consider this:

  • Strict email validation. Validating emails via code (regular expressions) is impossible as it requires multiple layers of checks and constant adjustments as new specifications are introduced. This is why most things that rely on email require you to validate your email via an actual email send step. The only way to prevent user errors with certainty is to validate their email before the user submits a form. This will require one of the following:
    1. User registration and sending verification emails
    2. Use social login to verify email
  • Bad email addresses (resulting in SMTP bounce)
  • Blacklists
  • SPAM filters

Other Considerations

  • Network availability / speed / packet loss
  • Browser compatibility
  • Firewall rules. You may want increased security posture on pages containing forms (remember to add exceptions for well known crawlers as as googlebot) and certainly the POST routes themselves.
  • XSS (can be triggered by what users enter, even if they are not malicious)
  • DDOS protection
  • Disaster recovery -- what happens if your server goes down? What happens if your database goes down? What happens if your email server goes down? What happens if your XConnect server goes down?

Conclusion

This is all about ensuring you and your client understand the importance and complexity of forms, and to adjust budgets accordingly.

Now, go forth and build forms that are slightly less bad than the other ones out there!

-MG


More Stories

Cover Image for Ideas For Docker up.ps1 Scripts

Ideas For Docker up.ps1 Scripts

> Because Docker can be brittle

Cover Image for Sitecore Symposium 2022

Sitecore Symposium 2022

> What I'm Watching 👀

Cover Image for JSS + TypeScript Sitecore Project Tips

JSS + TypeScript Sitecore Project Tips

> New tech, new challenges

Cover Image for Script: Boost SIF Certificate Expiry Days

Script: Boost SIF Certificate Expiry Days

> One simple script that definitely won't delete your system32 folder

Cover Image for NextJS: Unable to Verify the First Certificate

NextJS: Unable to Verify the First Certificate

> UNABLE_TO_VERIFY_LEAF_SIGNATURE

Cover Image for Early Returns in React Components

Early Returns in React Components

> When and how should you return early in a React component?

Cover Image for JSS: Reducing Bloat in Multilist Field Serialization

JSS: Reducing Bloat in Multilist Field Serialization

> Because: performance, security, and error-avoidance

Cover Image for Content Editor Search Bar Not Working

Content Editor Search Bar Not Working

> Sometimes it works, sometimes not

Cover Image for Year in Review: 2022

Year in Review: 2022

> Full steam ahead

Cover Image for Don't Ignore the HttpRequestValidationException

Don't Ignore the HttpRequestValidationException

> Doing so could be... potentially dangerous

Cover Image for Tips for New Sitecore Developers

Tips for New Sitecore Developers

> Because learning Sitecore can be hard

Cover Image for NextJS: Access has been blocked by CORS policy

NextJS: Access has been blocked by CORS policy

> CORS is almost as much of a nuisance as GDPR popups

Cover Image for Add TypeScript Type Checks to RouteData fields

Add TypeScript Type Checks to RouteData fields

> Inspired by error: Conversion of type may be a mistake because neither type sufficiently overlaps with the other.

Cover Image for Hello World

Hello World

> Welcome to the show

Cover Image for Tips for Applying Cumulative Sitecore XM/XP Patches and Hotfixes

Tips for Applying Cumulative Sitecore XM/XP Patches and Hotfixes

> It's probably time to overhaul your processes

Cover Image for On Mentorship and Community Contributions

On Mentorship and Community Contributions

> Reflections and what I learned as an MVP mentor

Cover Image for NextJS/JSS Edit Frames Before JSS v21.1.0

NextJS/JSS Edit Frames Before JSS v21.1.0

> It is possible. We have the technology.

Cover Image for Super Fast Project Builds with Visual Studio Publish

Super Fast Project Builds with Visual Studio Publish

> For when solution builds take too long

Cover Image for NextJS: Short URL for Viewing Layout Service Response

NextJS: Short URL for Viewing Layout Service Response

> Because the default URL is 2long4me

Cover Image for Azure PaaS Cache Optimization

Azure PaaS Cache Optimization

> App Services benefit greatly from proper configuration

Cover Image for Symposium 2022 Reflections

Symposium 2022 Reflections

> Sitecore is making big changes

Cover Image for On Sitecore Stack Exchange (SSE)

On Sitecore Stack Exchange (SSE)

> What I've learned, what I see, what I want to see

Cover Image for How to Run Old Versions of Solr in a Docker Container

How to Run Old Versions of Solr in a Docker Container

> Please don't make me install another version of Solr on my local...

Cover Image for SPE Script Performance & Troubleshooting

SPE Script Performance & Troubleshooting

> Script never ends or runs too slow? Get in here.