This might not provide the best user experience for authors to setup conditional emails / webhooks but this implementation definitely does not require a humongous effort to cater for the missing feature in the Episerver.Forms module.
Problem: At the time of writing, Episerver.Forms does not out-of-the-box provide content editors the ability to set up conditional emails / webhooks. For example, if a user has selected a specific value from a dropdown, only then should an email be sent. Same concept goes for webhooks.
Solution: Drag and drop a hidden field onto the form and enter an expression that the Forms Actors will later use to decide whether to send the email or not (or whether to post to a webhook or not)
Form authoring steps:
- Author the form using the Form Builder as usual
- Setup the email and/or webhook using the Form Builder. The default setting is for the form to send the email and/or post the form data to a webhook URL
- Drag and drop a hidden field and give it a name of “STOPEMAILIF”
- Give it a value of the format “{fieldName}:{valuesToMatchSeparatedByPipe}”. For example if you want the email to NOT be sent if the user selected a value of “Grade A” or “Grade C” from the dropdown field named “Grade”, then enter the value of “Grade:Grade A|Grade B”
- [Optional] Drag and drop another hidden field and give it a name of “STOPWEBHOOKIF” if you want to apply the same logic to webhooks. The value format should be the same as step 4.
Code:
- In Episerver, when a form is submitted, two types of ACTORs execute afterward:
- SendEmailAfterSubmissionActor
- WebhookAfterSubmissionActor
- The plan is to dynamically skip these actors if the STOPEMAILIF expression is satisfied. Same goes for the STOPWEBHOOKIF expression. The below implementation is all I needed in my case.
public class CustomActorsExecutingService : ActorsExecutingService
{
private readonly Injected _contentRepository;
public override IEnumerable GetFormSubmissionActors(Submission submission,
FormContainerBlock formContainer, FormIdentity formIden, HttpRequestBase request, HttpResponseBase response,
Guid permanentFormSubmissionId, bool isFormFinalizedSubmission)
{
var actors = base.GetFormSubmissionActors(submission, formContainer, formIden, request, response,
permanentFormSubmissionId, isFormFinalizedSubmission);
// check if form is setup with custom functionality
try
{
if (formContainer.ElementsArea != null && formContainer.ElementsArea.Items.Any())
{
var elems = formContainer.ElementsArea.Items.Select(i => _contentRepository.Service.Get(i.ContentLink)).ToList();
var hiddenElems = elems.OfType();
// Handle emails
var customElem = hiddenElems.FirstOrDefault(e => (e as IContent).Name == "STOPEMAILIF");
if (customElem != null)
{
var colonIndex = customElem.PredefinedValue.IndexOf(":");
var fieldName = customElem.PredefinedValue.Substring(0, colonIndex);
var valuesToMatch = customElem.PredefinedValue.Substring(colonIndex + 1)
.Split(new string[] {"|"}, StringSplitOptions.RemoveEmptyEntries);
var field = elems.FirstOrDefault(e => (e as IContent).Name == fieldName);
if (field != null)
{
var dataField = field as DataElementBlockBase;
if (dataField != null)
{
if (valuesToMatch.Contains(dataField.GetSubmittedValue()))
{
actors = actors.Where(a => !(a is SendEmailAfterSubmissionActor));
}
}
}
}
// Handle webhooks
customElem = hiddenElems.FirstOrDefault(e => (e as IContent).Name == "STOPWEBHOOKIF");
if (customElem != null)
{
var colonIndex = customElem.PredefinedValue.IndexOf(":");
var fieldName = customElem.PredefinedValue.Substring(0, colonIndex);
var valuesToMatch = customElem.PredefinedValue.Substring(colonIndex + 1)
.Split(new string[] { "|" }, StringSplitOptions.RemoveEmptyEntries);
var field = elems.FirstOrDefault(e => (e as IContent).Name == fieldName);
if (field != null)
{
var dataField = field as DataElementBlockBase;
if (dataField != null)
{
if (valuesToMatch.Contains(dataField.GetSubmittedValue()))
{
actors = actors.Where(a => !(a is CallWebhookAfterSubmissionActor));
}
}
}
}
}
}
catch (Exception ex) { }
return actors;
}
}
- Now to enable the custom implementation of the ActorsExecutingService, you need to configure your dependency injection container like the below. In my case this line of code went into my
DependencyResolverInitialization.cs
container.For<ActorsExecutingService>().Use<CustomActorsExecutingService>();
Your form will now run the custom logic above of dynamically skipping the Email/Webhook actors if your STOP expressions are satisfied.