Beispiel #1
0
        /// <summary>
        /// Gets or sets the form.
        /// </summary>
        /// <value>
        /// The form.
        /// </value>
        public WorkflowActionForm GetForm()
        {
            EnsureChildControls();
            var form = new WorkflowActionForm();
            form.Guid = _hfFormGuid.Value.AsGuid();
            if ( form.Guid != Guid.Empty )
            {
                form.NotificationSystemEmailId = _ddlNotificationSystemEmail.SelectedValueAsId();
                form.IncludeActionsInNotification = _cbIncludeActions.Checked;
                form.Header = _ceHeaderText.Text;
                form.Footer = _ceFooterText.Text;
                form.Actions = _falActions.Value;

                foreach ( var row in AttributeRows )
                {
                    var formAttribute = new WorkflowActionFormAttribute();
                    formAttribute.Attribute = new Rock.Model.Attribute { Guid = row.AttributeGuid, Name = row.AttributeName };
                    formAttribute.Guid = row.Guid;
                    formAttribute.Order = row.Order;
                    formAttribute.IsVisible = row.IsVisible;
                    formAttribute.IsReadOnly = !row.IsEditable;
                    formAttribute.IsRequired = row.IsRequired;
                    form.FormAttributes.Add( formAttribute );
                }

                form.ActionAttributeGuid = _ddlActionAttribute.SelectedValueAsGuid();

                return form;
            }
            return null;
        }
        /// <summary>
        /// Gets or sets the form.
        /// </summary>
        /// <value>
        /// The form.
        /// </value>
        public WorkflowActionForm GetForm()
        {
            EnsureChildControls();
            var form = new WorkflowActionForm();

            form.Guid = _hfFormGuid.Value.AsGuid();
            if (form.Guid != Guid.Empty)
            {
                form.NotificationSystemEmailId    = _ddlNotificationSystemEmail.SelectedValueAsId();
                form.IncludeActionsInNotification = _cbIncludeActions.Checked;
                form.Header     = _ceHeaderText.Text;
                form.Footer     = _ceFooterText.Text;
                form.Actions    = _falActions.Value;
                form.AllowNotes = _cbAllowNotes.Checked;

                foreach (var row in AttributeRows)
                {
                    var formAttribute = new WorkflowActionFormAttribute();
                    formAttribute.Attribute = new Rock.Model.Attribute {
                        Guid = row.AttributeGuid, Name = row.AttributeName
                    };
                    formAttribute.Guid       = row.Guid;
                    formAttribute.Order      = row.Order;
                    formAttribute.IsVisible  = row.IsVisible;
                    formAttribute.IsReadOnly = !row.IsEditable;
                    formAttribute.IsRequired = row.IsRequired;
                    formAttribute.HideLabel  = row.HideLabel;
                    formAttribute.PreHtml    = row.PreHtml;
                    formAttribute.PostHtml   = row.PostHtml;
                    form.FormAttributes.Add(formAttribute);
                }

                form.ActionAttributeGuid = _ddlActionAttribute.SelectedValueAsGuid();

                return(form);
            }
            return(null);
        }
        /// <summary>
        /// Handles the Click event of the btnSave control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
        protected void btnSave_Click( object sender, EventArgs e )
        {
            var rockContext = new RockContext();

            WorkflowType workflowType;
            WorkflowTypeService service = new WorkflowTypeService( rockContext );

            int workflowTypeId = int.Parse( hfWorkflowTypeId.Value );

            if ( workflowTypeId == 0 )
            {
                workflowType = new WorkflowType();
                workflowType.IsSystem = false;
                workflowType.Name = string.Empty;
            }
            else
            {
                workflowType = service.Get( workflowTypeId );
            }

            workflowType.Name = tbName.Text;
            workflowType.Description = tbDescription.Text;
            workflowType.CategoryId = cpCategory.SelectedValueAsInt();
            workflowType.Order = 0;
            workflowType.WorkTerm = tbWorkTerm.Text;
            if ( !string.IsNullOrWhiteSpace( tbProcessingInterval.Text ) )
            {
                workflowType.ProcessingIntervalSeconds = int.Parse( tbProcessingInterval.Text );
            }

            workflowType.IsPersisted = cbIsPersisted.Checked;
            workflowType.LoggingLevel = ddlLoggingLevel.SelectedValueAsEnum<WorkflowLoggingLevel>();
            workflowType.IsActive = cbIsActive.Checked;

            if ( !Page.IsValid )
            {
                return;
            }

            if ( !workflowType.IsValid )
            {
                // Controls will render the error messages
                return;
            }

            RockTransactionScope.WrapTransaction( () =>
            {
                // Save the entity field changes to workflow type
                if ( workflowType.Id.Equals( 0 ) )
                {
                    service.Add( workflowType );
                }
                rockContext.SaveChanges();

                // Save the attributes
                SaveAttributes( new Workflow().TypeId, "WorkflowTypeId", workflowType.Id.ToString(), AttributesState, rockContext );

                WorkflowActivityTypeService workflowActivityTypeService = new WorkflowActivityTypeService( rockContext );
                WorkflowActionTypeService workflowActionTypeService = new WorkflowActionTypeService( rockContext );
                WorkflowActionFormService workflowFormService = new WorkflowActionFormService( rockContext );
                WorkflowActionFormAttributeService workflowFormAttributeService = new WorkflowActionFormAttributeService( rockContext );

                // delete WorkflowActionTypes that aren't assigned in the UI anymore
                List<WorkflowActivityEditor> workflowActivityTypeEditorList = phActivities.Controls.OfType<WorkflowActivityEditor>().ToList();
                List<WorkflowActionType> actionTypesInDB = workflowActionTypeService.Queryable().Where( a => a.ActivityType.WorkflowTypeId.Equals( workflowType.Id ) ).ToList();
                List<WorkflowActionType> actionTypesInUI = new List<WorkflowActionType>();
                foreach ( WorkflowActivityEditor workflowActivityTypeEditor in workflowActivityTypeEditorList )
                {
                    foreach ( WorkflowActionEditor editor in workflowActivityTypeEditor.Controls.OfType<WorkflowActionEditor>() )
                    {
                        actionTypesInUI.Add( editor.WorkflowActionType );
                    }
                }
                var deletedActionTypes = from actionType in actionTypesInDB
                                         where !actionTypesInUI.Select( u => u.Guid ).Contains( actionType.Guid )
                                         select actionType;
                deletedActionTypes.ToList().ForEach( actionType =>
                {
                    if (actionType.WorkflowForm != null)
                    {
                        workflowFormService.Delete( actionType.WorkflowForm );
                    }
                    workflowActionTypeService.Delete( actionType );
                } );
                rockContext.SaveChanges();

                // delete WorkflowActivityTypes that aren't assigned in the UI anymore
                List<WorkflowActivityType> activityTypesInDB = workflowActivityTypeService.Queryable().Where( a => a.WorkflowTypeId.Equals( workflowType.Id ) ).ToList();
                List<WorkflowActivityType> activityTypesInUI = workflowActivityTypeEditorList.Select( a => a.GetWorkflowActivityType() ).ToList();
                var deletedActivityTypes = from activityType in activityTypesInDB
                                           where !activityTypesInUI.Select( u => u.Guid ).Contains( activityType.Guid )
                                           select activityType;
                deletedActivityTypes.ToList().ForEach( activityType =>
                {
                    workflowActivityTypeService.Delete( activityType );
                } );
                rockContext.SaveChanges();

                // add or update WorkflowActivityTypes(and Actions) that are assigned in the UI
                int workflowActivityTypeOrder = 0;
                foreach ( WorkflowActivityEditor workflowActivityTypeEditor in workflowActivityTypeEditorList )
                {
                    // Add or Update the activity type
                    WorkflowActivityType editorWorkflowActivityType = workflowActivityTypeEditor.GetWorkflowActivityType();
                    WorkflowActivityType workflowActivityType = workflowType.ActivityTypes.FirstOrDefault( a => a.Guid.Equals( editorWorkflowActivityType.Guid ) );
                    if ( workflowActivityType == null )
                    {
                        workflowActivityType = new WorkflowActivityType();
                        workflowType.ActivityTypes.Add( workflowActivityType );

                    }
                    workflowActivityType.IsActive = editorWorkflowActivityType.IsActive;
                    workflowActivityType.Name = editorWorkflowActivityType.Name;
                    workflowActivityType.Description = editorWorkflowActivityType.Description;
                    workflowActivityType.IsActivatedWithWorkflow = editorWorkflowActivityType.IsActivatedWithWorkflow;
                    workflowActivityType.Order = workflowActivityTypeOrder++;

                    int workflowActionTypeOrder = 0;
                    foreach ( WorkflowActionEditor workflowActionTypeEditor in workflowActivityTypeEditor.Controls.OfType<WorkflowActionEditor>() )
                    {
                        WorkflowActionType editorWorkflowActionType = workflowActionTypeEditor.WorkflowActionType;
                        WorkflowActionType workflowActionType = workflowActivityType.ActionTypes.FirstOrDefault( a => a.Guid.Equals( editorWorkflowActionType.Guid ) );
                        if ( workflowActionType == null )
                        {
                            // New action
                            workflowActionType = new WorkflowActionType();
                            workflowActivityType.ActionTypes.Add( workflowActionType );
                        }
                        workflowActionType.Name = editorWorkflowActionType.Name;
                        workflowActionType.EntityTypeId = editorWorkflowActionType.EntityTypeId;
                        workflowActionType.IsActionCompletedOnSuccess = editorWorkflowActionType.IsActionCompletedOnSuccess;
                        workflowActionType.IsActivityCompletedOnSuccess = editorWorkflowActionType.IsActivityCompletedOnSuccess;
                        workflowActionType.Attributes = editorWorkflowActionType.Attributes;
                        workflowActionType.AttributeValues = editorWorkflowActionType.AttributeValues;
                        workflowActionType.Order = workflowActionTypeOrder++;

                        if ( workflowActionType.WorkflowForm != null && editorWorkflowActionType.WorkflowForm == null )
                        {
                            // Form removed
                            workflowFormService.Delete( workflowActionType.WorkflowForm );
                            workflowActionType.WorkflowForm = null;
                        }

                        if (editorWorkflowActionType.WorkflowForm != null)
                        {
                            if (workflowActionType.WorkflowForm == null)
                            {
                                workflowActionType.WorkflowForm = new WorkflowActionForm();
                            }

                            workflowActionType.WorkflowForm.Header = editorWorkflowActionType.WorkflowForm.Header;
                            workflowActionType.WorkflowForm.Footer = editorWorkflowActionType.WorkflowForm.Footer;
                            workflowActionType.WorkflowForm.InactiveMessage = editorWorkflowActionType.WorkflowForm.InactiveMessage;
                            workflowActionType.WorkflowForm.Actions = editorWorkflowActionType.WorkflowForm.Actions;

                            var editorGuids = editorWorkflowActionType.WorkflowForm.FormAttributes
                                .Select( a => a.Attribute.Guid)
                                .ToList();

                            foreach( var formAttribute in workflowActionType.WorkflowForm.FormAttributes
                                .Where( a => !editorGuids.Contains(a.Attribute.Guid) ) )
                            {
                                workflowFormAttributeService.Delete(formAttribute);
                            }

                            int attributeOrder = 0;
                            foreach ( var editorAttribute in editorWorkflowActionType.WorkflowForm.FormAttributes.OrderBy( a => a.Order ) )
                            {
                                int attributeId = AttributeCache.Read( editorAttribute.Attribute.Guid ).Id;

                                var formAttribute = workflowActionType.WorkflowForm.FormAttributes
                                    .Where( a => a.AttributeId == attributeId )
                                    .FirstOrDefault();

                                if (formAttribute == null)
                                {
                                    formAttribute = new WorkflowActionFormAttribute();
                                    formAttribute.Guid = editorAttribute.Guid;
                                    formAttribute.AttributeId = attributeId;
                                    workflowActionType.WorkflowForm.FormAttributes.Add( formAttribute );
                                }

                                formAttribute.Order = attributeOrder++;
                                formAttribute.IsVisible = editorAttribute.IsVisible;
                                formAttribute.IsReadOnly = editorAttribute.IsReadOnly;
                                formAttribute.IsRequired = editorAttribute.IsRequired;
                            }
                        }
                    }
                }

                rockContext.SaveChanges();

                foreach ( var activityType in workflowType.ActivityTypes )
                {
                    foreach ( var workflowActionType in activityType.ActionTypes )
                    {
                        workflowActionType.SaveAttributeValues( rockContext );
                    }
                }

            } );

            var qryParams = new Dictionary<string, string>();
            qryParams["workflowTypeId"] = workflowType.Id.ToString();
            NavigateToPage( RockPage.Guid, qryParams );
        }
        /// <summary>
        /// Handles the Click event of the btnSave control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
        protected void btnSave_Click( object sender, EventArgs e )
        {
            ParseControls( true );

            var rockContext = new RockContext();
            var service = new WorkflowTypeService( rockContext );

            WorkflowType workflowType = null;

            int? workflowTypeId = hfWorkflowTypeId.Value.AsIntegerOrNull();
            if ( workflowTypeId.HasValue )
            {
                workflowType = service.Get( workflowTypeId.Value );
            }

            if ( workflowType == null )
            {
                workflowType = new WorkflowType();
            }

            var validationErrors = new List<string>();

            // check for unique prefix
            string prefix = tbNumberPrefix.UntrimmedText;
            if ( !string.IsNullOrWhiteSpace( prefix ) &&
                prefix.ToUpper() != ( workflowType.WorkflowIdPrefix ?? string.Empty ).ToUpper() )
            {
                if ( service.Queryable().AsNoTracking()
                    .Any( w =>
                        w.Id != workflowType.Id &&
                        w.WorkflowIdPrefix == prefix ) )
                {
                    validationErrors.Add( "Workflow Number Prefix is already being used by another workflow type.  Please use a unique prefix." );
                }
                else
                {
                    workflowType.WorkflowIdPrefix = prefix;
                }
            }
            else
            {
                workflowType.WorkflowIdPrefix = prefix;
            }

            workflowType.IsActive = cbIsActive.Checked;
            workflowType.Name = tbName.Text;
            workflowType.Description = tbDescription.Text;
            workflowType.CategoryId = cpCategory.SelectedValueAsInt();
            workflowType.WorkTerm = tbWorkTerm.Text;
            workflowType.ModifiedByPersonAliasId = CurrentPersonAliasId;
            workflowType.ModifiedDateTime = RockDateTime.Now;

            int? mins = tbProcessingInterval.Text.AsIntegerOrNull();
            if ( mins.HasValue )
            {
                workflowType.ProcessingIntervalSeconds = mins.Value * 60;
            }
            else
            {
                workflowType.ProcessingIntervalSeconds = null;
            }

            workflowType.IsPersisted = cbIsPersisted.Checked;
            workflowType.LoggingLevel = ddlLoggingLevel.SelectedValueAsEnum<WorkflowLoggingLevel>();
            workflowType.IconCssClass = tbIconCssClass.Text;
            workflowType.SummaryViewText = ceSummaryViewText.Text;
            workflowType.NoActionMessage = ceNoActionMessage.Text;

            if ( validationErrors.Any() )
            {
                nbValidationError.Text = string.Format( "Please Correct the Following<ul><li>{0}</li></ul>",
                    validationErrors.AsDelimited( "</li><li>" ) );
                nbValidationError.Visible = true;

                return;
            }

            if ( !Page.IsValid || !workflowType.IsValid )
            {
                return;
            }

            foreach(var activityType in ActivityTypesState)
            {
                if (!activityType.IsValid)
                {
                    return;
                }
                foreach(var actionType in activityType.ActionTypes)
                {
                    if ( !actionType.IsValid )
                    {
                        return;
                    }
                }
            }

            rockContext.WrapTransaction( () =>
            {
                // Save the entity field changes to workflow type
                if ( workflowType.Id.Equals( 0 ) )
                {
                    service.Add( workflowType );
                }
                rockContext.SaveChanges();

                // Save the workflow type attributes
                SaveAttributes( new Workflow().TypeId, "WorkflowTypeId", workflowType.Id.ToString(), AttributesState, rockContext );

                WorkflowActivityTypeService workflowActivityTypeService = new WorkflowActivityTypeService( rockContext );
                WorkflowActivityService workflowActivityService = new WorkflowActivityService( rockContext );
                WorkflowActionService workflowActionService = new WorkflowActionService( rockContext );
                WorkflowActionTypeService workflowActionTypeService = new WorkflowActionTypeService( rockContext );
                WorkflowActionFormService workflowFormService = new WorkflowActionFormService( rockContext );
                WorkflowActionFormAttributeService workflowFormAttributeService = new WorkflowActionFormAttributeService( rockContext );

                // delete WorkflowActionTypes that aren't assigned in the UI anymore
                List<WorkflowActionType> actionTypesInDB = workflowActionTypeService.Queryable().Where( a => a.ActivityType.WorkflowTypeId.Equals( workflowType.Id ) ).ToList();
                List<WorkflowActionType> actionTypesInUI = new List<WorkflowActionType>();

                foreach ( var workflowActivity in ActivityTypesState )
                {
                    foreach ( var workflowAction in workflowActivity.ActionTypes )
                    {
                        actionTypesInUI.Add( workflowAction );
                    }
                }

                var deletedActionTypes = from actionType in actionTypesInDB
                                         where !actionTypesInUI.Select( u => u.Guid ).Contains( actionType.Guid )
                                         select actionType;
                foreach ( var actionType in deletedActionTypes.ToList() )
                {
                    if ( actionType.WorkflowForm != null )
                    {
                        workflowFormService.Delete( actionType.WorkflowForm );
                    }

                    // Delete any workflow actions of this type
                    int loopCounter = 0;
                    foreach ( var action in workflowActionService.Queryable().Where( a => a.ActionTypeId == actionType.Id ) )
                    {
                        workflowActionService.Delete( action );
                        loopCounter++;
                        if ( loopCounter > 100 )
                        {
                            rockContext.SaveChanges();
                            loopCounter = 0;
                        }
                    }
                    rockContext.SaveChanges();

                    workflowActionTypeService.Delete( actionType );
                }
                rockContext.SaveChanges();

                // delete WorkflowActivityTypes that aren't assigned in the UI anymore
                List<WorkflowActivityType> activityTypesInDB = workflowActivityTypeService.Queryable().Where( a => a.WorkflowTypeId.Equals( workflowType.Id ) ).ToList();
                var deletedActivityTypes = from activityType in activityTypesInDB
                                           where !ActivityTypesState.Select( u => u.Guid ).Contains( activityType.Guid )
                                           select activityType;
                foreach ( var activityType in deletedActivityTypes.ToList() )
                {
                    // Delete any workflow activities of this type
                    int loopCounter = 0;
                    foreach ( var activity in workflowActivityService.Queryable().Where( a => a.ActivityTypeId == activityType.Id ) )
                    {
                        workflowActivityService.Delete( activity );
                        loopCounter++;
                        if ( loopCounter > 100 )
                        {
                            rockContext.SaveChanges();
                            loopCounter = 0;
                        }
                    }
                    rockContext.SaveChanges();

                    workflowActivityTypeService.Delete( activityType );
                }
                rockContext.SaveChanges();

                // add or update WorkflowActivityTypes(and Actions) that are assigned in the UI
                int workflowActivityTypeOrder = 0;
                foreach ( var editorWorkflowActivityType in ActivityTypesState )
                {
                    // Add or Update the activity type
                    WorkflowActivityType workflowActivityType = workflowType.ActivityTypes.FirstOrDefault( a => a.Guid.Equals( editorWorkflowActivityType.Guid ) );
                    if ( workflowActivityType == null )
                    {
                        workflowActivityType = new WorkflowActivityType();
                        workflowActivityType.Guid = editorWorkflowActivityType.Guid;
                        workflowType.ActivityTypes.Add( workflowActivityType );
                    }
                    workflowActivityType.IsActive = editorWorkflowActivityType.IsActive;
                    workflowActivityType.Name = editorWorkflowActivityType.Name;
                    workflowActivityType.Description = editorWorkflowActivityType.Description;
                    workflowActivityType.IsActivatedWithWorkflow = editorWorkflowActivityType.IsActivatedWithWorkflow;
                    workflowActivityType.Order = workflowActivityTypeOrder++;

                    // Save Activity Type
                    rockContext.SaveChanges();

                    // Save ActivityType Attributes
                    if ( ActivityAttributesState.ContainsKey( workflowActivityType.Guid ) )
                    {
                        SaveAttributes( new WorkflowActivity().TypeId, "ActivityTypeId", workflowActivityType.Id.ToString(), ActivityAttributesState[workflowActivityType.Guid], rockContext );
                    }

                    // Because the SaveAttributes above may have flushed the cached entity attribute cache, and it would get loaded again with
                    // a different context, manually reload the cache now with our context to prevent a database lock conflict (when database is
                    // configured without snapshot isolation turned on)
                    AttributeCache.LoadEntityAttributes( rockContext );

                    int workflowActionTypeOrder = 0;
                    foreach ( var editorWorkflowActionType in editorWorkflowActivityType.ActionTypes )
                    {
                        WorkflowActionType workflowActionType = workflowActivityType.ActionTypes.FirstOrDefault( a => a.Guid.Equals( editorWorkflowActionType.Guid ) );
                        if ( workflowActionType == null )
                        {
                            // New action
                            workflowActionType = new WorkflowActionType();
                            workflowActionType.Guid = editorWorkflowActionType.Guid;
                            workflowActivityType.ActionTypes.Add( workflowActionType );
                        }
                        workflowActionType.CriteriaAttributeGuid = editorWorkflowActionType.CriteriaAttributeGuid;
                        workflowActionType.CriteriaComparisonType = editorWorkflowActionType.CriteriaComparisonType;
                        workflowActionType.CriteriaValue = editorWorkflowActionType.CriteriaValue;
                        workflowActionType.Name = editorWorkflowActionType.Name;
                        workflowActionType.EntityTypeId = editorWorkflowActionType.EntityTypeId;
                        workflowActionType.IsActionCompletedOnSuccess = editorWorkflowActionType.IsActionCompletedOnSuccess;
                        workflowActionType.IsActivityCompletedOnSuccess = editorWorkflowActionType.IsActivityCompletedOnSuccess;
                        workflowActionType.Attributes = editorWorkflowActionType.Attributes;
                        workflowActionType.AttributeValues = editorWorkflowActionType.AttributeValues;
                        workflowActionType.Order = workflowActionTypeOrder++;

                        if ( workflowActionType.WorkflowForm != null && editorWorkflowActionType.WorkflowForm == null )
                        {
                            // Form removed
                            workflowFormService.Delete( workflowActionType.WorkflowForm );
                            workflowActionType.WorkflowForm = null;
                        }

                        if ( editorWorkflowActionType.WorkflowForm != null )
                        {
                            if ( workflowActionType.WorkflowForm == null )
                            {
                                workflowActionType.WorkflowForm = new WorkflowActionForm();
                            }

                            workflowActionType.WorkflowForm.NotificationSystemEmailId = editorWorkflowActionType.WorkflowForm.NotificationSystemEmailId;
                            workflowActionType.WorkflowForm.IncludeActionsInNotification = editorWorkflowActionType.WorkflowForm.IncludeActionsInNotification;
                            workflowActionType.WorkflowForm.AllowNotes = editorWorkflowActionType.WorkflowForm.AllowNotes;
                            workflowActionType.WorkflowForm.Header = editorWorkflowActionType.WorkflowForm.Header;
                            workflowActionType.WorkflowForm.Footer = editorWorkflowActionType.WorkflowForm.Footer;
                            workflowActionType.WorkflowForm.Actions = editorWorkflowActionType.WorkflowForm.Actions;
                            workflowActionType.WorkflowForm.ActionAttributeGuid = editorWorkflowActionType.WorkflowForm.ActionAttributeGuid;

                            var editorGuids = editorWorkflowActionType.WorkflowForm.FormAttributes
                                .Select( a => a.Attribute.Guid )
                                .ToList();

                            foreach ( var formAttribute in workflowActionType.WorkflowForm.FormAttributes
                                .Where( a => !editorGuids.Contains( a.Attribute.Guid ) ).ToList() )
                            {
                                workflowFormAttributeService.Delete( formAttribute );
                            }

                            int attributeOrder = 0;
                            foreach ( var editorAttribute in editorWorkflowActionType.WorkflowForm.FormAttributes.OrderBy( a => a.Order ) )
                            {
                                int attributeId = AttributeCache.Read( editorAttribute.Attribute.Guid, rockContext ).Id;

                                var formAttribute = workflowActionType.WorkflowForm.FormAttributes
                                    .Where( a => a.AttributeId == attributeId )
                                    .FirstOrDefault();

                                if ( formAttribute == null )
                                {
                                    formAttribute = new WorkflowActionFormAttribute();
                                    formAttribute.Guid = editorAttribute.Guid;
                                    formAttribute.AttributeId = attributeId;
                                    workflowActionType.WorkflowForm.FormAttributes.Add( formAttribute );
                                }

                                formAttribute.Order = attributeOrder++;
                                formAttribute.IsVisible = editorAttribute.IsVisible;
                                formAttribute.IsReadOnly = editorAttribute.IsReadOnly;
                                formAttribute.IsRequired = editorAttribute.IsRequired;
                                formAttribute.HideLabel = editorAttribute.HideLabel;
                                formAttribute.PreHtml = editorAttribute.PreHtml;
                                formAttribute.PostHtml = editorAttribute.PostHtml;
                            }
                        }
                    }
                }

                rockContext.SaveChanges();

                foreach ( var activityType in workflowType.ActivityTypes )
                {
                    foreach ( var workflowActionType in activityType.ActionTypes )
                    {
                        workflowActionType.SaveAttributeValues( rockContext );
                    }
                }

            } );

            var qryParams = new Dictionary<string, string>();
            qryParams["workflowTypeId"] = workflowType.Id.ToString();
            NavigateToPage( RockPage.Guid, qryParams );
        }
        /// <summary>
        /// Builds the action control.
        /// </summary>
        /// <param name="actionType">Type of the action.</param>
        /// <param name="activeWorkflowActionTypeGuid">The active workflow action type unique identifier.</param>
        /// <returns></returns>
        private WorkflowActionTypeEditor BuildActionControl( Control parentControl, bool setValues, WorkflowActionType actionType, 
            Dictionary<Guid, Attribute> attributes, Dictionary<string, string> activities, Guid? activeWorkflowActionTypeGuid = null, 
                bool showInvalid = false )
        {
            var control = new WorkflowActionTypeEditor();
            parentControl.Controls.Add( control );
            control.ID = actionType.Guid.ToString( "N" );
            control.ValidationGroup = btnSave.ValidationGroup;

            control.DeleteActionTypeClick += workflowActionTypeEditor_DeleteActionTypeClick;
            control.ChangeActionTypeClick += workflowActionTypeEditor_ChangeActionTypeClick;

            control.WorkflowActivities = activities;

            if (actionType.WorkflowForm != null)
            {
                var formAttributes = actionType.WorkflowForm.FormAttributes;

                // Remove any fields that were removed
                foreach ( var formAttribute in formAttributes.ToList() )
                {
                    if (!attributes.ContainsKey(formAttribute.Attribute.Guid))
                    {
                        formAttributes.Remove( formAttribute );
                    }
                }

                // Add any new attributes
                foreach(var attribute in attributes)
                {
                    if ( !formAttributes.Select( a => a.Attribute.Guid ).Contains( attribute.Key ) )
                    {
                        var formAttribute = new WorkflowActionFormAttribute();
                        formAttribute.Attribute = new Rock.Model.Attribute { Guid = attribute.Key, Name = attribute.Value.Name };
                        formAttribute.Guid = Guid.NewGuid();
                        formAttribute.Order = formAttributes.Any() ? formAttributes.Max( a => a.Order ) + 1 : 0;
                        formAttribute.IsVisible = false;
                        formAttribute.IsReadOnly = true;
                        formAttribute.IsRequired = false;
                        formAttribute.HideLabel = false;
                        formAttribute.PreHtml = string.Empty;
                        formAttribute.PostHtml = string.Empty;
                        formAttributes.Add( formAttribute );
                    }
                }
            }
            control.SetWorkflowActionType( actionType, attributes );

            control.Expanded = ExpandedActions.Contains( actionType.Guid );

            if ( setValues )
            {

                // Set order
                int newOrder = 0;
                foreach ( var attributeRow in control.FormEditor.AttributeRows )
                {
                    attributeRow.Order = newOrder++;
                }

                if ( !control.Expanded && showInvalid && !actionType.IsValid )
                {
                    control.Expanded = true;
                }

                if ( !control.Expanded )
                {
                    control.Expanded = activeWorkflowActionTypeGuid.HasValue && activeWorkflowActionTypeGuid.Equals( actionType.Guid );
                }
            }

            return control;
        }
        /// <summary>
        /// Handles the Click event of the btnSave control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
        protected void btnSave_Click( object sender, EventArgs e )
        {
            ParseControls( true );

            var rockContext = new RockContext();
            var service = new WorkflowTypeService( rockContext );

            WorkflowType workflowType = null;

            int? workflowTypeId = hfWorkflowTypeId.Value.AsIntegerOrNull();
            if ( workflowTypeId.HasValue )
            {
                workflowType = service.Get( workflowTypeId.Value );
            }

            if ( workflowType == null )
            {
                workflowType = new WorkflowType();
            }

            workflowType.IsActive = cbIsActive.Checked;
            workflowType.Name = tbName.Text;
            workflowType.Description = tbDescription.Text;
            workflowType.CategoryId = cpCategory.SelectedValueAsInt();
            workflowType.Order = 0;
            workflowType.WorkTerm = tbWorkTerm.Text;
            workflowType.ProcessingIntervalSeconds = tbProcessingInterval.Text.AsIntegerOrNull();
            workflowType.IsPersisted = cbIsPersisted.Checked;
            workflowType.LoggingLevel = ddlLoggingLevel.SelectedValueAsEnum<WorkflowLoggingLevel>();
            workflowType.IconCssClass = tbIconCssClass.Text;

            if ( !Page.IsValid || !workflowType.IsValid )
            {
                return;
            }

            foreach(var activityType in ActivityTypesState)
            {
                if (!activityType.IsValid)
                {
                    return;
                }
                foreach(var actionType in activityType.ActionTypes)
                {
                    if ( !actionType.IsValid )
                    {
                        return;
                    }
                }
            }

            rockContext.WrapTransaction( () =>
            {
                // Save the entity field changes to workflow type
                if ( workflowType.Id.Equals( 0 ) )
                {
                    service.Add( workflowType );
                }
                rockContext.SaveChanges();

                // Save the workflow type attributes
                SaveAttributes( new Workflow().TypeId, "WorkflowTypeId", workflowType.Id.ToString(), AttributesState, rockContext );

                WorkflowActivityTypeService workflowActivityTypeService = new WorkflowActivityTypeService( rockContext );
                WorkflowActionTypeService workflowActionTypeService = new WorkflowActionTypeService( rockContext );
                WorkflowActionFormService workflowFormService = new WorkflowActionFormService( rockContext );
                WorkflowActionFormAttributeService workflowFormAttributeService = new WorkflowActionFormAttributeService( rockContext );

                // delete WorkflowActionTypes that aren't assigned in the UI anymore
                List<WorkflowActionType> actionTypesInDB = workflowActionTypeService.Queryable().Where( a => a.ActivityType.WorkflowTypeId.Equals( workflowType.Id ) ).ToList();
                List<WorkflowActionType> actionTypesInUI = new List<WorkflowActionType>();

                foreach ( var workflowActivity in ActivityTypesState )
                {
                    foreach ( var workflowAction in workflowActivity.ActionTypes )
                    {
                        actionTypesInUI.Add( workflowAction );
                    }
                }

                var deletedActionTypes = from actionType in actionTypesInDB
                                         where !actionTypesInUI.Select( u => u.Guid ).Contains( actionType.Guid )
                                         select actionType;
                deletedActionTypes.ToList().ForEach( actionType =>
                {
                    if ( actionType.WorkflowForm != null )
                    {
                        workflowFormService.Delete( actionType.WorkflowForm );
                    }
                    workflowActionTypeService.Delete( actionType );
                } );
                rockContext.SaveChanges();

                // delete WorkflowActivityTypes that aren't assigned in the UI anymore
                List<WorkflowActivityType> activityTypesInDB = workflowActivityTypeService.Queryable().Where( a => a.WorkflowTypeId.Equals( workflowType.Id ) ).ToList();
                var deletedActivityTypes = from activityType in activityTypesInDB
                                           where !ActivityTypesState.Select( u => u.Guid ).Contains( activityType.Guid )
                                           select activityType;
                deletedActivityTypes.ToList().ForEach( activityType =>
                {
                    workflowActivityTypeService.Delete( activityType );
                } );
                rockContext.SaveChanges();

                // add or update WorkflowActivityTypes(and Actions) that are assigned in the UI
                int workflowActivityTypeOrder = 0;
                foreach ( var editorWorkflowActivityType in ActivityTypesState )
                {
                    // Add or Update the activity type
                    WorkflowActivityType workflowActivityType = workflowType.ActivityTypes.FirstOrDefault( a => a.Guid.Equals( editorWorkflowActivityType.Guid ) );
                    if ( workflowActivityType == null )
                    {
                        workflowActivityType = new WorkflowActivityType();
                        workflowActivityType.Guid = editorWorkflowActivityType.Guid;
                        workflowType.ActivityTypes.Add( workflowActivityType );
                    }
                    workflowActivityType.IsActive = editorWorkflowActivityType.IsActive;
                    workflowActivityType.Name = editorWorkflowActivityType.Name;
                    workflowActivityType.Description = editorWorkflowActivityType.Description;
                    workflowActivityType.IsActivatedWithWorkflow = editorWorkflowActivityType.IsActivatedWithWorkflow;
                    workflowActivityType.Order = workflowActivityTypeOrder++;

                    // Save Activity Type
                    rockContext.SaveChanges();

                    // Save ActivityType Attributes
                    if ( ActivityAttributesState.ContainsKey( workflowActivityType.Guid ) )
                    {
                        SaveAttributes( new WorkflowActivity().TypeId, "ActivityTypeId", workflowActivityType.Id.ToString(), ActivityAttributesState[workflowActivityType.Guid], rockContext );
                    }

                    int workflowActionTypeOrder = 0;
                    foreach ( var editorWorkflowActionType in editorWorkflowActivityType.ActionTypes )
                    {
                        WorkflowActionType workflowActionType = workflowActivityType.ActionTypes.FirstOrDefault( a => a.Guid.Equals( editorWorkflowActionType.Guid ) );
                        if ( workflowActionType == null )
                        {
                            // New action
                            workflowActionType = new WorkflowActionType();
                            workflowActionType.Guid = editorWorkflowActionType.Guid;
                            workflowActivityType.ActionTypes.Add( workflowActionType );
                        }
                        workflowActionType.CriteriaAttributeGuid = editorWorkflowActionType.CriteriaAttributeGuid;
                        workflowActionType.CriteriaComparisonType = editorWorkflowActionType.CriteriaComparisonType;
                        workflowActionType.CriteriaValue = editorWorkflowActionType.CriteriaValue;
                        workflowActionType.Name = editorWorkflowActionType.Name;
                        workflowActionType.EntityTypeId = editorWorkflowActionType.EntityTypeId;
                        workflowActionType.IsActionCompletedOnSuccess = editorWorkflowActionType.IsActionCompletedOnSuccess;
                        workflowActionType.IsActivityCompletedOnSuccess = editorWorkflowActionType.IsActivityCompletedOnSuccess;
                        workflowActionType.Attributes = editorWorkflowActionType.Attributes;
                        workflowActionType.AttributeValues = editorWorkflowActionType.AttributeValues;
                        workflowActionType.Order = workflowActionTypeOrder++;

                        if ( workflowActionType.WorkflowForm != null && editorWorkflowActionType.WorkflowForm == null )
                        {
                            // Form removed
                            workflowFormService.Delete( workflowActionType.WorkflowForm );
                            workflowActionType.WorkflowForm = null;
                        }

                        if ( editorWorkflowActionType.WorkflowForm != null )
                        {
                            if ( workflowActionType.WorkflowForm == null )
                            {
                                workflowActionType.WorkflowForm = new WorkflowActionForm();
                            }

                            workflowActionType.WorkflowForm.NotificationSystemEmailId = editorWorkflowActionType.WorkflowForm.NotificationSystemEmailId;
                            workflowActionType.WorkflowForm.IncludeActionsInNotification = editorWorkflowActionType.WorkflowForm.IncludeActionsInNotification;
                            workflowActionType.WorkflowForm.AllowNotes = editorWorkflowActionType.WorkflowForm.AllowNotes;
                            workflowActionType.WorkflowForm.Header = editorWorkflowActionType.WorkflowForm.Header;
                            workflowActionType.WorkflowForm.Footer = editorWorkflowActionType.WorkflowForm.Footer;
                            workflowActionType.WorkflowForm.Actions = editorWorkflowActionType.WorkflowForm.Actions;
                            workflowActionType.WorkflowForm.ActionAttributeGuid = editorWorkflowActionType.WorkflowForm.ActionAttributeGuid;

                            var editorGuids = editorWorkflowActionType.WorkflowForm.FormAttributes
                                .Select( a => a.Attribute.Guid )
                                .ToList();

                            foreach ( var formAttribute in workflowActionType.WorkflowForm.FormAttributes
                                .Where( a => !editorGuids.Contains( a.Attribute.Guid ) ).ToList() )
                            {
                                workflowFormAttributeService.Delete( formAttribute );
                            }

                            int attributeOrder = 0;
                            foreach ( var editorAttribute in editorWorkflowActionType.WorkflowForm.FormAttributes.OrderBy( a => a.Order ) )
                            {
                                int attributeId = AttributeCache.Read( editorAttribute.Attribute.Guid, rockContext ).Id;

                                var formAttribute = workflowActionType.WorkflowForm.FormAttributes
                                    .Where( a => a.AttributeId == attributeId )
                                    .FirstOrDefault();

                                if ( formAttribute == null )
                                {
                                    formAttribute = new WorkflowActionFormAttribute();
                                    formAttribute.Guid = editorAttribute.Guid;
                                    formAttribute.AttributeId = attributeId;
                                    workflowActionType.WorkflowForm.FormAttributes.Add( formAttribute );
                                }

                                formAttribute.Order = attributeOrder++;
                                formAttribute.IsVisible = editorAttribute.IsVisible;
                                formAttribute.IsReadOnly = editorAttribute.IsReadOnly;
                                formAttribute.IsRequired = editorAttribute.IsRequired;
                                formAttribute.HideLabel = editorAttribute.HideLabel;
                                formAttribute.PreHtml = editorAttribute.PreHtml;
                                formAttribute.PostHtml = editorAttribute.PostHtml;
                            }
                        }
                    }
                }

                rockContext.SaveChanges();

                foreach ( var activityType in workflowType.ActivityTypes )
                {
                    foreach ( var workflowActionType in activityType.ActionTypes )
                    {
                        workflowActionType.SaveAttributeValues( rockContext );
                    }
                }

            } );

            var qryParams = new Dictionary<string, string>();
            qryParams["workflowTypeId"] = workflowType.Id.ToString();
            NavigateToPage( RockPage.Guid, qryParams );
        }
        /// <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);
        }