Wednesday, October 18, 2017

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 ;) 






4 comments:

  1. Can you please tell us how you got PostedFormData in SubmitAction?

    ReplyDelete
  2. It's in the code example. Starting with "var postedFormData = formSubmitContext.PostedFormData;" and using GetProperty.

    This is one of the things that should be changed to make writing submit actions easier.

    ReplyDelete
  3. Hi Gert,
    Thanks for quick response. But, I'm not able to find PostedFormData Property in formSubmitContext.
    Namespace: Sitecore.ExperienceForms.Processing.FormSubmitContext
    Are you using having the same namespace?
    Thanks,
    Nikki

    ReplyDelete
    Replies
    1. They might have changed something in the release version. I think you can skip the PostedFormData and get to the Fields collection from the formSubmitContext: formSubmitContext.Fields (and continue from there).

      Delete