Showing posts with label Forms. Show all posts
Showing posts with label Forms. Show all posts

Wednesday, November 12, 2025

Sitecore Forms and the Content Security Policy

Sitecore Forms and the Content-Security-Policy (CSP)

The situation

A site is using Sitecore XM/XP and Sitecore Forms and has implemented a Content Security Policy (CSP) header - which is a best practice. However, Sitecore Forms apparently does not really like these policies as they do tend to block stuff. We had a (rather big) content site that had made edits to their CSP - which is manageable in Sitecore so they can adapt it per site - based on a recent penetration test. After this extension (tightening) of the CSP we noticed the forms from Sitecore Forms were not working anymore. To be more precise: the form appeared fine, but the submit action was not working anymore when it included a "Redirect to Page" action. 

The error

Luckily the error was pretty clear in a browser console:
CSP error

The CSP header was blocking the script. 
The error mentions "Executing inline script violates the following Content Security Policy directive 'script-src ... Either the 'unsafe-inline' keyword, a hash ('sha256-Dcwc6bB3ob8DnpIRKtqhRwu0Wl6bkf7uLnQFk3g6bPQ='), or a nonce ('nonce-...') is required to enable inline execution. The action has been blocked."

The solution

As the code which outputs the inline script is in the Sitecore assemblies, it does not seem an option to add a nonce value. Adding unsafe-inline everywhere is also not a good option as that would lower the quality of the CSP dramatically. So we went for another option and tried to add this unsafe-inline only when there is a form on the page.

Adding unsafe-inline conditionally 

First of all we will add an indicator in the HttpContext to tell us whether there is a form on the page. This can be done in Form.cshtml (located in \Views\FormBuilder), which is the main cshtml file of Sitecore Forms. But as we are using SXA it can also be done in the SXA Forms wrapper. This is Sitecore Form Wrapper.cshtml (located in \Views\Shared) and as we already had some customization in this file to add translations (see my previous post on this topic) we added a few lines here:
var context = HttpContext.Current;
context.Items["WeHaveAForm"] = "Y";
You can name the context item whatever you want of course.

Now we need to act on this context item. Again, there are options. We already had some code that placed a CSP in the header based on a value set in Sitecore on the Site item. But if you do not, a generic solution would be to place it in the global_asax Application_EndRequest function.
var context = HttpContext.Current?.Items["WeHaveAForm"];
if (context != null && context.Equals("Y"))
{
    var csp = Response.Headers["Content-Security-Policy"];
    if (string.IsNullOrEmpty(csp))
    {
        return;
    }

    csp = csp.Replace("script-src", "script-src 'unsafe-inline'");
    var pattern = @"'nonce-[^']+'";
    csp = Regex.Replace(csp, pattern, string.Empty);
    Response.Headers.Set("Content-Security-Policy", csp);
}
As you can see we do just a little bit more here:
  1. We check if the item is present in the context and get out if it is not
  2. We check if we have a csp value - if not we don't need to do anything so we get out
  3. We add the 'unsafe-inline' part to the script-src, if it is present in the csp
  4. We remove the complete nonce if that is present
  5. We set the new value in the Content-Security-Policy header

It is important to also remove the nonce. When a CSP header includes both a nonce and unsafe-inline, the browser ignores the unsafe-inline for scripts or styles and uses the nonce to allow specific inline elements. So if we keep the nonce, the unsafe-inline addition will not do anything.

Conclusion

We fixed the redirects on the forms without adding unsafe-inline on all pages. I would assume that is the best solution we could find here. 

Tuesday, April 16, 2019

Sitecore 9 Forms : translating client-side error messages

Sitecore 9 Forms : translating error messages client-side

A while ago a wrote a post on translating error messages in Sitecore Forms. In that post I mentioned that the validation done by jquery on the client-side was out of scope (of that post).

When a question on the subject appeared on Sitecore Stack Exchange, I started investigating. With a little help of Support the outcome was adding some custom scripts (and files). A better solution might be added in a future release but for now (9.1.1 and earlier) we'll have to do it like this. Note that my answer on SSE is a general one - this post addresses fixing it in a SXA environment.

