/// <summary>
        /// Adds a new form section or updates an existing form section in the
        /// <paramref name="actionForm"/>. This also handles the creation of
        /// any new form attributes as needed.
        /// </summary>
        /// <param name="actionForm">The <see cref="WorkflowActionForm"/> being updated.</param>
        /// <param name="workflowType">The <see cref="WorkflowType"/> being updated.</param>
        /// <param name="attributeService">The database service that provides access to the attributes.</param>
        /// <param name="formAttributeService">The database service that provides access to the form attributes.</param>
        /// <param name="formSectionService">The database service that provides access to the form sections.</param>
        /// <param name="section">The section view model that will be used as the source of information.</param>
        /// <param name="nextAttributeOrder">The next attribute order number to use when creating a new attribute.</param>
        /// <returns>The <see cref="WorkflowActionFormSection"/> entity that was either updated or created.</returns>
        private static WorkflowActionFormSection AddOrUpdateFormSection(WorkflowActionForm actionForm, WorkflowType workflowType, AttributeService attributeService, WorkflowActionFormAttributeService formAttributeService, WorkflowActionFormSectionService formSectionService, FormSectionViewModel section, List <FormFieldViewModel> formFields, ref int nextAttributeOrder)
        {
            var formSection = actionForm.FormSections.FirstOrDefault(s => s.Guid == section.Guid);

            // If the section was not found then create a new one and add it
            // to the form.
            if (formSection == null)
            {
                formSection = new WorkflowActionFormSection
                {
                    Guid = section.Guid
                };
                actionForm.FormSections.Add(formSection);
                formSectionService.Add(formSection);
            }

            // Update the standard section properties from the view model.
            formSection.Description            = section.Description;
            formSection.Title                  = section.Title;
            formSection.ShowHeadingSeparator   = section.ShowHeadingSeparator;
            formSection.SectionTypeValueId     = Rock.Blocks.WorkFlow.FormBuilder.Utility.GetDefinedValueId(section.Type);
            formSection.SectionVisibilityRules = section.VisibilityRule?.FromViewModel(formFields);

            // Loop through all fields that need to be either added or updated.
            for (int fieldOrder = 0; fieldOrder < section.Fields.Count; fieldOrder++)
            {
                var field = section.Fields[fieldOrder];

                var formField = AddOrUpdateFormField(actionForm, workflowType, attributeService, formAttributeService, formSection, field, formFields, ref nextAttributeOrder);
                formField.Order = fieldOrder;
            }

            return(formSection);
        }
        /// <summary>
        /// Adds a new form field or updates an existing form field in the form.
        /// This will also handle creating the attribute if a new form field must
        /// be created.
        /// </summary>
        /// <param name="actionForm">The <see cref="WorkflowActionForm"/> being updated.</param>
        /// <param name="workflowType">The <see cref="WorkflowType"/> being updated.</param>
        /// <param name="attributeService">The database service that provides access to attributes.</param>
        /// <param name="formAttributeService">The database service that provides access to form attributes.</param>
        /// <param name="formSection">The <see cref="WorkflowActionFormSection"/> being updated.</param>
        /// <param name="field">The field view model that contains the source information.</param>
        /// <param name="nextAttributeOrder">The next attribute Order value to use when adding a new attribute.</param>
        /// <returns>The <see cref="WorkflowActionFormAttribute"/> that was either created or updated.</returns>
        private static WorkflowActionFormAttribute AddOrUpdateFormField(WorkflowActionForm actionForm, WorkflowType workflowType, AttributeService attributeService, WorkflowActionFormAttributeService formAttributeService, WorkflowActionFormSection formSection, FormFieldViewModel field, List <FormFieldViewModel> formFields, ref int nextAttributeOrder)
        {
            var fieldType = FieldTypeCache.Get(field.FieldTypeGuid);

            // If the field type or its C# component could not be found then
            // we abort with a hard error. We need it to convert data.
            if (fieldType == null || fieldType.Field == null)
            {
                throw new Exception($"Field type '{field.FieldTypeGuid}' not found.");
            }

            var formField = actionForm.FormAttributes.FirstOrDefault(a => a.Attribute.Guid == field.Guid);

            // If the form field was not found then create a new attribute and
            // new form field.
            if (formField == null)
            {
                var attribute = new Rock.Model.Attribute
                {
                    Guid         = field.Guid,
                    EntityTypeId = EntityTypeCache.Get <Rock.Model.Workflow>().Id,
                    EntityTypeQualifierColumn = nameof(Rock.Model.Workflow.WorkflowTypeId),
                    EntityTypeQualifierValue  = workflowType.Id.ToString(),
                    FieldTypeId = fieldType.Id,
                    IsSystem    = true,
                    Order       = nextAttributeOrder++
                };

                formField = new WorkflowActionFormAttribute
                {
                    Attribute = attribute
                };

                actionForm.FormAttributes.Add(formField);
                attributeService.Add(attribute);
                formAttributeService.Add(formField);
            }

            // Convert the attribute configuration into values that can be stored
            // in the database.
            var configurationValues = fieldType.Field.GetPrivateConfigurationValues(field.ConfigurationValues);

            // Update all the standard properties.
            formField.ActionFormSection      = formSection;
            formField.Attribute.DefaultValue = fieldType.Field.GetPrivateEditValue(field.DefaultValue, configurationValues);
            formField.Attribute.Description  = field.Description;
            formField.Attribute.IsRequired   = field.IsRequired;
            formField.IsRequired             = field.IsRequired;
            formField.Attribute.IsGridColumn = field.IsShowOnGrid;
            formField.Attribute.Key          = field.Key;
            formField.Attribute.Name         = field.Name;
            formField.ColumnSize             = field.Size;
            formField.IsVisible            = true;
            formField.HideLabel            = field.IsHideLabel;
            formField.FieldVisibilityRules = field.VisibilityRule?.FromViewModel(formFields);

            // Add or update the attribute qualifiers. Do not delete any old ones.
            foreach (var kvp in configurationValues)
            {
                var qualifier = formField.Attribute.AttributeQualifiers.FirstOrDefault(q => q.Key == kvp.Key);

                if (qualifier == null)
                {
                    formField.Attribute.AttributeQualifiers.Add(new AttributeQualifier
                    {
                        IsSystem = false,
                        Key      = kvp.Key,
                        Value    = kvp.Value ?? string.Empty
                    });
                }
                else
                {
                    qualifier.Value = kvp.Value ?? string.Empty;
                }
            }

            return(formField);
        }