description |
---|
How to amend the built-in behavior of adding fields and associating workflows with new forms |
By default, a single workflow is added when a new form is created. This workflow will send a copy of the form to the email address of the current backoffice user.
A single "data consent" field will also be added unless it has been disabled via configuration.
It's possible to amend this behavior and change it to fit your needs.
Two interfaces are used to abstract the logic for setting default fields and workflows for a form. They are IApplyDefaultFieldsBehavior
and IApplyDefaultWorkflowsBehavior
respectively.
The default behaviors are defined using built-in, internal classes that implement this interface.
You can create your own implementation of these interfaces.
An illustrative example, adding a custom workflow that writes to the log, is shown below.
Firstly, the custom workflow:
using System;
using System.Collections.Generic;
using Umbraco.Core.Composing;
using Umbraco.Core.Logging;
using Umbraco.Forms.Core.Attributes;
using Umbraco.Forms.Core.Enums;
using Umbraco.Forms.Core.Persistence.Dtos;
namespace MyNamespace
{
public class TestWorkflow : WorkflowType
{
public const string LogMessageWorkflowId = "7ca500a7-cb34-4a82-8ae9-2acac777382d";
private readonly ILogger<LogMessageWorkflow> _logger;
public LogMessageWorkflow(ILogger<LogMessageWorkflow> logger)
{
Id = new Guid(LogMessageWorkflowId);
Name = "Test Workflow";
Description = "A test workflow that writes a log line";
Icon = "icon-edit";
_logger = logger;
}
[Setting("Message", Description = "The log message to write", View = "TextField")]
public string Message { get; set; }
public override List<Exception> ValidateSettings()
{
var exs = new List<Exception>();
if (string.IsNullOrEmpty(Message))
{
exs.Add(new Exception("'Message' setting has not been set"));
}
return exs;
}
public override WorkflowExecutionStatus Execute(WorkflowExecutionContext context)
{
_logger.LogInformation($"'{Message}' written at {DateTime.Now}");
return WorkflowExecutionStatus.Completed;
}
}
}
Secondly, the custom implementation of IApplyDefaultWorkflowsBehavior
:
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Cms.Core.Hosting;
using Umbraco.Forms.Core;
using Umbraco.Forms.Core.Enums;
using Umbraco.Forms.Core.Providers;
using Umbraco.Forms.Web.Behaviors;
using Umbraco.Forms.Web.Models.Backoffice;
namespace MyNamespace
{
public class CustomApplyDefaultWorkflowsBehavior : IApplyDefaultWorkflowsBehavior
{
private readonly WorkflowCollection _workflowCollection;
private readonly IHostingEnvironment _hostingEnvironment;
public CustomApplyDefaultWorkflowsBehavior(
WorkflowCollection workflowCollection, IHostingEnvironment hostingEnvironment)
{
_workflowCollection = workflowCollection;
_hostingEnvironment = hostingEnvironment;
}
public void ApplyDefaultWorkflows(FormDesign form)
{
// Retrieve the type of the default workflow to add.
WorkflowType testWorkflowType = _workflowCollection[new Guid(LogMessageWorkflow.LogMessageWorkflowId)];
// Create a workflow object based on the workflow type.
var defaultWorkflow = new FormWorkflowWithTypeSettings
{
Id = Guid.Empty,
Name = "Log a message",
Active = true,
IncludeSensitiveData = IncludeSensitiveData.False,
SortOrder = 1,
WorkflowTypeId = testWorkflowType.Id,
WorkflowTypeName = testWorkflowType.Name,
WorkflowTypeDescription = testWorkflowType.Description,
WorkflowTypeGroup = testWorkflowType.Group,
WorkflowTypeIcon = testWorkflowType.Icon,
// Optionally set the default workflow to be mandatory (which means editors won't be able to remove it
// via the back-office user interface).
IsMandatory = true
};
// Retrieve the settings from the type.
Dictionary<string, Core.Attributes.Setting> workflowTypeSettings = testWorkflowType.Settings();
// Create a collection for the specific settings to be applied to the workflow.
// Populate with the setting details from the type.
var workflowSettings = new List<SettingWithValue>();
foreach (KeyValuePair<string, Core.Attributes.Setting> setting in workflowTypeSettings)
{
Core.Attributes.Setting settingItem = setting.Value;
var settingItemToAdd = new SettingWithValue
{
Name = settingItem.Name,
Alias = settingItem.Alias,
Description = settingItem.Description,
Prevalues = settingItem.GetPreValues(),
View = _hostingEnvironment.ToAbsolute(settingItem.GetSettingView()),
Value = string.Empty
};
workflowSettings.Add(settingItemToAdd);
}
// For each setting, provide a value for the workflow instance (in this example, we only have one).
SettingWithValue messageSetting = workflowSettings.SingleOrDefault(x => x.Alias == "Message");
if (messageSetting != null)
{
messageSetting.Value = "A test log message";
}
// Apply the settings to the workflow.
defaultWorkflow.Settings = workflowSettings;
// Associate the workflow with the appropriate form submission event.
form.FormWorkflows.OnSubmit.Add(defaultWorkflow);
}
}
}
Finally, to register the custom implementation in place of the default one:
using Umbraco.Cms.Core.Composing;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Extensions;
using Umbraco.Forms.Core.Providers;
using Umbraco.Forms.Testsite.Business.Workflows;
using Umbraco.Forms.Web.Behaviors;
namespace MyNamespace
{
public class TestSiteComposer : IComposer
{
public void Compose(IUmbracoBuilder builder)
{
builder.WithCollectionBuilder<WorkflowCollectionBuilder>()
.Add<LogMessageWorkflow>();
builder.Services.AddUnique<IApplyDefaultWorkflowsBehavior, CustomApplyDefaultWorkflowsBehavior>();
}
}
}
When adding a default workflow in code, it's possible to make it mandatory, which will prevent editors from removing it from a form.
You can see this in the example above, where the IsMandatory
property of the created FormWorkflowWithTypeSettings
instance is set to true
.
The following class shows the default implementation provided with Forms. You can copy this and customize it to your needs.
using Microsoft.Extensions.Options;
using Umbraco.Forms.Core.Configuration;
using Umbraco.Forms.Core.Models;
using Umbraco.Forms.Web.Extensions;
using Umbraco.Forms.Web.Models.Backoffice;
namespace Umbraco.Forms.Web.Behaviors
{
internal class CustomApplyDefaultFieldsBehavior : IApplyDefaultFieldsBehavior
{
private readonly FormDesignSettings _formDesignSettings;
public CustomApplyDefaultFieldsBehavior(IOptions<FormDesignSettings> formDesignSettings) =>
_formDesignSettings = formDesignSettings.Value;
public virtual void ApplyDefaultFields(FormDesign form)
{
// Add one page as a starting point.
var page = new Page();
form.Pages.Add(page);
// Add one empty fieldset to the page to start with.
var fieldset = new FieldSet
{
Id = Guid.NewGuid()
};
page.FieldSets.Add(fieldset);
// Add one full-width (12cols) container/row to the fieldset.
var container = new FieldsetContainer
{
Width = 12
};
fieldset.Containers.Add(container);
// As all forms default to having StoreRecordsLocally we need to add the data consent field to the the form
// (unless this feature has been explicitly disabled).
if (_formDesignSettings.DisableAutomaticAdditionOfDataConsentField)
{
return;
}
container.AddDataConsentField(_formDesignSettings, _fieldCollection);
// Add any further fields you require.
}
}
}
Again, you will need to register your custom class, for example, in a composer with:
builder.Services.AddUnique<IApplyDefaultFieldsBehavior, CustomApplyDefaultFieldsBehavior>();