First a small recap:
The messages entered here will be used for client- and server-side error messages. Which is nice. Just be aware that somewhere also jquery validation gets used and that can lead to strange situations. I noticed that the email validation can give two different error messages depending on what the error is.. and one of them is not translated as it comes from jquery. 
This is what  I wrote on the translated error messages in my first post. So let's start translating that email validation!

The issue can be easily reproduced with a translated form (non-English). Start typing in an email field and you will get the English jquery message. Once you have something like "...@x"  the jquery message will disappear and the Sitecore one will appear (which can be translated).

Adding the localized scripts

  1. Download all the jquery-validation localized messages files you need from https://github.com/jquery-validation/jquery-validation/tree/master/src/localization
  2. Place those files in a \sitecore modules\Web\ExperienceForms\scripts\localization folder. 
  3. You can change the translation in the files as needed - their structure is very straight forward
  4. Open Sitecore Form Wrapper.cshtml in the \Views\Shared folder 
  5. Add a script just after the @Html.RenderFormScripts():  (adapt the script for the languages you have in your environment)
<script type="text/javascript">
var lang = '@Sitecore.Context.Language.Name';
var script = document.createElement("script");
if (lang == 'nl')
{
    script.src = "/sitecore%20modules/Web/ExperienceForms/scripts/localization/messages_nl.js";
    $("head").append(script);
}
else if (lang == 'fr')
{
    script.src = "/sitecore%20modules/Web/ExperienceForms/scripts/localization/messages_fr.js";
    $("head").append(script);
}
...
</script>

Don't forget to add the added/changed files to your solution. And be aware when upgrading that you changed a Sitecore file...

This should do it. The messages are not translatable by the editors, but at least they are not English everywhere...

Alternatives?

It will also work if you put the messages javascript files in the scripts folder of Forms (\sitecore modules\Web\ExperienceForms\scripts) and add the name of the messages file to the Scripts field in the form (where the other jquery js files already are). Problem with this solution is that the Scripts field is shared - meaning it is the same for all languages. So, you can add one language and that will be used for all languages (including English). Unless you have a single language site that is non-English, this is not what you want.

Monday, October 29, 2018

Sitecore 9.1 Forms - Prefilling fields & security

Sitecore 9.1 Forms

The Forms feature has 2 major new features in version 9.1 of the Sitecore platform. You'll get conditional fields and prefilling of fields. It's about that last one I wanted to give you a little bit of advice...

Prefilling fields

Without going into too much details (Sitecore 9.1 is only released in tech preview at the time of writing - release version will be available soon) the prefilling is based on value providers - classes that will get the values based on a parameter defined with the field. 

Prefilling is a nice feature, as it makes the user experience better and the conversion rate of the forms higher - visitors are lazy ;)

But.. before you jump into prefilling everything you think you know about me, read this article: https://www.campaignion.org/blog/all-you-have-know-about-pre-filling-forms

It is a good overview of what you can an can't do when prefilling forms. And there is one option related to Sitecore that I want to address.

Prefilling xConnext data

Sitecore environments usually have (a lot of) information about their visitors in the xDB database. It is very tempting to use that data to prefill the forms. But there might be catch..  

Consider the following scenario (which is not so far fetched) on a website:
  • a form with just an e-mail address to subscribe to the newsletter
  • a contact form (with email, name, address, ...)
  • all forms will identify the contact
  • all forms use prefilling on fields where possible (all ootb xXonnect fields)

So, what is the catch? Well, if I subscribe you (yes, you.. not me) to the newsletter and the system already knows you it will attach my session to your profile. No harm done, until I visit the contact form and see all your contact data. 

How do you prevent this? You probably want to keep the contact identifcation as that gives your marketers the information they want in the xConnect reports. Adding a Captcha to your form will help a bit (and this might be a good idea for other reasons as well). But actually, the only good solution is -as described in the mentioned post above- to only prefill data from a datastore (like xDB) when your user is really identified - meaning logged in or truly identified through an identifier in the querystring. 

Conclusion

Prefilling is a nice new feature (and it looks better than the WFFM version). And I hope the community will start sharing value providers. But be aware of what you do with your visitors data.. after all, you don't want to end up in a next version of Mikkel Rømer "white hat hackers" session.

Monday, January 22, 2018

Sitecore Forms Send Email Campaign message

Sitecore Forms 9.0 Update-1 rev. 171219

Sitecore release it's Update-1 version of the platform. In this version they included EXM (Email Experience Manager) as out-of-the-box part of the product. No more separate module..  like they did with Forms before. This change also had some (expected) changes to the Forms module. In the initial release version there was no submit action that could send an email. We expected this to be introduced together with EXM and that was a correct assumption.



So.. we have a "Send Email Campaign message" submit action now.

Send Email Campaign message submit action


I tried to do a quick test on a vanilla install of the platform:
  1. Create an automated campaign in EXM (how-to)
  2. Test the campaign - yes, the test mail arrived perfectly.
  3. Create a form (any form will do), added a few fields and all wanted save actions, amongst which the "Send Email Campaign Message" - place it before the redirect ;)
    The save action will allow you to select a campaign - my newly created campaign was in the list so I selected that one.
  4. Add the form to a page (we had to create/generate an mvc layout for this*)
  5. Publish everything and let's try this...
Submit the form with some test data.. and.. damn..  "Failed to send email!" 
In the Sitecore logs I found more information:  ERROR Contact id is null.

As I am not that familiar with EXM (yet), as probably quite a lot of others, I was not aware that I could only send a mail to a known contact. Sitecore Support helped me on this one, so now I figured out that I do need to identify my contact first.
To send an email with the "Send Email Campaign Message" action, your contact needs to be identified.
Sound reasonable, but the (first) problem is that there is no out-of-the-box submit action to do this. Luckily all you need to do this yourself has been documented on the official doc site.  (still weird that they can document it, but not put it in the product...).

Documentation on the submit action could have saved me some time, so hopefully this small post will help someone.

Further usage

We did not find a way to send the form data in the message (without customizing the save action) - unless the form data is all in the contact data, which probably is not the case.

We also haven't found a solution to send the email to someone else - not the person who submitted the form. The mail will be send to the identified contact.

Conclusion

I must admit I was hoping Update-1 would have more impact on the Forms part of the product. I was also hoping the "Send Email" functionality would be in there. One could say it is, but without custom code is useless. Let's get our hopes up for Update-2...

Questions?

For questions on the topic, please find me (and many other Sitecore folks) on Sitecore Slack or Stack Exchange.

Is your custom automated campaign is not showing in the Send Email Campaign Message Action? See https://sitecore.stackexchange.com/a/11511/237


* Sitecore Forms is MVC only - and the vanilla setup of Sitecore still comes with a default WebForms homepage 😞

Wednesday, October 18, 2017

Sitecore 9 Forms : translating error messages

Sitecore 9 Experience Forms

Sitecore released a successor for Web Forms for Marketers (WFFM) with the new Sitecore 9 version, called Sitecore Forms.

In a multilingual environment you want to translate your forms, which is easy as the form editor itself has a language switch.  But what about the error messages - those things a visitor gets when (s)he fills in the form not as expected...

Translating error messages

Find the validators in the Sitecore Content Editor: /sitecore/system/Settings/Forms/Validations. 

Each validator has a Message field. Create versions of the validator item in all the languages needed and fill (or update) the message field as desired. Just check that the message is valid: the parameters in the text need to match the original value. As in the example (image) for email there is "{0}". A developer will recognize this syntax - all translations do need this token as it will be replaced by the field name when displaying the error.

Client-side messages

The messages entered here will be used for client- and server-side error messages. Which is nice. Just be aware that somewhere also jquery validation gets used and that can lead to strange situations. I noticed that the email validation can give two different error messages depending on what the error is.. and one of them is not translated as it comes from jquery. 

Translating the jquery validation is out of scope here as that is documented elsewhere and is not related to the Sitecore solution. Update: https://ggullentops.blogspot.com/2019/04/sitecore-9-forms-translating-client-error.html ;)

Required field

You might notice that the most used validation - required field - is not in the list. That is because this is a special case.. also on a Forms field, you would not select a required validator, but just tick the required checkbox. 

The code for the required field validation works in a different way and the message will come from the Sitecore dictionary. If you want to update or translate it, you need to add a key to that Dictionary. This can be done in the master database - just add an item in the /system/Dictionary - the name doesn't really matter. The key however needs to be exactly "{0} is required.". The phrase can be anything you want, just remember to add the {0} token. 

Create versions and translate in all languages needed - and don't forget to publish.

Update - password/email confirmation fields

As new fields got added in newer versions (9.3) - some new translation requirements were found. AS asked in the comments, the password & email confirmation fields seem to be special cases as well. How to translate them was mentioned on Sitecore StackExchange (https://sitecore.stackexchange.com/q/24129/237): just add "The {0} and {1} do not match." to the dictionary as well.

Sitecore 9 Forms: custom submit action

Sitecore 9 Experience Forms

Sitecore released a successor for Web Forms for Marketers (WFFM) with the new Sitecore 9 version, called Sitecore Forms.

Submit actions - save actions

Sitecore Forms comes with a few submit actions (or save actions as we used to call them, or how they are still called on the submit button) out of the box like Trigger Goal, Redirect to Page, Save Data ... but is also missing a few and developers will probably -just as with wffm- need to make custom ones.

Custom redirect

I wrote a post on Sitecore 8 about customizing the redirect url. In Sitecore 9 with Forms, that article is no longer valid as we should use a different approach and use a custom submit action. So lets try to do that: create a submit action that redirects to a page and adds the value of a field (e.g. email) to the querystring to be processed on the results page.
Could become useful as there is no ootb option for a success message anymore.


The code

Let's go straight to the good stuff. We'll create a class derived from SubmitActionBase<>  and as we want to create redirect functionality we can reuse the ActionData class from the existing redirect so it becomes derived from SubmitActionBase<RedirectActionData>.

public class CustomRedirectAction : SubmitActionBase<RedirectActionData>
{
  public CustomRedirectAction(ISubmitActionData submitActionData) : base(submitActionData)
  {
  }

  protected override bool Execute(RedirectActionData data, FormSubmitContext formSubmitContext)
  {
    Assert.ArgumentNotNull(formSubmitContext, "formSubmitContext");
    if (data == null || !(data.ReferenceId != Guid.Empty))
        return false;
    var item = Sitecore.Context.Database.GetItem(new ID(data.ReferenceId));
    if (item == null)
        return false;

    var email = string.Empty;
    var postedFormData = formSubmitContext.PostedFormData;
    var field = postedFormData.Fields.FirstOrDefault(f => f.Name.Equals("Email"));
    if (field != null)
    {
        var property = field.GetType().GetProperty("Value");
        var postedEmail = property.GetValue(field);
        email= postedEmail.ToStringOrEmpty();
    }
            
    var defaultUrlOptions = LinkManager.GetDefaultUrlOptions();
    defaultUrlOptions.SiteResolving = Settings.Rendering.SiteResolving;
    formSubmitContext.RedirectUrl = new UrlString(LinkManager.GetItemUrl(item, defaultUrlOptions)) + "?email=" + email;
    formSubmitContext.RedirectOnSuccess = true;
    formSubmitContext.Abort();
    return true;
  }
}

We start by fetching the item to redirect to - which is found in the reference id. If this is not available, we get out.

Fetching data from the submit context

Next thing to do is get the value of the email field. That was a bit tricky and I'm not sure whether I used the best approach here - I did get this idea from the Save Data code so it won't be all bad :).

The good news is that we can use the item names (of the "field" item of the form) so we shouldn't have issues with translations. And it will work for other forms if they keep the name identical (the name is not the label shown on the form by the way). Finding the field is easy as they all are a property of the posted form data available in the form submit context. But getting the data from that field was not so trivial - but the "property" stuff works.

Finalizing

As we have all our data now, we van trigger the redirect. Based on the redirect item we can construct the base url - and then add the email to it in the querystring. 

FormSubmitContext

Once the final url is known, we can set it in the form submit context. We also set the redirect property to true and abort the context. Aborting the context will prevent other actions to be executed. Our redirect should be the last.

Configuring Sitecore Forms

Once we have our code, we need to configure Sitecore Forms to use it. The configuration is done in Sitecore.

Open a content editor and go to /sitecore/system/Settings/Forms/Submit Actions. Create a new item of type Submit Action. Fill in the Model Type (e.g. Forms.CustomRedirectAction) and an error message. 

Editor

The editor field will define how your editor can configure your action. In this case we want them to be able to select an item from the content tree. We could use the existing one from the ootb redirect action, but lets create a new one..

Move to the core database and locate /sitecore/client/Applications/FormsBuilder/Components/Layouts/Actions. You will see the existing editor options there (including the RedirectToPage which has a wrong and confusing display name -Trigger Goal- at the time of writing).  As these are all editor actions that will open a content tree at a certain location, the easiest way to accomplish our needs is copy an existing tree and update the items needed - which is actually just the ItemTreeView. The StaticData field defines the root item available for your editors. You might have an issue getting the correct data in here, as the droptree on this field will show you the core database. And you want data from the master database.. switching to raw values and entering the desired guid will do the trick!



Once this is set, we can go back to the master database and finalize our submit action by selecting our newly created editor option.


That's all, your submit action should be available on your forms in the "add" list of any submit button. 

And don't forget to publish ;) 






Sitecore 9 Forms : custom validation

Sitecore 9 Experience Forms

Sitecore released a successor for Web Forms for Marketers (WFFM) with the new Sitecore 9 version, called Sitecore Forms.

Validators

Sitecore Forms comes with a few validators out of the box like email, string length, ... and of course the required field but that one is kinda special.

What if you want to validate fields with your own custom business rules?  
Lets see how we can create a validator that checks the length of the string input (based on parameters) and combines that with a regular expression validation that all characters are numeric.

Custom validator

We start by creating a new class and derive from RegularExpressionValidation. This will give us some functionality concerning the regular expression validation.

We define our class properties that we'll need to setup the validation:
public override string RegularExpression => "^[0-9]+$";
private int MinLength { get; set; }
private int MaxLength { get; set; }
Three functions need to be overridden:
  • Initialize: initializes the validator based on the validationModel object
  • Validate : server side validation based on the value
  • GetClientValidationRules : registers the client side validation rules

Code

The resulting code will look like this:
public class CustomValidation : RegularExpressionValidation
{
  public override string RegularExpression => "^[0-9]+$";

  private int MinLength { get; set; }
  private int MaxLength { get; set; }

  public CustomValidation(ValidationDataModel validationItem) : base(validationItem)
  {
  }

  public override void Initialize(object validationModel)
  {
    base.Initialize(validationModel);
    var stringInputViewModel = validationModel as StringInputViewModel;
    if (stringInputViewModel == null) return;
    MinLength = stringInputViewModel.MinLength;
    MaxLength = stringInputViewModel.MaxLength;
    Title = stringInputViewModel.Title;
  }

  public override ValidationResult Validate(object value)
  {
    if (value == null) return ValidationResult.Success;
 
    var num = value.ToString().Length;
    if (num > 0 && (num < MinLength || num > MaxLength))
    {
      return new ValidationResult(FormatMessage(Title));
    }

    var regex = new Regex(RegularExpression, RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture | RegexOptions.Compiled);
    var input = (string)value;
    if (string.IsNullOrEmpty(input) || regex.IsMatch(input)) return ValidationResult.Success;
    return new ValidationResult(FormatMessage(Title));
  }

  public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
  {
    if (MaxLength > 0)
        yield return new ModelClientValidationStringLengthRule(FormatMessage(Title), MinLength, MaxLength);

    yield return new ModelClientValidationRegexRule(FormatMessage(Title), new Regex(RegularExpression, RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture | RegexOptions.Compiled).ToString());
  }
}

We compile the code and publish the dll to the website, where we will add the validator to the configuration in Sitecore.

Sitecore Forms configuration

Open the Content Editor and go to the Forms section in Settings: /sitecore/system/Settings/Forms/Validations. Create a new item of type Validation (or copy an existing and update the fields).

You need to fill in the type (e.g. "Forms.CustomValidation,Forms") and a message. The message has to contain "{0}" as this will be replaced with the title of the field in the code - so e.g. {0} is not a valid entry for this field.
Also, do not forget to create a version in every language of your site and translate the message.

Now we need to attach the validator to the field types in order to make it visible when selecting a validator. Go to the Field Types section and let's take the single line text field: /sitecore/system/Settings/Forms/Field Types/Basic/Single-Line Text. Find the Allowed Validations field and your custom validator to the selected ones.





That's it..  if you now go to a form and select a single-line text field, you will be able to select your newly created validator.


ps: don't forget to publish ;)